PageRenderTime 9ms CodeModel.GetById 5ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 1ms

/db_update.php

https://bitbucket.org/gencer/fluxbb
PHP | 1909 lines | 1263 code | 427 blank | 219 comment | 260 complexity | a004d8302dbf5e509f2ced5b2fd985e8 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2
   3/**
   4 * Copyright (C) 2008-2012 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.5.6');
  11
  12define('UPDATE_TO_DB_REVISION', 21);
  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 (is_null($str) || $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(!empty($old_charset) ? $old_charset : 'ISO-8859-1', 'UTF-8', $str);
 270		else if (function_exists('mb_convert_encoding'))
 271			$str = mb_convert_encoding($str, 'UTF-8', !empty($old_charset) ? $old_charset : '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 (is_null($cur_column['Collation']))
 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 it's the first go
 362		if (is_null($start_at) || $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.(is_null($start_at) ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.(is_null($start_at) ? '' : ' 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] = is_null($value) ? 'NULL' : '\''.$db->escape($value).'\'';
 390
 391			$db->query('INSERT INTO '.$table.'_utf8('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or (is_null($error_callback) ? 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 (!is_null($start_at) && $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 it's 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.(is_null($start_at) ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.(is_null($start_at ) ? '' : ' 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.'='.(is_null($value) ? '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 (!is_null($start_at) && $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(pun_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 = pun_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 column 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 column 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_base_url\'', __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 column 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 column 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		// Add the group promotion columns to the groups table
1026		$db->add_field('groups', 'g_promote_min_posts', 'INT(10) UNSIGNED', false, 0, 'g_user_title') or error('Unable to add g_promote_min_posts field', __FILE__, __LINE__, $db->error());
1027		$db->add_field('groups', 'g_promote_next_group', 'INT(10) UNSIGNED', false, 0, 'g_promote_min_posts') or error('Unable to add g_promote_next_group field', __FILE__, __LINE__, $db->error());
1028
1029		// Add a field for the per-group permission to post links
1030		$db->add_field('groups', 'g_post_links', 'TINYINT(1)', false, 1, 'g_delete_topics') or error('Unable to add per-group permission to post links', __FILE__, __LINE__, $db->error());
1031
1032		// Add a field for the per-group permission to promote users to the next auto-promote group
1033		$db->add_field('groups', 'g_mod_promote_users', 'TINYINT(1)', false, 0, 'g_mod_ban_users') or error('Unable to add per-group permission to promote users', __FILE__, __LINE__, $db->error());
1034
1035		// In case we had the fulltext search extension installed (1.3-legacy), remove it
1036		$db->drop_index('topics', 'subject_idx') or error('Unable to drop subject_idx index', __FILE__, __LINE__, $db->error());
1037		$db->drop_index('posts', 'message_idx') or error('Unable to drop message_idx index', __FILE__, __LINE__, $db->error());
1038		// In case we had the fulltext search mod installed (1.2), remove it
1039		$db->drop_index('topics', 'subject_fulltext_search') or error('Unable to drop subject_fulltext_search index', __FILE__, __LINE__, $db->error());
1040		$db->drop_index('posts', 'message_fulltext_search') or error('Unable to drop message_fulltext_search index', __FILE__, __LINE__, $db->error());
1041
1042		// If the search_cache table has been dropped by the fulltext search extension, recreate it
1043		if (!$db->table_exists('search_cache'))
1044		{
1045			$schema = array(
1046				'FIELDS'		=> array(
1047					'id'			=> array(
1048						'datatype'		=> 'INT(10) UNSIGNED',
1049						'allow_null'	=> false,
1050						'default'		=> '0'
1051					),
1052					'ident'			=> array(
1053						'datatype'		=> 'VARCHAR(200)',
1054						'allow_null'	=> false,
1055						'default'		=> '\'\''
1056					),
1057					'search_data'	=> array(
1058						'datatype'		=> 'MEDIUMTEXT',
1059						'allow_null'	=> true
1060					)
1061				),
1062				'PRIMARY KEY'	=> array('id'),
1063				'INDEXES'		=> array(
1064					'ident_idx'	=> array('ident')
1065				)
1066			);
1067
1068			if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
1069				$schema['INDEXES']['ident_idx'] = array('ident(8)');
1070
1071			$db->create_table('search_cache', $schema);
1072		}
1073
1074		// If the search_matches table has been dropped by the fulltext search extension, recreate it
1075		if (!$db->table_exists('search_matches'))
1076		{
1077			$schema = array(
1078				'FIELDS'		=> array(
1079					'post_id'		=> array(
1080						'datatype'		=> 'INT(10) UNSIGNED',
1081						'allow_null'	=> false,
1082						'default'	…

Large files files are truncated, but you can click here to view the full file