source: branches/rsr.v5.1.dev/web/punbb/db_update.php @ 4

Last change on this file since 4 was 3, checked in by dj3c1t, 13 years ago

passage a Fluxbb 1.4.7

File size: 78.6 KB
RevLine 
[3]1<?php
2
3/**
4 * Copyright (C) 2008-2011 FluxBB
5 * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
6 * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
7 */
8
9// The FluxBB version this script updates to
10define('UPDATE_TO', '1.4.7');
11
12define('UPDATE_TO_DB_REVISION', 15);
13define('UPDATE_TO_SI_REVISION', 2);
14define('UPDATE_TO_PARSER_REVISION', 2);
15
16define('MIN_PHP_VERSION', '4.4.0');
17define('MIN_MYSQL_VERSION', '4.1.2');
18define('MIN_PGSQL_VERSION', '7.0.0');
19define('PUN_SEARCH_MIN_WORD', 3);
20define('PUN_SEARCH_MAX_WORD', 20);
21
22// The MySQL connection character set that was used for FluxBB 1.2 - in 99% of cases this should be detected automatically,
23// but can be overridden using the below constant if required.
24//define('FORUM_DEFAULT_CHARSET', 'latin1');
25
26
27// The number of items to process per page view (lower this if the update script times out during UTF-8 conversion)
28define('PER_PAGE', 300);
29
30// Don't set to UTF-8 until after we've found out what the default character set is
31define('FORUM_NO_SET_NAMES', 1);
32
33// Make sure we are running at least MIN_PHP_VERSION
34if (!function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<'))
35        exit('You are running PHP version '.PHP_VERSION.'. FluxBB '.UPDATE_TO.' requires at least PHP '.MIN_PHP_VERSION.' to run properly. You must upgrade your PHP installation before you can continue.');
36
37define('PUN_ROOT', dirname(__FILE__).'/');
38
39// Attempt to load the configuration file config.php
40if (file_exists(PUN_ROOT.'config.php'))
41        include PUN_ROOT.'config.php';
42
43// If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
44if (defined('FORUM'))
45        define('PUN', FORUM);
46
47// If PUN isn't defined, config.php is missing or corrupt
48if (!defined('PUN'))
49{
50        header('Location: install.php');
51        exit;
52}
53
54// Enable debug mode
55if (!defined('PUN_DEBUG'))
56        define('PUN_DEBUG', 1);
57
58// Load the functions script
59require PUN_ROOT.'include/functions.php';
60
61// Load UTF-8 functions
62require PUN_ROOT.'include/utf8/utf8.php';
63
64// Strip out "bad" UTF-8 characters
65forum_remove_bad_characters();
66
67// Reverse the effect of register_globals
68forum_unregister_globals();
69
70// Turn on full PHP error reporting
71error_reporting(E_ALL);
72
73// Force POSIX locale (to prevent functions such as strtolower() from messing up UTF-8 strings)
74setlocale(LC_CTYPE, 'C');
75
76// Turn off magic_quotes_runtime
77if (get_magic_quotes_runtime())
78        set_magic_quotes_runtime(0);
79
80// Strip slashes from GET/POST/COOKIE (if magic_quotes_gpc is enabled)
81if (get_magic_quotes_gpc())
82{
83        function stripslashes_array($array)
84        {
85                return is_array($array) ? array_map('stripslashes_array', $array) : stripslashes($array);
86        }
87
88        $_GET = stripslashes_array($_GET);
89        $_POST = stripslashes_array($_POST);
90        $_COOKIE = stripslashes_array($_COOKIE);
91        $_REQUEST = stripslashes_array($_REQUEST);
92}
93
94// If a cookie name is not specified in config.php, we use the default (forum_cookie)
95if (empty($cookie_name))
96        $cookie_name = 'pun_cookie';
97
98// If the cache directory is not specified, we use the default setting
99if (!defined('FORUM_CACHE_DIR'))
100        define('FORUM_CACHE_DIR', PUN_ROOT.'cache/');
101
102// Turn off PHP time limit
103@set_time_limit(0);
104
105// Define a few commonly used constants
106define('PUN_UNVERIFIED', 0);
107define('PUN_ADMIN', 1);
108define('PUN_MOD', 2);
109define('PUN_GUEST', 3);
110define('PUN_MEMBER', 4);
111
112// Load DB abstraction layer and try to connect
113require PUN_ROOT.'include/dblayer/common_db.php';
114
115// Check what the default character set is - since 1.2 didn't specify any we will use whatever the default was (usually latin1)
116$old_connection_charset = defined('FORUM_DEFAULT_CHARSET') ? FORUM_DEFAULT_CHARSET : $db->get_names();
117
118// Set the connection to UTF-8 now
119$db->set_names('utf8');
120
121// Get the forum config
122$result = $db->query('SELECT * FROM '.$db->prefix.'config') or error('Unable to fetch config.', __FILE__, __LINE__, $db->error());
123while ($cur_config_item = $db->fetch_row($result))
124        $pun_config[$cur_config_item[0]] = $cur_config_item[1];
125
126// Load language file
127$default_lang = $pun_config['o_default_lang'];
128
129if (!file_exists(PUN_ROOT.'lang/'.$default_lang.'/update.php'))
130        $default_lang = 'English';
131
132require PUN_ROOT.'lang/'.$default_lang.'/common.php';
133require PUN_ROOT.'lang/'.$default_lang.'/update.php';
134
135// Check current version
136$cur_version = $pun_config['o_cur_version'];
137
138if (version_compare($cur_version, '1.2', '<'))
139        error(sprintf($lang_update['Version mismatch error'], $db_name));
140
141// Do some DB type specific checks
142$mysql = false;
143switch ($db_type)
144{
145        case 'mysql':
146        case 'mysqli':
147        case 'mysql_innodb':
148        case 'mysqli_innodb':
149                $mysql_info = $db->get_version();
150                if (version_compare($mysql_info['version'], MIN_MYSQL_VERSION, '<'))
151                        error(sprintf($lang_update['You are running error'], 'MySQL', $mysql_info['version'], UPDATE_TO, MIN_MYSQL_VERSION));
152
153                $mysql = true;
154                break;
155
156        case 'pgsql':
157                $pgsql_info = $db->get_version();
158                if (version_compare($pgsql_info['version'], MIN_PGSQL_VERSION, '<'))
159                        error(sprintf($lang_update['You are running error'], 'PostgreSQL', $pgsql_info['version'], UPDATE_TO, MIN_PGSQL_VERSION));
160
161                break;
162}
163
164// Check the database, search index and parser revision and the current version
165if (isset($pun_config['o_database_revision']) && $pun_config['o_database_revision'] >= UPDATE_TO_DB_REVISION &&
166                isset($pun_config['o_searchindex_revision']) && $pun_config['o_searchindex_revision'] >= UPDATE_TO_SI_REVISION &&
167                isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION &&
168                version_compare($pun_config['o_cur_version'], UPDATE_TO, '>='))
169        error($lang_update['No update error']);
170
171$default_style = $pun_config['o_default_style'];
172if (!file_exists(PUN_ROOT.'style/'.$default_style.'.css'))
173        $default_style = 'Air';
174
175// Start a session, used to queue up errors if duplicate users occur when converting from FluxBB v1.2.
176session_start();
177
178//
179// Determines whether $str is UTF-8 encoded or not
180//
181function seems_utf8($str)
182{
183        $str_len = strlen($str);
184        for ($i = 0; $i < $str_len; ++$i)
185        {
186                if (ord($str[$i]) < 0x80) continue; # 0bbbbbbb
187                else if ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
188                else if ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
189                else if ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
190                else if ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
191                else if ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
192                else return false; # Does not match any model
193
194                for ($j = 0; $j < $n; ++$j) # n bytes matching 10bbbbbb follow ?
195                {
196                        if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
197                                return false;
198                }
199        }
200
201        return true;
202}
203
204
205//
206// Translates the number from a HTML numeric entity into an UTF-8 character
207//
208function dcr2utf8($src)
209{
210        $dest = '';
211        if ($src < 0)
212                return false;
213        else if ($src <= 0x007f)
214                $dest .= chr($src);
215        else if ($src <= 0x07ff)
216        {
217                $dest .= chr(0xc0 | ($src >> 6));
218                $dest .= chr(0x80 | ($src & 0x003f));
219        }
220        else if ($src == 0xFEFF)
221        {
222                // nop -- zap the BOM
223        }
224        else if ($src >= 0xD800 && $src <= 0xDFFF)
225        {
226                // found a surrogate
227                return false;
228        }
229        else if ($src <= 0xffff)
230        {
231                $dest .= chr(0xe0 | ($src >> 12));
232                $dest .= chr(0x80 | (($src >> 6) & 0x003f));
233                $dest .= chr(0x80 | ($src & 0x003f));
234        }
235        else if ($src <= 0x10ffff)
236        {
237                $dest .= chr(0xf0 | ($src >> 18));
238                $dest .= chr(0x80 | (($src >> 12) & 0x3f));
239                $dest .= chr(0x80 | (($src >> 6) & 0x3f));
240                $dest .= chr(0x80 | ($src & 0x3f));
241        }
242        else
243        {
244                // out of range
245                return false;
246        }
247
248        return $dest;
249}
250
251
252//
253// Attempts to convert $str from $old_charset to UTF-8. Also converts HTML entities (including numeric entities) to UTF-8 characters
254//
255function convert_to_utf8(&$str, $old_charset)
256{
257        if ($str === null || $str == '')
258                return false;
259
260        $save = $str;
261
262        // Replace literal entities (for non-UTF-8 compliant html_entity_encode)
263        if (version_compare(PHP_VERSION, '5.0.0', '<') && $old_charset == 'ISO-8859-1' || $old_charset == 'ISO-8859-15')
264                $str = html_entity_decode($str, ENT_QUOTES, $old_charset);
265
266        if ($old_charset != 'UTF-8' && !seems_utf8($str))
267        {
268                if (function_exists('iconv'))
269                        $str = iconv($old_charset == 'ISO-8859-1' ? 'WINDOWS-1252' : 'ISO-8859-1', 'UTF-8', $str);
270                else if (function_exists('mb_convert_encoding'))
271                        $str = mb_convert_encoding($str, 'UTF-8', $old_charset == 'ISO-8859-1' ? 'WINDOWS-1252' : 'ISO-8859-1');
272                else if ($old_charset == 'ISO-8859-1')
273                        $str = utf8_encode($str);
274        }
275
276        // Replace literal entities (for UTF-8 compliant html_entity_encode)
277        if (version_compare(PHP_VERSION, '5.0.0', '>='))
278                $str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
279
280        // Replace numeric entities
281        $str = preg_replace_callback('%&#([0-9]+);%', 'utf8_callback_1', $str);
282        $str = preg_replace_callback('%&#x([a-f0-9]+);%i', 'utf8_callback_2', $str);
283
284        // Remove "bad" characters
285        $str = remove_bad_characters($str);
286
287        return ($save != $str);
288}
289
290
291function utf8_callback_1($matches)
292{
293        return dcr2utf8($matches[1]);
294}
295
296
297function utf8_callback_2($matches)
298{
299        return dcr2utf8(hexdec($matches[1]));
300}
301
302
303//
304// Alter a table to be utf8. MySQL only
305// Function based on update_convert_table_utf8() from the Drupal project (http://drupal.org/)
306//
307function alter_table_utf8($table)
308{
309        global $mysql, $db;
310        static $types;
311
312        if (!$mysql)
313                return;
314
315        if (!isset($types))
316        {
317                $types = array(
318                        'char'                  => 'binary',
319                        'varchar'               => 'varbinary',
320                        'tinytext'              => 'tinyblob',
321                        'mediumtext'    => 'mediumblob',
322                        'text'                  => 'blob',
323                        'longtext'              => 'longblob'
324                );
325        }
326
327        // Set table default charset to utf8
328        $db->query('ALTER TABLE '.$table.' CHARACTER SET utf8') or error('Unable to set table character set', __FILE__, __LINE__, $db->error());
329
330        // Find out which columns need converting and build SQL statements
331        $result = $db->query('SHOW FULL COLUMNS FROM '.$table) or error('Unable to fetch column information', __FILE__, __LINE__, $db->error());
332        while ($cur_column = $db->fetch_assoc($result))
333        {
334                if ($cur_column['Collation'] === null)
335                        continue;
336
337                list($type) = explode('(', $cur_column['Type']);
338                if (isset($types[$type]) && strpos($cur_column['Collation'], 'utf8') === false)
339                {
340                        $allow_null = ($cur_column['Null'] == 'YES');
341                        $collate = (substr($cur_column['Collation'], -3) == 'bin') ? 'utf8_bin' : 'utf8_general_ci';
342
343                        $db->alter_field($table, $cur_column['Field'], preg_replace('%'.$type.'%i', $types[$type], $cur_column['Type']), $allow_null, $cur_column['Default'], null, true) or error('Unable to alter field to binary', __FILE__, __LINE__, $db->error());
344                        $db->alter_field($table, $cur_column['Field'], $cur_column['Type'].' CHARACTER SET utf8 COLLATE '.$collate, $allow_null, $cur_column['Default'], null, true) or error('Unable to alter field to utf8', __FILE__, __LINE__, $db->error());
345                }
346        }
347}
348
349//
350// Safely converts text type columns into utf8
351// If finished returns true, otherwise returns $end_at
352//
353function convert_table_utf8($table, $callback, $old_charset, $key = null, $start_at = null, $error_callback = null)
354{
355        global $mysql, $db, $old_connection_charset;
356
357        $finished = true;
358        $end_at = 0;
359        if ($mysql)
360        {
361                // Only set up the tables if we are doing this in 1 go, or its the first go
362                if ($start_at === null || $start_at == 0)
363                {
364                        // Drop any temp table that exists, in-case it's left over from a failed update
365                        $db->drop_table($table.'_utf8', true) or error('Unable to drop left over temp table', __FILE__, __LINE__, $db->error());
366
367                        // Copy the table
368                        $db->query('CREATE TABLE '.$table.'_utf8 LIKE '.$table) or error('Unable to create new table', __FILE__, __LINE__, $db->error());
369
370                        // Set table default charset to utf8
371                        alter_table_utf8($table.'_utf8');
372                }
373
374                // Change to the old character set so MySQL doesn't attempt to perform conversion on the data from the old table
375                $db->set_names($old_connection_charset);
376
377                // Move & Convert everything
378                $result = $db->query('SELECT * FROM '.$table.($start_at === null ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.($start_at === null ? '' : ' LIMIT '.PER_PAGE), false) or error('Unable to select from old table', __FILE__, __LINE__, $db->error());
379
380                // Change back to utf8 mode so we can insert it into the new table
381                $db->set_names('utf8');
382
383                while ($cur_item = $db->fetch_assoc($result))
384                {
385                        $cur_item = call_user_func($callback, $cur_item, $old_charset);
386
387                        $temp = array();
388                        foreach ($cur_item as $idx => $value)
389                                $temp[$idx] = $value === null ? 'NULL' : '\''.$db->escape($value).'\'';
390
391                        $db->query('INSERT INTO '.$table.'_utf8('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or ($error_callback === null ? error('Unable to insert data to new table', __FILE__, __LINE__, $db->error()) : call_user_func($error_callback, $cur_item));
392
393                        $end_at = $cur_item[$key];
394                }
395
396                // If we aren't doing this all in 1 go and $end_at has a value (i.e. we have processed at least 1 row), figure out if we have more to do or not
397                if ($start_at !== null && $end_at > 0)
398                {
399                        $result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1') or error('Unable to check for next row', __FILE__, __LINE__, $db->error());
400                        $finished = $db->num_rows($result) == 0;
401                }
402
403                // Only swap the tables if we are doing this in 1 go, or its the last go
404                if ($finished)
405                {
406                        // Delete old table
407                        $db->drop_table($table, true) or error('Unable to drop old table', __FILE__, __LINE__, $db->error());
408
409                        // Rename table
410                        $db->query('ALTER TABLE '.$table.'_utf8 RENAME '.$table) or error('Unable to rename new table', __FILE__, __LINE__, $db->error());
411
412                        return true;
413                }
414
415                return $end_at;
416        }
417        else
418        {
419                // Convert everything
420                $result = $db->query('SELECT * FROM '.$table.($start_at === null ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.($start_at === null ? '' : ' LIMIT '.PER_PAGE)) or error('Unable to select from table', __FILE__, __LINE__, $db->error());
421                while ($cur_item = $db->fetch_assoc($result))
422                {
423                        $cur_item = call_user_func($callback, $cur_item, $old_charset);
424
425                        $temp = array();
426                        foreach ($cur_item as $idx => $value)
427                                $temp[] = $idx.'='.($value === null ? 'NULL' : '\''.$db->escape($value).'\'');
428
429                        if (!empty($temp))
430                                $db->query('UPDATE '.$table.' SET '.implode(', ', $temp).' WHERE '.$key.'=\''.$db->escape($cur_item[$key]).'\'') or error('Unable to update data', __FILE__, __LINE__, $db->error());
431
432                        $end_at = $cur_item[$key];
433                }
434
435                if ($start_at !== null && $end_at > 0)
436                {
437                        $result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1') or error('Unable to check for next row', __FILE__, __LINE__, $db->error());
438                        if ($db->num_rows($result) == 0)
439                                return true;
440
441                        return $end_at;
442                }
443
444                return true;
445        }
446}
447
448
449header('Content-type: text/html; charset=utf-8');
450
451// Empty all output buffers and stop buffering
452while (@ob_end_clean());
453
454
455$stage = isset($_REQUEST['stage']) ? $_REQUEST['stage'] : '';
456$old_charset = isset($_REQUEST['req_old_charset']) ? str_replace('ISO8859', 'ISO-8859', strtoupper($_REQUEST['req_old_charset'])) : 'ISO-8859-1';
457$start_at = isset($_REQUEST['start_at']) ? intval($_REQUEST['start_at']) : 0;
458$query_str = '';
459
460// Show form
461if (empty($stage))
462{
463        if (file_exists(FORUM_CACHE_DIR.'db_update.lock'))
464        {
465                // Deal with newlines, tabs and multiple spaces
466                $pattern = array("\t", '  ', '  ');
467                $replace = array('&#160; &#160; ', '&#160; ', ' &#160;');
468                $message = str_replace($pattern, $replace, $pun_config['o_maintenance_message']);
469
470?>
471<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
472<head>
473<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
474<title><?php echo $lang_update['Maintenance'] ?></title>
475<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
476</head>
477<body>
478
479<div id="punmaint" class="pun">
480<div class="top-box"><div><!-- Top Corners --></div></div>
481<div class="punwrap">
482
483<div id="brdmain">
484<div class="block">
485        <h2><?php echo $lang_update['Maintenance'] ?></h2>
486        <div class="box">
487                <div class="inbox">
488                        <p><?php echo $message ?></p>
489                </div>
490        </div>
491</div>
492</div>
493
494</div>
495<div class="end-box"><div><!-- Bottom Corners --></div></div>
496</div>
497
498</body>
499</html>
500<?php
501
502        }
503        else
504        {
505
506?>
507<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
508
509<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
510<head>
511<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
512<title><?php echo $lang_update['Update'] ?></title>
513<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
514</head>
515<body onload="document.getElementById('install').req_db_pass.focus();document.getElementById('install').start.disabled=false;">
516
517<div id="pundb_update" class="pun">
518<div class="top-box"><div><!-- Top Corners --></div></div>
519<div class="punwrap">
520
521<div id="brdheader" class="block">
522        <div class="box">
523                <div id="brdtitle" class="inbox">
524                        <h1><span><?php echo $lang_update['Update'] ?></span></h1>
525                        <div id="brddesc"><p><?php echo $lang_update['Update message'] ?></p><p><strong><?php echo $lang_update['Note']; ?></strong> <?php echo $lang_update['Members message']; ?></p></div>
526                </div>
527        </div>
528</div>
529
530<div id="brdmain">
531<div class="blockform">
532        <h2><span><?php echo $lang_update['Update'] ?></span></h2>
533        <div class="box">
534                <form id="install" method="post" action="db_update.php">
535                        <input type="hidden" name="stage" value="start" />
536                        <div class="inform">
537                                <fieldset>
538                                <legend><?php echo $lang_update['Administrator only'] ?></legend>
539                                        <div class="infldset">
540                                                <p><?php echo $lang_update['Database password info'] ?></p>
541                                                <p><strong><?php echo $lang_update['Note']; ?></strong> <?php echo $lang_update['Database password note'] ?></p>
542                                                <label class="required"><strong><?php echo $lang_update['Database password'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br /><input type="password" id="req_db_pass" name="req_db_pass" /><br /></label>
543                                                <p><?php echo $lang_update['Maintenance message info'] ?></p>
544                                                <div class="txtarea">
545                                                        <label class="required"><strong><?php echo $lang_update['Maintenance message'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br />
546                                                        <textarea name="req_maintenance_message" rows="4" cols="65"><?php echo pun_htmlspecialchars($pun_config['o_maintenance_message']) ?></textarea><br /></label>
547                                                </div>
548                                        </div>
549                                </fieldset>
550                        </div>
551                        <div class="inform">
552                                <div class="forminfo">
553                                        <p><?php echo $lang_update['Intro 1'] ?></p>
554                                        <p><?php echo $lang_update['Intro 2'] ?></p>
555<?php
556
557        if (strpos($cur_version, '1.2') === 0)
558        {
559                if (!function_exists('iconv') && !function_exists('mb_convert_encoding'))
560                {
561
562?>
563                                        <p><?php echo $lang_update['No charset conversion'] ?></p>
564<?php
565
566                }
567
568?>
569                                </div>
570                        </div>
571                        <div class="inform">
572                                <div class="forminfo">
573                                        <p><?php echo $lang_update['Enable conversion'] ?></p>
574                                        <p><?php echo $lang_update['Current character set'] ?></p>
575                                </div>
576                                <fieldset>
577                                        <legend><?php echo $lang_update['Charset conversion'] ?></legend>
578                                        <div class="infldset">
579                                                <div class="rbox">
580                                                        <label><input type="checkbox" name="convert_charset" value="1" checked="checked" /><?php echo $lang_update['Enable conversion label'] ?><br /></label>
581                                                </div>
582                                                <label>
583                                                        <strong><?php echo $lang_update['Current character set label'] ?></strong><br /><?php echo $lang_update['Current character set info'] ?><br />
584                                                        <input type="text" name="req_old_charset" size="12" maxlength="20" value="<?php echo $old_charset ?>" /><br />
585                                                </label>
586                                        </div>
587                                </fieldset>
588<?php
589
590        }
591        else
592                echo "\t\t\t\t".'</div>'."\n";
593
594?>
595                        </div>
596                        <p class="buttons"><input type="submit" name="start" value="<?php echo $lang_update['Start update'] ?>" /></p>
597                </form>
598        </div>
599</div>
600</div>
601
602</div>
603<div class="end-box"><div><!-- Bottom Corners --></div></div>
604</div>
605
606</body>
607</html>
608<?php
609
610        }
611        $db->end_transaction();
612        $db->close();
613        exit;
614
615}
616
617// Read the lock file
618$lock = file_exists(FORUM_CACHE_DIR.'db_update.lock') ? trim(file_get_contents(FORUM_CACHE_DIR.'db_update.lock')) : false;
619$lock_error = false;
620
621// Generate or fetch the UID - this confirms we have a valid admin
622if (isset($_POST['req_db_pass']))
623{
624        $req_db_pass = strtolower(trim($_POST['req_db_pass']));
625
626        switch ($db_type)
627        {
628                // For SQLite we compare against the database file name, since the password is left blank
629                case 'sqlite':
630                        if ($req_db_pass != strtolower($db_name))
631                                error(sprintf($lang_update['Invalid file error'], 'config.php'));
632
633                        break;
634                // For everything else, check the password matches
635                default:
636                        if ($req_db_pass != strtolower($db_password))
637                                error(sprintf($lang_update['Invalid password error'], 'config.php'));
638
639                        break;
640        }
641
642        // Generate a unique id to identify this session, only if this is a valid session
643        $uid = pun_hash($req_db_pass.'|'.uniqid(rand(), true));
644        if ($lock) // We already have a lock file
645                $lock_error = true;
646        else // Create the lock file
647        {
648                $fh = @fopen(FORUM_CACHE_DIR.'db_update.lock', 'wb');
649                if (!$fh)
650                        error(sprintf($lang_update['Unable to lock error'], 'cache'));
651
652                fwrite($fh, $uid);
653                fclose($fh);
654
655                // Update maintenance message
656                if ($_POST['req_maintenance_message'] != '')
657                        $maintenance_message = pun_trim(pun_linebreaks($_POST['req_maintenance_message']));
658                else
659                {
660                        // Load the admin_options.php language file
661                        require PUN_ROOT.'lang/'.$default_lang.'/admin_options.php';
662
663                        $maintenance_message = $lang_admin_options['Default maintenance message'];
664                }
665
666                $db->query('UPDATE '.$db->prefix.'config SET conf_value=\''.$db->escape($maintenance_message).'\' WHERE conf_name=\'o_maintenance_message\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
667
668                // Regenerate the config cache
669                if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
670                        require PUN_ROOT.'include/cache.php';
671
672                generate_config_cache();
673        }
674}
675else if (isset($_GET['uid']))
676{
677        $uid = trim($_GET['uid']);
678        if (!$lock || $lock != $uid) // The lock doesn't exist or doesn't match the given UID
679                $lock_error = true;
680}
681else
682        error($lang_update['No password error']);
683
684// If there is an error with the lock file
685if ($lock_error)
686        error(sprintf($lang_update['Script runs error'], FORUM_CACHE_DIR.'db_update.lock'));
687
688switch ($stage)
689{
690        // Start by updating the database structure
691        case 'start':
692                $query_str = '?stage=preparse_posts';
693
694                // If we don't need to update the database, skip this stage
695                if (isset($pun_config['o_database_revision']) && $pun_config['o_database_revision'] >= UPDATE_TO_DB_REVISION)
696                        break;
697
698                // Make all email fields VARCHAR(80)
699                $db->alter_field('bans', 'email', 'VARCHAR(80)', true) or error('Unable to alter email field', __FILE__, __LINE__, $db->error());
700                $db->alter_field('posts', 'poster_email', 'VARCHAR(80)', true) or error('Unable to alter poster_email field', __FILE__, __LINE__, $db->error());
701                $db->alter_field('users', 'email', 'VARCHAR(80)', false, '') or error('Unable to alter email field', __FILE__, __LINE__, $db->error());
702                $db->alter_field('users', 'jabber', 'VARCHAR(80)', true) or error('Unable to alter jabber field', __FILE__, __LINE__, $db->error());
703                $db->alter_field('users', 'msn', 'VARCHAR(80)', true) or error('Unable to alter msn field', __FILE__, __LINE__, $db->error());
704                $db->alter_field('users', 'activate_string', 'VARCHAR(80)', true) or error('Unable to alter activate_string field', __FILE__, __LINE__, $db->error());
705
706                // Make all IP fields VARCHAR(39) to support IPv6
707                $db->alter_field('posts', 'poster_ip', 'VARCHAR(39)', true) or error('Unable to alter poster_ip field', __FILE__, __LINE__, $db->error());
708                $db->alter_field('users', 'registration_ip', 'VARCHAR(39)', false, '0.0.0.0') or error('Unable to alter registration_ip field', __FILE__, __LINE__, $db->error());
709
710                // Make the message field MEDIUMTEXT to allow proper conversion of 65535 character posts to UTF-8
711                $db->alter_field('posts', 'message', 'MEDIUMTEXT', true) or error('Unable to alter message field', __FILE__, __LINE__, $db->error());
712
713                // Add the DST option to the users table
714                $db->add_field('users', 'dst', 'TINYINT(1)', false, 0, 'timezone') or error('Unable to add dst field', __FILE__, __LINE__, $db->error());
715
716                // Add the last_post field to the online table
717                $db->add_field('online', 'last_post', 'INT(10) UNSIGNED', true, null, null) or error('Unable to add last_post field', __FILE__, __LINE__, $db->error());
718
719                // Add the last_search field to the online table
720                $db->add_field('online', 'last_search', 'INT(10) UNSIGNED', true, null, null) or error('Unable to add last_search field', __FILE__, __LINE__, $db->error());
721
722                // Add the last_search column to the users table
723                $db->add_field('users', 'last_search', 'INT(10) UNSIGNED', true, null, 'last_post') or error('Unable to add last_search field', __FILE__, __LINE__, $db->error());
724
725                // Drop use_avatar column from users table
726                $db->drop_field('users', 'use_avatar') or error('Unable to drop use_avatar field', __FILE__, __LINE__, $db->error());
727
728                // Drop save_pass column from users table
729                $db->drop_field('users', 'save_pass') or error('Unable to drop save_pass field', __FILE__, __LINE__, $db->error());
730
731                // Drop g_edit_subjects_interval column from groups table
732                $db->drop_field('groups', 'g_edit_subjects_interval');
733
734                // Add database revision number
735                if (!array_key_exists('o_database_revision', $pun_config))
736                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_database_revision\', \'0\')') or error('Unable to insert config value \'o_database_revision\'', __FILE__, __LINE__, $db->error());
737
738                // Add search index revision number
739                if (!array_key_exists('o_searchindex_revision', $pun_config))
740                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_searchindex_revision\', \'0\')') or error('Unable to insert config value \'o_searchindex_revision\'', __FILE__, __LINE__, $db->error());
741
742                // Add parser revision number
743                if (!array_key_exists('o_parser_revision', $pun_config))
744                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_parser_revision\', \'0\')') or error('Unable to insert config value \'o_parser_revision\'', __FILE__, __LINE__, $db->error());
745
746                // Add default email setting option
747                if (!array_key_exists('o_default_email_setting', $pun_config))
748                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_default_email_setting\', \'1\')') or error('Unable to insert config value \'o_default_email_setting\'', __FILE__, __LINE__, $db->error());
749
750                // Make sure we have o_additional_navlinks (was added in 1.2.1)
751                if (!array_key_exists('o_additional_navlinks', $pun_config))
752                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_additional_navlinks\', \'\')') or error('Unable to insert config value \'o_additional_navlinks\'', __FILE__, __LINE__, $db->error());
753
754                // Insert new config option o_topic_views
755                if (!array_key_exists('o_topic_views', $pun_config))
756                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_topic_views\', \'1\')') or error('Unable to insert config value \'o_topic_views\'', __FILE__, __LINE__, $db->error());
757
758                // Insert new config option o_signatures
759                if (!array_key_exists('o_signatures', $pun_config))
760                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_signatures\', \'1\')') or error('Unable to insert config value \'o_signatures\'', __FILE__, __LINE__, $db->error());
761
762                // Insert new config option o_smtp_ssl
763                if (!array_key_exists('o_smtp_ssl', $pun_config))
764                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_smtp_ssl\', \'0\')') or error('Unable to insert config value \'o_smtp_ssl\'', __FILE__, __LINE__, $db->error());
765
766                // Insert new config option o_default_dst
767                if (!array_key_exists('o_default_dst', $pun_config))
768                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_default_dst\', \'0\')') or error('Unable to insert config value \'o_default_dst\'', __FILE__, __LINE__, $db->error());
769
770                // Insert new config option o_quote_depth
771                if (!array_key_exists('o_quote_depth', $pun_config))
772                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_quote_depth\', \'3\')') or error('Unable to insert config value \'o_quote_depth\'', __FILE__, __LINE__, $db->error());
773
774                // Insert new config option o_feed_type
775                if (!array_key_exists('o_feed_type', $pun_config))
776                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_feed_type\', \'2\')') or error('Unable to insert config value \'o_feed_type\'', __FILE__, __LINE__, $db->error());
777
778                // Insert new config option o_feed_ttl
779                if (!array_key_exists('o_feed_ttl', $pun_config))
780                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_feed_ttl\', \'0\')') or error('Unable to insert config value \'o_feed_ttl\'', __FILE__, __LINE__, $db->error());
781
782                // Insert config option o_base_url which was removed in 1.3
783                if (!array_key_exists('o_base_url', $pun_config))
784                {
785                        // If it isn't in $pun_config['o_base_url'] it should be in $base_url, but just in-case it isn't we can make a guess at it
786                        if (!isset($base_url))
787                        {
788                                // Make an educated guess regarding base_url
789                                $base_url  = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';  // protocol
790                                $base_url .= preg_replace('%:(80|443)$%', '', $_SERVER['HTTP_HOST']);                                                   // host[:port]
791                                $base_url .= str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));                                                  // path
792                        }
793
794                        if (substr($base_url, -1) == '/')
795                                $base_url = substr($base_url, 0, -1);
796
797                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_base_url\', \''.$db->escape($base_url).'\')') or error('Unable to insert config value \'o_quote_depth\'', __FILE__, __LINE__, $db->error());
798                }
799
800                if (strpos($cur_version, '1.2') === 0)
801                {
802                        // Groups are almost the same as 1.2:
803                        // unverified:  32000 -> 0
804
805                        $db->query('UPDATE '.$db->prefix.'users SET group_id = 0 WHERE group_id = 32000') or error('Unable to update unverified users', __FILE__, __LINE__, $db->error());
806                }
807                else if (strpos($cur_version, '1.3') === 0)
808                {
809                        // Groups have changed quite a lot from 1.3:
810                        // unverified:  0 -> 0
811                        // admin:               1 -> 1
812                        // mod:                 ? -> 2
813                        // guest:               2 -> 3
814                        // member:              ? -> 4
815
816                        $result = $db->query('SELECT MAX(g_id) + 1 FROM '.$db->prefix.'groups') or error('Unable to select temp group ID', __FILE__, __LINE__, $db->error());
817                        $temp_id = $db->result($result);
818
819                        $result = $db->query('SELECT g_id FROM '.$db->prefix.'groups WHERE g_moderator = 1 AND g_id > 1 LIMIT 1') or error('Unable to select moderator group', __FILE__, __LINE__, $db->error());
820                        if ($db->num_rows($result))
821                                $mod_gid = $db->result($result);
822                        else
823                        {
824                                $db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('."'Moderators', 'Moderator', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0)") or error('Unable to add group', __FILE__, __LINE__, $db->error());
825                                $mod_gid = $db->insert_id();
826                        }
827
828                        $member_gid = $pun_config['o_default_user_group'];
829
830                        // move the mod group to a temp place
831                        $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$temp_id.' WHERE g_id = '.$mod_gid) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
832                        $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$temp_id.' WHERE group_id = '.$mod_gid) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
833                        $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$temp_id.' WHERE group_id = '.$mod_gid) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
834                        if ($member_gid == $mod_gid) $member_gid = $temp_id;
835
836                        // move whoever is in 3 to a spare slot
837                        $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$mod_gid.' WHERE g_id = 3') or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
838                        $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$mod_gid.' WHERE group_id = 3') or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
839                        $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$mod_gid.' WHERE group_id = 3') or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
840                        if ($member_gid == 3) $member_gid = $mod_gid;
841
842                        // move guest to 3
843                        $db->query('UPDATE '.$db->prefix.'groups SET g_id = 3 WHERE g_id = 2') or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
844                        $db->query('UPDATE '.$db->prefix.'users SET group_id = 3 WHERE group_id = 2') or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
845                        $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 3 WHERE group_id = 2') or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
846                        if ($member_gid == 2) $member_gid = 3;
847
848                        // move mod group in temp place to 2
849                        $db->query('UPDATE '.$db->prefix.'groups SET g_id = 2 WHERE g_id = '.$temp_id) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
850                        $db->query('UPDATE '.$db->prefix.'users SET group_id = 2 WHERE group_id = '.$temp_id) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
851                        $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 2 WHERE group_id = '.$temp_id) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
852                        if ($member_gid == $temp_id) $member_gid = 2;
853
854                        // Only move stuff around if it isn't already in the right place
855                        if ($member_gid != $mod_gid || $member_gid != 4)
856                        {
857                                // move members to temp place
858                                $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$temp_id.' WHERE g_id = '.$member_gid) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
859                                $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$temp_id.' WHERE group_id = '.$member_gid) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
860                                $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$temp_id.' WHERE group_id = '.$member_gid) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
861
862                                // move whoever is in 4 to members place
863                                $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$member_gid.' WHERE g_id = 4') or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
864                                $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$member_gid.' WHERE group_id = 4') or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
865                                $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$member_gid.' WHERE group_id = 4') or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
866
867                                // move members in temp place to 4
868                                $db->query('UPDATE '.$db->prefix.'groups SET g_id = 4 WHERE g_id = '.$temp_id) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
869                                $db->query('UPDATE '.$db->prefix.'users SET group_id = 4 WHERE group_id = '.$temp_id) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
870                                $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 4 WHERE group_id = '.$temp_id) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
871                        }
872
873                        $db->query('UPDATE '.$db->prefix.'config SET conf_value=\''.$member_gid.'\' WHERE conf_name=\'o_default_user_group\'') or error('Unable to update default user group ID', __FILE__, __LINE__, $db->error());
874                }
875
876                // Server time zone is now simply the default time zone
877                if (!array_key_exists('o_default_timezone', $pun_config))
878                        $db->query('UPDATE '.$db->prefix.'config SET conf_name = \'o_default_timezone\' WHERE conf_name = \'o_server_timezone\'') or error('Unable to update time zone config', __FILE__, __LINE__, $db->error());
879
880                // Increase visit timeout to 30 minutes (only if it hasn't been changed from the default)
881                if (!array_key_exists('o_database_revision', $pun_config) && $pun_config['o_timeout_visit'] == '600')
882                        $db->query('UPDATE '.$db->prefix.'config SET conf_value = \'1800\' WHERE conf_name = \'o_timeout_visit\'') or error('Unable to update visit timeout config', __FILE__, __LINE__, $db->error());
883
884                // Remove obsolete g_post_polls permission from groups table
885                $db->drop_field('groups', 'g_post_polls');
886
887                // Make room for multiple moderator groups
888                if (!$db->field_exists('groups', 'g_moderator'))
889                {
890                        // Add g_moderator column to groups table
891                        $db->add_field('groups', 'g_moderator', 'TINYINT(1)', false, 0, 'g_user_title') or error('Unable to add g_moderator field', __FILE__, __LINE__, $db->error());
892
893                        // Give the moderator group moderator privileges
894                        $db->query('UPDATE '.$db->prefix.'groups SET g_moderator = 1 WHERE g_id = 2') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
895                }
896
897                // Replace obsolete p_mod_edit_users config setting with new per-group permission
898                if (array_key_exists('p_mod_edit_users', $pun_config))
899                {
900                        $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_edit_users\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
901
902                        $db->add_field('groups', 'g_mod_edit_users', 'TINYINT(1)', false, 0, 'g_moderator') or error('Unable to add g_mod_edit_users field', __FILE__, __LINE__, $db->error());
903
904                        $db->query('UPDATE '.$db->prefix.'groups SET g_mod_edit_users = '.$pun_config['p_mod_edit_users'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
905                }
906
907                // Replace obsolete p_mod_rename_users config setting with new per-group permission
908                if (array_key_exists('p_mod_rename_users', $pun_config))
909                {
910                        $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_rename_users\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
911
912                        $db->add_field('groups', 'g_mod_rename_users', 'TINYINT(1)', false, 0, 'g_mod_edit_users') or error('Unable to add g_mod_rename_users field', __FILE__, __LINE__, $db->error());
913
914                        $db->query('UPDATE '.$db->prefix.'groups SET g_mod_rename_users = '.$pun_config['p_mod_rename_users'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
915                }
916
917                // Replace obsolete p_mod_change_passwords config setting with new per-group permission
918                if (array_key_exists('p_mod_change_passwords', $pun_config))
919                {
920                        $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_change_passwords\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
921
922                        $db->add_field('groups', 'g_mod_change_passwords', 'TINYINT(1)', false, 0, 'g_mod_rename_users') or error('Unable to add g_mod_change_passwords field', __FILE__, __LINE__, $db->error());
923
924                        $db->query('UPDATE '.$db->prefix.'groups SET g_mod_change_passwords = '.$pun_config['p_mod_change_passwords'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
925                }
926
927                // Replace obsolete p_mod_ban_users config setting with new per-group permission
928                if (array_key_exists('p_mod_ban_users', $pun_config))
929                {
930                        $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_ban_users\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
931
932                        $db->add_field('groups', 'g_mod_ban_users', 'TINYINT(1)', false, 0, 'g_mod_change_passwords') or error('Unable to add g_mod_ban_users field', __FILE__, __LINE__, $db->error());
933
934                        $db->query('UPDATE '.$db->prefix.'groups SET g_mod_ban_users = '.$pun_config['p_mod_ban_users'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
935                }
936
937                // We need to add a unique index to avoid users having multiple rows in the online table
938                if (!$db->index_exists('online', 'user_id_ident_idx'))
939                {
940                        $db->truncate_table('online') or error('Unable to clear online table', __FILE__, __LINE__, $db->error());
941
942                        if ($mysql)
943                                $db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident(25)'), true) or error('Unable to add user_id_ident_idx index', __FILE__, __LINE__, $db->error());
944                        else
945                                $db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident'), true) or error('Unable to add user_id_ident_idx index', __FILE__, __LINE__, $db->error());
946                }
947
948                // Remove the redundant user_id_idx on the online table
949                $db->drop_index('online', 'user_id_idx') or error('Unable to drop user_id_idx index', __FILE__, __LINE__, $db->error());
950
951                // Add an index to ident on the online table
952                if ($mysql)
953                        $db->add_index('online', 'ident_idx', array('ident(25)')) or error('Unable to add ident_idx index', __FILE__, __LINE__, $db->error());
954                else
955                        $db->add_index('online', 'ident_idx', array('ident')) or error('Unable to add ident_idx index', __FILE__, __LINE__, $db->error());
956
957                // Add an index to logged in the online table
958                $db->add_index('online', 'logged_idx', array('logged')) or error('Unable to add logged_idx index', __FILE__, __LINE__, $db->error());
959
960                // Add an index to last_post in the topics table
961                $db->add_index('topics', 'last_post_idx', array('last_post')) or error('Unable to add last_post_idx index', __FILE__, __LINE__, $db->error());
962
963                // Add an index to username on the bans table
964                if ($mysql)
965                        $db->add_index('bans', 'username_idx', array('username(25)')) or error('Unable to add username_idx index', __FILE__, __LINE__, $db->error());
966                else
967                        $db->add_index('bans', 'username_idx', array('username')) or error('Unable to add username_idx index', __FILE__, __LINE__, $db->error());
968
969                // Change the username_idx on users to a unique index of max size 25
970                $db->drop_index('users', 'username_idx') or error('Unable to drop old username_idx index', __FILE__, __LINE__, $db->error());
971                $field = $mysql ? 'username(25)' : 'username';
972
973                // Attempt to add a unique index. If the user doesn't use a transactional database this can fail due to multiple matching usernames in the
974                // users table. This is bad, but just giving up if it happens is even worse! If it fails just add a regular non-unique index.
975                if (!$db->add_index('users', 'username_idx', array($field), true))
976                        $db->add_index('users', 'username_idx', array($field)) or error('Unable to add username_idx field', __FILE__, __LINE__, $db->error());
977
978                // Add g_view_users field to groups table
979                $db->add_field('groups', 'g_view_users', 'TINYINT(1)', false, 1, 'g_read_board') or error('Unable to add g_view_users field', __FILE__, __LINE__, $db->error());
980
981                // Add the last_email_sent column to the users table and the g_send_email and
982                // g_email_flood columns to the groups table
983                $db->add_field('users', 'last_email_sent', 'INT(10) UNSIGNED', true, null, 'last_search') or error('Unable to add last_email_sent field', __FILE__, __LINE__, $db->error());
984                $db->add_field('groups', 'g_send_email', 'TINYINT(1)', false, 1, 'g_search_users') or error('Unable to add g_send_email field', __FILE__, __LINE__, $db->error());
985                $db->add_field('groups', 'g_email_flood', 'SMALLINT(6)', false, 60, 'g_search_flood') or error('Unable to add g_email_flood field', __FILE__, __LINE__, $db->error());
986
987                // Add the last_report_sent column to the users table and the g_report_flood
988                // column to the groups table
989                $db->add_field('users', 'last_report_sent', 'INT(10) UNSIGNED', true, null, 'last_email_sent') or error('Unable to add last_report_sent field', __FILE__, __LINE__, $db->error());
990                $db->add_field('groups', 'g_report_flood', 'SMALLINT(6)', false, 60, 'g_email_flood') or error('Unable to add g_report_flood field', __FILE__, __LINE__, $db->error());
991
992                // Set non-default g_send_email, g_flood_email and g_flood_report values properly
993                $db->query('UPDATE '.$db->prefix.'groups SET g_send_email = 0 WHERE g_id = 3') or error('Unable to update group email permissions', __FILE__, __LINE__, $db->error());
994                $db->query('UPDATE '.$db->prefix.'groups SET g_email_flood = 0, g_report_flood = 0 WHERE g_id IN (1,2,3)') or error('Unable to update group email permissions', __FILE__, __LINE__, $db->error());
995
996                // Add the auto notify/subscription option to the users table
997                $db->add_field('users', 'auto_notify', 'TINYINT(1)', false, 0, 'notify_with_post') or error('Unable to add auto_notify field', __FILE__, __LINE__, $db->error());
998
999                // Add the first_post_id column to the topics table
1000                if (!$db->field_exists('topics', 'first_post_id'))
1001                {
1002                        $db->add_field('topics', 'first_post_id', 'INT(10) UNSIGNED', false, 0, 'posted') or error('Unable to add first_post_id field', __FILE__, __LINE__, $db->error());
1003                        $db->add_index('topics', 'first_post_id_idx', array('first_post_id')) or error('Unable to add first_post_id_idx index', __FILE__, __LINE__, $db->error());
1004
1005                        // Now that we've added the column and indexed it, we need to give it correct data
1006                        $result = $db->query('SELECT MIN(id) AS first_post, topic_id FROM '.$db->prefix.'posts GROUP BY topic_id') or error('Unable to fetch first_post_id', __FILE__, __LINE__, $db->error());
1007
1008                        while ($cur_post = $db->fetch_assoc($result))
1009                                $db->query('UPDATE '.$db->prefix.'topics SET first_post_id = '.$cur_post['first_post'].' WHERE id = '.$cur_post['topic_id']) or error('Unable to update first_post_id', __FILE__, __LINE__, $db->error());
1010                }
1011
1012                // Move any users with the old unverified status to their new group
1013                $db->query('UPDATE '.$db->prefix.'users SET group_id=0 WHERE group_id=32000') or error('Unable to move unverified users', __FILE__, __LINE__, $db->error());
1014
1015                // Add the ban_creator column to the bans table
1016                $db->add_field('bans', 'ban_creator', 'INT(10) UNSIGNED', false, 0) or error('Unable to add ban_creator field', __FILE__, __LINE__, $db->error());
1017
1018                // Add the time/date format settings to the user table
1019                $db->add_field('users', 'time_format', 'TINYINT(1)', false, 0, 'dst') or error('Unable to add time_format field', __FILE__, __LINE__, $db->error());
1020                $db->add_field('users', 'date_format', 'TINYINT(1)', false, 0, 'dst') or error('Unable to add date_format field', __FILE__, __LINE__, $db->error());
1021
1022                // Change the search_data field to mediumtext
1023                $db->alter_field('search_cache', 'search_data', 'MEDIUMTEXT', true) or error('Unable to alter search_data field', __FILE__, __LINE__, $db->error());
1024
1025                // Incase we had the fulltext search extension installed (1.3-legacy), remove it
1026                $db->drop_index('topics', 'subject_idx') or error('Unable to drop subject_idx index', __FILE__, __LINE__, $db->error());
1027                $db->drop_index('posts', 'message_idx') or error('Unable to drop message_idx index', __FILE__, __LINE__, $db->error());
1028                // Incase we had the fulltext search mod installed (1.2), remove it
1029                $db->drop_index('topics', 'subject_fulltext_search') or error('Unable to drop subject_fulltext_search index', __FILE__, __LINE__, $db->error());
1030                $db->drop_index('posts', 'message_fulltext_search') or error('Unable to drop message_fulltext_search index', __FILE__, __LINE__, $db->error());
1031
1032                // If the search_cache table has been dropped by the fulltext search extension, recreate it
1033                if (!$db->table_exists('search_cache'))
1034                {
1035                        $schema = array(
1036                                'FIELDS'                => array(
1037                                        'id'                    => array(
1038                                                'datatype'              => 'INT(10) UNSIGNED',
1039                                                'allow_null'    => false,
1040                                                'default'               => '0'
1041                                        ),
1042                                        'ident'                 => array(
1043                                                'datatype'              => 'VARCHAR(200)',
1044                                                'allow_null'    => false,
1045                                                'default'               => '\'\''
1046                                        ),
1047                                        'search_data'   => array(
1048                                                'datatype'              => 'MEDIUMTEXT',
1049                                                'allow_null'    => true
1050                                        )
1051                                ),
1052                                'PRIMARY KEY'   => array('id'),
1053                                'INDEXES'               => array(
1054                                        'ident_idx'     => array('ident')
1055                                )
1056                        );
1057
1058                        if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
1059                                $schema['INDEXES']['ident_idx'] = array('ident(8)');
1060
1061                        $db->create_table('search_cache', $schema);
1062                }
1063
1064                // If the search_matches table has been dropped by the fulltext search extension, recreate it
1065                if (!$db->table_exists('search_matches'))
1066                {
1067                        $schema = array(
1068                                'FIELDS'                => array(
1069                                        'post_id'               => array(
1070                                                'datatype'              => 'INT(10) UNSIGNED',
1071                                                'allow_null'    => false,
1072                                                'default'               => '0'
1073                                        ),
1074                                        'word_id'               => array(
1075                                                'datatype'              => 'INT(10) UNSIGNED',
1076                                                'allow_null'    => false,
1077                                                'default'               => '0'
1078                                        ),
1079                                        'subject_match' => array(
1080                                                'datatype'              => 'TINYINT(1)',
1081                                                'allow_null'    => false,
1082                                                'default'               => '0'
1083                                        )
1084                                ),
1085                                'INDEXES'               => array(
1086                                        'word_id_idx'   => array('word_id'),
1087                                        'post_id_idx'   => array('post_id')
1088                                )
1089                        );
1090
1091                        $db->create_table('search_matches', $schema);
1092                }
1093
1094                // If the search_words table has been dropped by the fulltext search extension, recreate it
1095                if (!$db->table_exists('search_words'))
1096                {
1097                        $schema = array(
1098                                'FIELDS'                => array(
1099                                        'id'                    => array(
1100                                                'datatype'              => 'SERIAL',
1101                                                'allow_null'    => false
1102                                        ),
1103                                        'word'                  => array(
1104                                                'datatype'              => 'VARCHAR(20)',
1105                                                'allow_null'    => false,
1106                                                'default'               => '\'\'',
1107                                                'collation'             => 'bin'
1108                                        )
1109                                ),
1110                                'PRIMARY KEY'   => array('word'),
1111                                'INDEXES'               => array(
1112                                        'id_idx'        => array('id')
1113                                )
1114                        );
1115
1116                        if ($db_type == 'sqlite')
1117                        {
1118                                $schema['PRIMARY KEY'] = array('id');
1119                                $schema['UNIQUE KEYS'] = array('word_idx'       => array('word'));
1120                        }
1121
1122                        $db->create_table('search_words', $schema);
1123                }
1124
1125                // Rename the subscription table
1126                $db->rename_table('subscriptions', 'topic_subscriptions');
1127
1128                // if we don't have the forum_subscriptions table, create it
1129                if (!$db->table_exists('forum_subscriptions'))
1130                {
1131                        $schema = array(
1132                                'FIELDS'                => array(
1133                                        'user_id'               => array(
1134                                                'datatype'              => 'INT(10) UNSIGNED',
1135                                                'allow_null'    => false,
1136                                                'default'               => '0'
1137                                        ),
1138                                        'forum_id'              => array(
1139                                                'datatype'              => 'INT(10) UNSIGNED',
1140                                                'allow_null'    => false,
1141                                                'default'               => '0'
1142                                        )
1143                                ),
1144                                'PRIMARY KEY'   => array('user_id', 'forum_id')
1145                        );
1146
1147                        $db->create_table('forum_subscriptions', $schema) or error('Unable to create forum subscriptions table', __FILE__, __LINE__, $db->error());
1148                }
1149
1150                // Insert new config option o_forum_subscriptions
1151                if (!array_key_exists('o_forum_subscriptions', $pun_config))
1152                        $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_forum_subscriptions\', \'1\')') or error('Unable to insert config value \'o_forum_subscriptions\'', __FILE__, __LINE__, $db->error());
1153
1154                // Rename config option o_subscriptions to o_topic_subscriptions
1155                if (!array_key_exists('o_topic_subscriptions', $pun_config))
1156                        $db->query('UPDATE '.$db->prefix.'config SET conf_name=\'o_topic_subscriptions\' WHERE conf_name=\'o_subscriptions\'') or error('Unable to rename config value \'o_subscriptions\'', __FILE__, __LINE__, $db->error());
1157
1158                // Change the default style if the old doesn't exist anymore
1159                if ($pun_config['o_default_style'] != $default_style)
1160                        $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.$db->escape($default_style).'\' WHERE conf_name = \'o_default_style\'') or error('Unable to update default style config', __FILE__, __LINE__, $db->error());
1161
1162                // For MySQL(i) without InnoDB, change the engine of the online table (for performance reasons)
1163                if ($db_type == 'mysql' || $db_type == 'mysqli')
1164                        $db->query('ALTER TABLE '.$db->prefix.'online ENGINE = MyISAM') or error('Unable to change engine type of online table to MyISAM', __FILE__, __LINE__, $db->error());
1165
1166                // Should we do charset conversion or not?
1167                if (strpos($cur_version, '1.2') === 0 && isset($_POST['convert_charset']))
1168                        $query_str = '?stage=conv_bans&req_old_charset='.$old_charset;
1169
1170                break;
1171
1172
1173        // Convert bans
1174        case 'conv_bans':
1175                $query_str = '?stage=conv_categories&req_old_charset='.$old_charset;
1176
1177                function _conv_bans($cur_item, $old_charset)
1178                {
1179                        global $lang_update;
1180
1181                        echo sprintf($lang_update['Converting item'], $lang_update['ban'], $cur_item['id']).'<br />'."\n";
1182
1183                        convert_to_utf8($cur_item['username'], $old_charset);
1184                        convert_to_utf8($cur_item['message'], $old_charset);
1185
1186                        return $cur_item;
1187                }
1188
1189                $end_at = convert_table_utf8($db->prefix.'bans', '_conv_bans', $old_charset, 'id', $start_at);
1190
1191                if ($end_at !== true)
1192                        $query_str = '?stage=conv_bans&req_old_charset='.$old_charset.'&start_at='.$end_at;
1193
1194                break;
1195
1196
1197        // Convert categories
1198        case 'conv_categories':
1199                $query_str = '?stage=conv_censors&req_old_charset='.$old_charset;
1200
1201                echo sprintf($lang_update['Converting'], $lang_update['categories']).'<br />'."\n";
1202
1203                function _conv_categories($cur_item, $old_charset)
1204                {
1205                        convert_to_utf8($cur_item['cat_name'], $old_charset);
1206
1207                        return $cur_item;
1208                }
1209
1210                convert_table_utf8($db->prefix.'categories', '_conv_categories', $old_charset, 'id');
1211
1212                break;
1213
1214
1215        // Convert censor words
1216        case 'conv_censors':
1217                $query_str = '?stage=conv_config&req_old_charset='.$old_charset;
1218
1219                echo sprintf($lang_update['Converting'], $lang_update['censor words']).'<br />'."\n";
1220
1221                function _conv_censoring($cur_item, $old_charset)
1222                {
1223                        convert_to_utf8($cur_item['search_for'], $old_charset);
1224                        convert_to_utf8($cur_item['replace_with'], $old_charset);
1225
1226                        return $cur_item;
1227                }
1228
1229                convert_table_utf8($db->prefix.'censoring', '_conv_censoring', $old_charset, 'id');
1230
1231                break;
1232
1233
1234        // Convert config
1235        case 'conv_config':
1236                $query_str = '?stage=conv_forums&req_old_charset='.$old_charset;
1237
1238                echo sprintf($lang_update['Converting'], $lang_update['configuration']).'<br />'."\n";
1239
1240                function _conv_config($cur_item, $old_charset)
1241                {
1242                        convert_to_utf8($cur_item['conf_value'], $old_charset);
1243
1244                        return $cur_item;
1245                }
1246
1247                convert_table_utf8($db->prefix.'config', '_conv_config', $old_charset, 'conf_name');
1248
1249                break;
1250
1251
1252        // Convert forums
1253        case 'conv_forums':
1254                $query_str = '?stage=conv_perms&req_old_charset='.$old_charset;
1255
1256                echo sprintf($lang_update['Converting'], $lang_update['forums']).'<br />'."\n";
1257
1258                function _conv_forums($cur_item, $old_charset)
1259                {
1260                        $moderators = ($cur_item['moderators'] != '') ? unserialize($cur_item['moderators']) : array();
1261                        $moderators_utf8 = array();
1262                        foreach ($moderators as $mod_username => $mod_user_id)
1263                        {
1264                                convert_to_utf8($mod_username, $old_charset);
1265                                $moderators_utf8[$mod_username] = $mod_user_id;
1266                        }
1267
1268                        convert_to_utf8($cur_item['forum_name'], $old_charset);
1269                        convert_to_utf8($cur_item['forum_desc'], $old_charset);
1270
1271                        if (!empty($moderators_utf8))
1272                                $cur_item['moderators'] = serialize($moderators_utf8);
1273
1274                        return $cur_item;
1275                }
1276
1277                convert_table_utf8($db->prefix.'forums', '_conv_forums', $old_charset, 'id');
1278
1279                break;
1280
1281
1282        // Convert forum permissions
1283        case 'conv_perms':
1284                $query_str = '?stage=conv_groups&req_old_charset='.$old_charset;
1285
1286                alter_table_utf8($db->prefix.'forum_perms');
1287
1288                break;
1289
1290
1291        // Convert groups
1292        case 'conv_groups':
1293                $query_str = '?stage=conv_online&req_old_charset='.$old_charset;
1294
1295                echo sprintf($lang_update['Converting'], $lang_update['groups']).'<br />'."\n";
1296
1297                function _conv_groups($cur_item, $old_charset)
1298                {
1299                        convert_to_utf8($cur_item['g_title'], $old_charset);
1300                        convert_to_utf8($cur_item['g_user_title'], $old_charset);
1301
1302                        return $cur_item;
1303                }
1304
1305                convert_table_utf8($db->prefix.'groups', '_conv_groups', $old_charset, 'g_id');
1306
1307                break;
1308
1309
1310        // Convert online
1311        case 'conv_online':
1312                $query_str = '?stage=conv_posts&req_old_charset='.$old_charset;
1313
1314                // Truncate the table
1315                $db->truncate_table('online') or error('Unable to empty online table', __FILE__, __LINE__, $db->error());
1316
1317                alter_table_utf8($db->prefix.'online');
1318
1319                break;
1320
1321
1322        // Convert posts
1323        case 'conv_posts':
1324                $query_str = '?stage=conv_ranks&req_old_charset='.$old_charset;
1325
1326                function _conv_posts($cur_item, $old_charset)
1327                {
1328                        global $lang_update;
1329
1330                        echo sprintf($lang_update['Converting item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
1331
1332                        convert_to_utf8($cur_item['poster'], $old_charset);
1333                        convert_to_utf8($cur_item['message'], $old_charset);
1334                        convert_to_utf8($cur_item['edited_by'], $old_charset);
1335
1336                        return $cur_item;
1337                }
1338
1339                $end_at = convert_table_utf8($db->prefix.'posts', '_conv_posts', $old_charset, 'id', $start_at);
1340
1341                if ($end_at !== true)
1342                        $query_str = '?stage=conv_posts&req_old_charset='.$old_charset.'&start_at='.$end_at;
1343
1344                break;
1345
1346
1347        // Convert ranks
1348        case 'conv_ranks':
1349                $query_str = '?stage=conv_reports&req_old_charset='.$old_charset;
1350
1351                echo sprintf($lang_update['Converting'], $lang_update['ranks']).'<br />'."\n";
1352
1353                function _conv_ranks($cur_item, $old_charset)
1354                {
1355                        convert_to_utf8($cur_item['rank'], $old_charset);
1356
1357                        return $cur_item;
1358                }
1359
1360                convert_table_utf8($db->prefix.'ranks', '_conv_ranks', $old_charset, 'id');
1361
1362                break;
1363
1364
1365        // Convert reports
1366        case 'conv_reports':
1367                $query_str = '?stage=conv_search_cache&req_old_charset='.$old_charset;
1368
1369                function _conv_reports($cur_item, $old_charset)
1370                {
1371                        global $lang_update;
1372
1373                        echo sprintf($lang_update['Converting item'], $lang_update['report'], $cur_item['id']).'<br />'."\n";
1374
1375                        convert_to_utf8($cur_item['message'], $old_charset);
1376
1377                        return $cur_item;
1378                }
1379
1380                $end_at = convert_table_utf8($db->prefix.'reports', '_conv_reports', $old_charset, 'id', $start_at);
1381
1382                if ($end_at !== true)
1383                        $query_str = '?stage=conv_reports&req_old_charset='.$old_charset.'&start_at='.$end_at;
1384
1385                break;
1386
1387
1388        // Convert search cache
1389        case 'conv_search_cache':
1390                $query_str = '?stage=conv_search_matches&req_old_charset='.$old_charset;
1391
1392                // Truncate the table
1393                $db->truncate_table('search_cache') or error('Unable to empty search cache table', __FILE__, __LINE__, $db->error());
1394
1395                alter_table_utf8($db->prefix.'search_cache');
1396
1397                break;
1398
1399
1400        // Convert search matches
1401        case 'conv_search_matches':
1402                $query_str = '?stage=conv_search_words&req_old_charset='.$old_charset;
1403
1404                // Truncate the table
1405                $db->truncate_table('search_matches') or error('Unable to empty search index match table', __FILE__, __LINE__, $db->error());
1406
1407                alter_table_utf8($db->prefix.'search_matches');
1408
1409                break;
1410
1411
1412        // Convert search words
1413        case 'conv_search_words':
1414                $query_str = '?stage=conv_subscriptions&req_old_charset='.$old_charset;
1415
1416                // Truncate the table
1417                $db->truncate_table('search_words') or error('Unable to empty search index words table', __FILE__, __LINE__, $db->error());
1418
1419                // Reset the sequence for the search words (not needed for SQLite)
1420                switch ($db_type)
1421                {
1422                        case 'mysql':
1423                        case 'mysqli':
1424                        case 'mysql_innodb':
1425                        case 'mysqli_innodb':
1426                                $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Unable to update table auto_increment', __FILE__, __LINE__, $db->error());
1427                                break;
1428
1429                        case 'pgsql';
1430                                $db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)') or error('Unable to update sequence', __FILE__, __LINE__, $db->error());
1431                                break;
1432                }
1433
1434                alter_table_utf8($db->prefix.'search_words');
1435
1436                break;
1437
1438
1439        // Convert subscriptions
1440        case 'conv_subscriptions':
1441                $query_str = '?stage=conv_topics&req_old_charset='.$old_charset;
1442
1443                // By this stage we should have already renamed the subscription table
1444                alter_table_utf8($db->prefix.'topic_subscriptions');
1445                alter_table_utf8($db->prefix.'forum_subscriptions'); // This should actually already be utf8, but for consistency...
1446
1447                break;
1448
1449
1450        // Convert topics
1451        case 'conv_topics':
1452                $query_str = '?stage=conv_users&req_old_charset='.$old_charset;
1453
1454                function _conv_topics($cur_item, $old_charset)
1455                {
1456                        global $lang_update;
1457
1458                        echo sprintf($lang_update['Converting item'], $lang_update['topic'], $cur_item['id']).'<br />'."\n";
1459
1460                        convert_to_utf8($cur_item['poster'], $old_charset);
1461                        convert_to_utf8($cur_item['subject'], $old_charset);
1462                        convert_to_utf8($cur_item['last_poster'], $old_charset);
1463
1464                        return $cur_item;
1465                }
1466
1467                $end_at = convert_table_utf8($db->prefix.'topics', '_conv_topics', $old_charset, 'id', $start_at);
1468
1469                if ($end_at !== true)
1470                        $query_str = '?stage=conv_topics&req_old_charset='.$old_charset.'&start_at='.$end_at;
1471
1472                break;
1473
1474
1475        // Convert users
1476        case 'conv_users':
1477                $query_str = '?stage=preparse_posts';
1478
1479                if ($start_at == 0)
1480                        $_SESSION['dupe_users'] = array();
1481
1482                function _conv_users($cur_item, $old_charset)
1483                {
1484                        global $lang_update;
1485
1486                        echo sprintf($lang_update['Converting item'], $lang_update['user'], $cur_item['id']).'<br />'."\n";
1487
1488                        convert_to_utf8($cur_item['username'], $old_charset);
1489                        convert_to_utf8($cur_item['title'], $old_charset);
1490                        convert_to_utf8($cur_item['realname'], $old_charset);
1491                        convert_to_utf8($cur_item['location'], $old_charset);
1492                        convert_to_utf8($cur_item['signature'], $old_charset);
1493                        convert_to_utf8($cur_item['admin_note'], $old_charset);
1494
1495                        return $cur_item;
1496                }
1497
1498                function _error_users($cur_user)
1499                {
1500                        $_SESSION['dupe_users'][$cur_user['id']] = $cur_user;
1501                }
1502
1503                $end_at = convert_table_utf8($db->prefix.'users', '_conv_users', $old_charset, 'id', $start_at, '_error_users');
1504
1505                if ($end_at !== true)
1506                        $query_str = '?stage=conv_users&req_old_charset='.$old_charset.'&start_at='.$end_at;
1507                else if (!empty($_SESSION['dupe_users']))
1508                        $query_str = '?stage=conv_users_dupe';
1509
1510                break;
1511
1512
1513        // Handle any duplicate users which occured due to conversion
1514        case 'conv_users_dupe':
1515                $query_str = '?stage=preparse_posts';
1516
1517                if (!$mysql || empty($_SESSION['dupe_users']))
1518                        break;
1519
1520                if (isset($_POST['form_sent']))
1521                {
1522                        $errors = array();
1523
1524                        require PUN_ROOT.'include/email.php';
1525
1526                        foreach ($_SESSION['dupe_users'] as $id => $cur_user)
1527                        {
1528                                $errors[$id] = array();
1529
1530                                $username = pun_trim($_POST['dupe_users'][$id]);
1531
1532                                if (pun_strlen($username) < 2)
1533                                        $errors[$id][] = $lang_update['Username too short error'];
1534                                else if (pun_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
1535                                        $errors[$id][] = $lang_update['Username too long error'];
1536                                else if (!strcasecmp($username, 'Guest'))
1537                                        $errors[$id][] = $lang_update['Username Guest reserved error'];
1538                                else if (preg_match('%[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}%', $username) || preg_match('%((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))%', $username))
1539                                        $errors[$id][] = $lang_update['Username IP format error'];
1540                                else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
1541                                        $errors[$id][] = $lang_update['Username bad characters error'];
1542                                else if (preg_match('%(?:\[/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*)\]|\[(?:img|url|quote|list)=)%i', $username))
1543                                        $errors[$id][] = $lang_update['Username BBCode error'];
1544
1545                                $result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(ucp_preg_replace('%[^\p{L}\p{N}]%u', '', $username)).'\')) AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
1546
1547                                if ($db->num_rows($result))
1548                                {
1549                                        $busy = $db->result($result);
1550                                        $errors[$id][] = sprintf($lang_update['Username duplicate error'], pun_htmlspecialchars($busy));
1551                                }
1552
1553                                if (empty($errors[$id]))
1554                                {
1555                                        $old_username = $cur_user['username'];
1556                                        $_SESSION['dupe_users'][$id]['username'] = $cur_user['username'] = $username;
1557
1558                                        $temp = array();
1559                                        foreach ($cur_user as $idx => $value)
1560                                                $temp[$idx] = $value === null ? 'NULL' : '\''.$db->escape($value).'\'';
1561
1562                                        // Insert the renamed user
1563                                        $db->query('INSERT INTO '.$db->prefix.'users('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or error('Unable to insert data to new table', __FILE__, __LINE__, $db->error());
1564
1565                                        // Renaming a user also affects a bunch of other stuff, lets fix that too...
1566                                        $db->query('UPDATE '.$db->prefix.'posts SET poster=\''.$db->escape($username).'\' WHERE poster_id='.$id) or error('Unable to update posts', __FILE__, __LINE__, $db->error());
1567
1568                                        // TODO: The following must compare using collation utf8_bin otherwise we will accidently update posts/topics/etc belonging to both of the duplicate users, not just the one we renamed!
1569                                        $db->query('UPDATE '.$db->prefix.'posts SET edited_by=\''.$db->escape($username).'\' WHERE edited_by=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update posts', __FILE__, __LINE__, $db->error());
1570                                        $db->query('UPDATE '.$db->prefix.'topics SET poster=\''.$db->escape($username).'\' WHERE poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
1571                                        $db->query('UPDATE '.$db->prefix.'topics SET last_poster=\''.$db->escape($username).'\' WHERE last_poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
1572                                        $db->query('UPDATE '.$db->prefix.'forums SET last_poster=\''.$db->escape($username).'\' WHERE last_poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update forums', __FILE__, __LINE__, $db->error());
1573                                        $db->query('UPDATE '.$db->prefix.'online SET ident=\''.$db->escape($username).'\' WHERE ident=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update online list', __FILE__, __LINE__, $db->error());
1574
1575                                        // If the user is a moderator or an administrator we have to update the moderator lists
1576                                        $result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$cur_user['group_id']) or error('Unable to fetch group', __FILE__, __LINE__, $db->error());
1577                                        $group_mod = $db->result($result);
1578
1579                                        if ($cur_user['group_id'] == PUN_ADMIN || $group_mod == '1')
1580                                        {
1581                                                $result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
1582
1583                                                while ($cur_forum = $db->fetch_assoc($result))
1584                                                {
1585                                                        $cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
1586
1587                                                        if (in_array($id, $cur_moderators))
1588                                                        {
1589                                                                unset($cur_moderators[$old_username]);
1590                                                                $cur_moderators[$username] = $id;
1591                                                                uksort($cur_moderators, 'utf8_strcasecmp');
1592
1593                                                                $db->query('UPDATE '.$db->prefix.'forums SET moderators=\''.$db->escape(serialize($cur_moderators)).'\' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
1594                                                        }
1595                                                }
1596                                        }
1597
1598                                        // Email the user alerting them of the change
1599                                        if (file_exists(PUN_ROOT.'lang/'.$cur_user['language'].'/mail_templates/rename.tpl'))
1600                                                $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_user['language'].'/mail_templates/rename.tpl'));
1601                                        else if (file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/mail_templates/rename.tpl'))
1602                                                $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/mail_templates/rename.tpl'));
1603                                        else
1604                                                $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/English/mail_templates/rename.tpl'));
1605
1606                                        // The first row contains the subject
1607                                        $first_crlf = strpos($mail_tpl, "\n");
1608                                        $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
1609                                        $mail_message = trim(substr($mail_tpl, $first_crlf));
1610
1611                                        $mail_subject = str_replace('<board_title>', $pun_config['o_board_title'], $mail_subject);
1612                                        $mail_message = str_replace('<base_url>', get_base_url().'/', $mail_message);
1613                                        $mail_message = str_replace('<old_username>', $old_username, $mail_message);
1614                                        $mail_message = str_replace('<new_username>', $username, $mail_message);
1615                                        $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
1616
1617                                        pun_mail($cur_user['email'], $mail_subject, $mail_message);
1618
1619                                        unset($_SESSION['dupe_users'][$id]);
1620                                }
1621                        }
1622                }
1623
1624                if (!empty($_SESSION['dupe_users']))
1625                {
1626                        $query_str = '';
1627
1628?>
1629<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1630
1631<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
1632<head>
1633<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1634<title><?php echo $lang_update['Update'] ?></title>
1635<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
1636</head>
1637<body>
1638
1639<div id="pundb_update" class="pun">
1640<div class="top-box"><div><!-- Top Corners --></div></div>
1641<div class="punwrap">
1642
1643<div class="blockform">
1644        <h2><span><?php echo $lang_update['Error converting users'] ?></span></h2>
1645        <div class="box">
1646                <form method="post" action="db_update.php?stage=conv_users_dupe&amp;uid=<?php echo $uid ?>">
1647                        <input type="hidden" name="form_sent" value="1" />
1648                        <div class="inform">
1649                                <div class="forminfo">
1650                                                <p style="font-size: 1.1em"><?php echo $lang_update['Error info 1'] ?></p>
1651                                                <p style="font-size: 1.1em"><?php echo $lang_update['Error info 2'] ?></p>
1652                                </div>
1653                        </div>
1654<?php
1655
1656                        foreach ($_SESSION['dupe_users'] as $id => $cur_user)
1657                        {
1658
1659?>
1660                        <div class="inform">
1661                                <fieldset>
1662                                        <legend><?php echo pun_htmlspecialchars($cur_user['username']); ?></legend>
1663                                        <div class="infldset">
1664                                                <label class="required"><strong><?php echo $lang_update['New username'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br /><input type="text" name="<?php echo 'dupe_users['.$id.']'; ?>" value="<?php if (isset($_POST['dupe_users'][$id])) echo pun_htmlspecialchars($_POST['dupe_users'][$id]); ?>" size="25" maxlength="25" /><br /></label>
1665                                        </div>
1666                                </fieldset>
1667<?php if (!empty($errors[$id])): ?>                             <div class="forminfo error-info">
1668                                        <h3><?php echo $lang_update['Correct errors'] ?></h3>
1669                                        <ul class="error-list">
1670<?php
1671
1672foreach ($errors[$id] as $cur_error)
1673        echo "\t\t\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
1674?>
1675                                        </ul>
1676                                </div>
1677<?php endif; ?>                 </div>
1678<?php
1679
1680                        }
1681
1682?>
1683                        <p class="buttons"><input type="submit" name="rename" value="<?php echo $lang_update['Rename users'] ?>" /></p>
1684                </form>
1685        </div>
1686</div>
1687
1688</div>
1689<div class="end-box"><div><!-- Bottom Corners --></div></div>
1690</div>
1691
1692</body>
1693</html>
1694<?php
1695
1696                }
1697
1698                break;
1699
1700
1701        // Preparse posts
1702        case 'preparse_posts':
1703                $query_str = '?stage=preparse_sigs';
1704
1705                // If we don't need to parse the posts, skip this stage
1706                if (isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION)
1707                        break;
1708
1709                require PUN_ROOT.'include/parser.php';
1710
1711                // Fetch posts to process this cycle
1712                $result = $db->query('SELECT id, message FROM '.$db->prefix.'posts WHERE id > '.$start_at.' ORDER BY id ASC LIMIT '.PER_PAGE) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
1713
1714                $temp = array();
1715                $end_at = 0;
1716                while ($cur_item = $db->fetch_assoc($result))
1717                {
1718                        echo sprintf($lang_update['Preparsing item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
1719                        $db->query('UPDATE '.$db->prefix.'posts SET message = \''.$db->escape(preparse_bbcode($cur_item['message'], $temp)).'\' WHERE id = '.$cur_item['id']) or error('Unable to update post', __FILE__, __LINE__, $db->error());
1720
1721                        $end_at = $cur_item['id'];
1722                }
1723
1724                // Check if there is more work to do
1725                if ($end_at > 0)
1726                {
1727                        $result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
1728
1729                        if ($db->num_rows($result) > 0)
1730                                $query_str = '?stage=preparse_posts&start_at='.$end_at;
1731                }
1732
1733                break;
1734
1735
1736        // Preparse signatures
1737        case 'preparse_sigs':
1738                $query_str = '?stage=rebuild_idx';
1739
1740                // If we don't need to parse the sigs, skip this stage
1741                if (isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION)
1742                        break;
1743
1744                require PUN_ROOT.'include/parser.php';
1745
1746                // Fetch users to process this cycle
1747                $result = $db->query('SELECT id, signature FROM '.$db->prefix.'users WHERE id > '.$start_at.' ORDER BY id ASC LIMIT '.PER_PAGE) or error('Unable to fetch users', __FILE__, __LINE__, $db->error());
1748
1749                $temp = array();
1750                $end_at = 0;
1751                while ($cur_item = $db->fetch_assoc($result))
1752                {
1753                        echo sprintf($lang_update['Preparsing item'], $lang_update['signature'], $cur_item['id']).'<br />'."\n";
1754                        $db->query('UPDATE '.$db->prefix.'users SET signature = \''.$db->escape(preparse_bbcode($cur_item['signature'], $temp, true)).'\' WHERE id = '.$cur_item['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
1755
1756                        $end_at = $cur_item['id'];
1757                }
1758
1759                // Check if there is more work to do
1760                if ($end_at > 0)
1761                {
1762                        $result = $db->query('SELECT 1 FROM '.$db->prefix.'users WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
1763                        if ($db->num_rows($result) > 0)
1764                                $query_str = '?stage=preparse_sigs&start_at='.$end_at;
1765                }
1766
1767                break;
1768
1769
1770        // Rebuild the search index
1771        case 'rebuild_idx':
1772                $query_str = '?stage=finish';
1773
1774                // If we don't need to update the search index, skip this stage
1775                if (isset($pun_config['o_searchindex_revision']) && $pun_config['o_searchindex_revision'] >= UPDATE_TO_SI_REVISION)
1776                        break;
1777
1778                if ($start_at == 0)
1779                {
1780                        // Truncate the tables just in-case we didn't already (if we are coming directly here without converting the tables)
1781                        $db->truncate_table('search_cache') or error('Unable to empty search cache table', __FILE__, __LINE__, $db->error());
1782                        $db->truncate_table('search_matches') or error('Unable to empty search index match table', __FILE__, __LINE__, $db->error());
1783                        $db->truncate_table('search_words') or error('Unable to empty search index words table', __FILE__, __LINE__, $db->error());
1784
1785                        // Reset the sequence for the search words (not needed for SQLite)
1786                        switch ($db_type)
1787                        {
1788                                case 'mysql':
1789                                case 'mysqli':
1790                                case 'mysql_innodb':
1791                                case 'mysqli_innodb':
1792                                        $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Unable to update table auto_increment', __FILE__, __LINE__, $db->error());
1793                                        break;
1794
1795                                case 'pgsql';
1796                                        $db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)') or error('Unable to update sequence', __FILE__, __LINE__, $db->error());
1797                                        break;
1798                        }
1799                }
1800
1801                require PUN_ROOT.'include/search_idx.php';
1802
1803                // Fetch posts to process this cycle
1804                $result = $db->query('SELECT p.id, p.message, t.subject, t.first_post_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id WHERE p.id > '.$start_at.' ORDER BY p.id ASC LIMIT '.PER_PAGE) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
1805
1806                $end_at = 0;
1807                while ($cur_item = $db->fetch_assoc($result))
1808                {
1809                        echo sprintf($lang_update['Rebuilding index item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
1810
1811                        if ($cur_item['id'] == $cur_item['first_post_id'])
1812                                update_search_index('post', $cur_item['id'], $cur_item['message'], $cur_item['subject']);
1813                        else
1814                                update_search_index('post', $cur_item['id'], $cur_item['message']);
1815
1816                        $end_at = $cur_item['id'];
1817                }
1818
1819                // Check if there is more work to do
1820                if ($end_at > 0)
1821                {
1822                        $result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
1823
1824                        if ($db->num_rows($result) > 0)
1825                                $query_str = '?stage=rebuild_idx&start_at='.$end_at;
1826                }
1827
1828                break;
1829
1830
1831        // Show results page
1832        case 'finish':
1833                // We update the version number
1834                $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO.'\' WHERE conf_name = \'o_cur_version\'') or error('Unable to update version', __FILE__, __LINE__, $db->error());
1835
1836                // And the database revision number
1837                $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_DB_REVISION.'\' WHERE conf_name = \'o_database_revision\'') or error('Unable to update database revision number', __FILE__, __LINE__, $db->error());
1838
1839                // And the search index revision number
1840                $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_SI_REVISION.'\' WHERE conf_name = \'o_searchindex_revision\'') or error('Unable to update search index revision number', __FILE__, __LINE__, $db->error());
1841
1842                // And the parser revision number
1843                $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_PARSER_REVISION.'\' WHERE conf_name = \'o_parser_revision\'') or error('Unable to update parser revision number', __FILE__, __LINE__, $db->error());
1844
1845                // Check the default language still exists!
1846                if (!file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/common.php'))
1847                        $db->query('UPDATE '.$db->prefix.'config SET conf_value = \'English\' WHERE conf_name = \'o_default_lang\'') or error('Unable to update default language', __FILE__, __LINE__, $db->error());
1848
1849                // Check the default style still exists!
1850                if (!file_exists(PUN_ROOT.'style/'.$pun_config['o_default_style'].'.css'))
1851                        $db->query('UPDATE '.$db->prefix.'config SET conf_value = \'Air\' WHERE conf_name = \'o_default_style\'') or error('Unable to update default style', __FILE__, __LINE__, $db->error());
1852
1853                // This feels like a good time to synchronize the forums
1854                $result = $db->query('SELECT id FROM '.$db->prefix.'forums') or error('Unable to fetch forum IDs', __FILE__, __LINE__, $db->error());
1855
1856                while ($row = $db->fetch_row($result))
1857                        update_forum($row[0]);
1858
1859                // Empty the PHP cache
1860                forum_clear_cache();
1861
1862                // Delete the update lock file
1863                @unlink(FORUM_CACHE_DIR.'db_update.lock');
1864
1865?>
1866<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1867
1868<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
1869<head>
1870<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1871<title><?php echo $lang_update['Update'] ?></title>
1872<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
1873</head>
1874<body>
1875
1876<div id="pundb_update" class="pun">
1877<div class="top-box"><div><!-- Top Corners --></div></div>
1878<div class="punwrap">
1879
1880<div class="blockform">
1881        <h2><span><?php echo $lang_update['Update'] ?></span></h2>
1882        <div class="box">
1883                <div class="fakeform">
1884                        <div class="inform">
1885                                <div class="forminfo">
1886                                        <p style="font-size: 1.1em"><?php printf($lang_update['Successfully updated'], sprintf('<a href="index.php">%s</a>', $lang_update['go to index'])) ?></p>
1887                                </div>
1888                        </div>
1889                </div>
1890        </div>
1891</div>
1892
1893</div>
1894<div class="end-box"><div><!-- Bottom Corners --></div></div>
1895</div>
1896
1897</body>
1898</html>
1899<?php
1900
1901                break;
1902}
1903
1904$db->end_transaction();
1905$db->close();
1906
1907if ($query_str != '')
1908        exit('<script type="text/javascript">window.location="db_update.php'.$query_str.'&uid='.$uid.'"</script><noscript><meta http-equiv="refresh" content="0;url=db_update.php'.$query_str.'&uid='.$uid.'" /></noscript>');
Note: See TracBrowser for help on using the repository browser.