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