PageRenderTime 161ms CodeModel.GetById 4ms app.highlight 122ms RepoModel.GetById 1ms app.codeStats 1ms

/install/upgrade.php

https://github.com/Arantor/Elkarte
PHP | 4467 lines | 3461 code | 597 blank | 409 comment | 721 complexity | 3fe4172b1f5f9edd38337f4143b567e3 MD5 | raw file
   1<?php
   2
   3/**
   4 * @name      ElkArte Forum
   5 * @copyright ElkArte Forum contributors
   6 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
   7 *
   8 * This software is a derived product, based on:
   9 *
  10 * Simple Machines Forum (SMF)
  11 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
  12 * license:	BSD, See included LICENSE.TXT for terms and conditions.
  13 *
  14 * @version 1.0 Alpha
  15 *
  16 */
  17
  18// Version information...
  19define('CURRENT_VERSION', '1.0 Alpha');
  20define('CURRENT_LANG_VERSION', '1.0');
  21
  22$GLOBALS['required_php_version'] = '5.1.0';
  23$GLOBALS['required_mysql_version'] = '4.0.18';
  24
  25$databases = array(
  26	'mysql' => array(
  27		'name' => 'MySQL',
  28		'version' => '4.0.18',
  29		'version_check' => 'return min(mysql_get_server_info(), mysql_get_client_info());',
  30		'utf8_support' => true,
  31		'utf8_version' => '4.1.0',
  32		'utf8_version_check' => 'return mysql_get_server_info();',
  33		'alter_support' => true,
  34	),
  35	'postgresql' => array(
  36		'name' => 'PostgreSQL',
  37		'version' => '8.0',
  38		'version_check' => '$version = pg_version(); return $version[\'client\'];',
  39		'always_has_db' => true,
  40	),
  41	'sqlite' => array(
  42		'name' => 'SQLite',
  43		'version' => '1',
  44		'version_check' => 'return 1;',
  45		'always_has_db' => true,
  46	),
  47);
  48
  49// General options for the script.
  50$timeLimitThreshold = 3;
  51$upgrade_path = dirname(__FILE__);
  52$upgradeurl = $_SERVER['PHP_SELF'];
  53
  54// Where the images etc are kept.
  55$oursite = 'http://www.elkarte.net';
  56
  57// Disable the need for admins to login?
  58$disable_security = false;
  59
  60// How long, in seconds, must admin be inactive to allow someone else to run?
  61$upcontext['inactive_timeout'] = 10;
  62
  63// All the steps in detail.
  64// Number,Name,Function,Progress Weight.
  65$upcontext['steps'] = array(
  66	0 => array(1, 'Login', 'action_welcomeLogin', 2),
  67	1 => array(2, 'Upgrade Options', 'action_upgradeOptions', 2),
  68	2 => array(3, 'Backup', 'action_backupDatabase', 10),
  69	3 => array(4, 'Database Changes', 'action_databaseChanges', 70),
  70	// This is removed as it doesn't really work right at the moment.
  71	//4 => array(5, 'Cleanup Mods', 'action_cleanupMods', 10),
  72	4 => array(5, 'Delete Upgrade', 'action_deleteUpgrade', 1),
  73);
  74// Just to remember which one has files in it.
  75$upcontext['database_step'] = 3;
  76@set_time_limit(600);
  77if (!ini_get('safe_mode'))
  78{
  79	ini_set('mysql.connect_timeout', -1);
  80	ini_set('default_socket_timeout', 900);
  81}
  82// Clean the upgrade path if this is from the client.
  83if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
  84	for ($i = 1; $i < $_SERVER['argc']; $i++)
  85	{
  86		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
  87			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
  88	}
  89
  90// Are we from the client?
  91if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
  92{
  93	$command_line = true;
  94	$disable_security = 1;
  95}
  96else
  97	$command_line = false;
  98
  99// Load this now just because we can.
 100require_once($upgrade_path . '/Settings.php');
 101
 102// Are we logged in?
 103if (isset($upgradeData))
 104{
 105	$upcontext['user'] = unserialize(base64_decode($upgradeData));
 106
 107	// Check for sensible values.
 108	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
 109		$upcontext['user']['started'] = time();
 110	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
 111		$upcontext['user']['updated'] = 0;
 112
 113	$upcontext['started'] = $upcontext['user']['started'];
 114	$upcontext['updated'] = $upcontext['user']['updated'];
 115}
 116
 117// Nothing sensible?
 118if (empty($upcontext['updated']))
 119{
 120	$upcontext['started'] = time();
 121	$upcontext['updated'] = 0;
 122	$upcontext['user'] = array(
 123		'id' => 0,
 124		'name' => 'Guest',
 125		'pass' => 0,
 126		'started' => $upcontext['started'],
 127		'updated' => $upcontext['updated'],
 128	);
 129}
 130
 131// Load up some essential data...
 132loadEssentialData();
 133
 134// Are we going to be mimic'ing SSI at this point?
 135if (isset($_GET['ssi']))
 136{
 137	require_once(SOURCEDIR . '/Subs.php');
 138	require_once(SOURCEDIR . '/Errors.php');
 139	require_once(SOURCEDIR . '/Logging.php');
 140	require_once(SOURCEDIR . '/Load.php');
 141	require_once(SUBSDIR . '/Cache.subs.php');
 142	require_once(SOURCEDIR . '/Security.php');
 143	require_once(SUBSDIR . '/Package.subs.php');
 144
 145	loadUserSettings();
 146	loadPermissions();
 147}
 148
 149// All the non-SSI stuff.
 150if (!function_exists('ip2range'))
 151	require_once(SOURCEDIR . '/Subs.php');
 152
 153if (!function_exists('un_htmlspecialchars'))
 154{
 155	function un_htmlspecialchars($string)
 156	{
 157		return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES)) + array('&#039;' => '\'', '&nbsp;' => ' '));
 158	}
 159}
 160
 161if (!function_exists('text2words'))
 162{
 163	function text2words($text)
 164	{
 165		global $smcFunc;
 166
 167		// Step 1: Remove entities/things we don't consider words:
 168		$words = preg_replace('~(?:[\x0B\0\xA0\t\r\s\n(){}\\[\\]<>!@$%^*.,:+=`\~\?/\\\\]+|&(?:amp|lt|gt|quot);)+~', ' ', $text);
 169
 170		// Step 2: Entities we left to letters, where applicable, lowercase.
 171		$words = preg_replace('~([^&\d]|^)[#;]~', '$1 ', un_htmlspecialchars(strtolower($words)));
 172
 173		// Step 3: Ready to split apart and index!
 174		$words = explode(' ', $words);
 175		$returned_words = array();
 176		foreach ($words as $word)
 177		{
 178			$word = trim($word, '-_\'');
 179
 180			if ($word != '')
 181				$returned_words[] = substr($word, 0, 20);
 182		}
 183
 184		return array_unique($returned_words);
 185	}
 186}
 187
 188if (!function_exists('clean_cache'))
 189{
 190	// Empty out the cache folder.
 191	function clean_cache($type = '')
 192	{
 193		// No directory = no game.
 194		if (!is_dir(CACHEDIR))
 195			return;
 196
 197		// Remove the files in our own disk cache, if any
 198		$dh = opendir(CACHEDIR);
 199		while ($file = readdir($dh))
 200		{
 201			if ($file != '.' && $file != '..' && $file != 'index.php' && $file != '.htaccess' && (!$type || substr($file, 0, strlen($type)) == $type))
 202				@unlink(CACHEDIR . '/' . $file);
 203		}
 204		closedir($dh);
 205
 206		// Invalidate cache, to be sure!
 207		// ... as long as Load.php can be modified, anyway.
 208		@touch(SOURCEDIR . '/' . 'Load.php');
 209		clearstatcache();
 210	}
 211}
 212
 213// MD5 Encryption.
 214if (!function_exists('md5_hmac'))
 215{
 216	function md5_hmac($data, $key)
 217	{
 218		if (strlen($key) > 64)
 219			$key = pack('H*', md5($key));
 220		$key = str_pad($key, 64, chr(0x00));
 221
 222		$k_ipad = $key ^ str_repeat(chr(0x36), 64);
 223		$k_opad = $key ^ str_repeat(chr(0x5c), 64);
 224
 225		return md5($k_opad . pack('H*', md5($k_ipad . $data)));
 226	}
 227}
 228
 229// http://www.faqs.org/rfcs/rfc959.html
 230if (!class_exists('Ftp_Connection'))
 231{
 232	class Ftp_Connection
 233	{
 234		var $connection = 'no_connection', $error = false, $last_message, $pasv = array();
 235
 236		// Create a new FTP connection...
 237		function ftp_connection($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@yourdomain.org')
 238		{
 239			if ($ftp_server !== null)
 240				$this->connect($ftp_server, $ftp_port, $ftp_user, $ftp_pass);
 241		}
 242
 243		function connect($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = 'ftpclient@yourdomain.org')
 244		{
 245			if (substr($ftp_server, 0, 6) == 'ftp://')
 246				$ftp_server = substr($ftp_server, 6);
 247			elseif (substr($ftp_server, 0, 7) == 'ftps://')
 248				$ftp_server = 'ssl://' . substr($ftp_server, 7);
 249			if (substr($ftp_server, 0, 7) == 'http://')
 250				$ftp_server = substr($ftp_server, 7);
 251			$ftp_server = strtr($ftp_server, array('/' => '', ':' => '', '@' => ''));
 252
 253			// Connect to the FTP server.
 254			$this->connection = @fsockopen($ftp_server, $ftp_port, $err, $err, 5);
 255			if (!$this->connection)
 256			{
 257				$this->error = 'bad_server';
 258				return;
 259			}
 260
 261			// Get the welcome message...
 262			if (!$this->check_response(220))
 263			{
 264				$this->error = 'bad_response';
 265				return;
 266			}
 267
 268			// Send the username, it should ask for a password.
 269			fwrite($this->connection, 'USER ' . $ftp_user . "\r\n");
 270			if (!$this->check_response(331))
 271			{
 272				$this->error = 'bad_username';
 273				return;
 274			}
 275
 276			// Now send the password... and hope it goes okay.
 277			fwrite($this->connection, 'PASS ' . $ftp_pass . "\r\n");
 278			if (!$this->check_response(230))
 279			{
 280				$this->error = 'bad_password';
 281				return;
 282			}
 283		}
 284
 285		function chdir($ftp_path)
 286		{
 287			if (!is_resource($this->connection))
 288				return false;
 289
 290			// No slash on the end, please...
 291			if (substr($ftp_path, -1) == '/' && $ftp_path !== '/')
 292				$ftp_path = substr($ftp_path, 0, -1);
 293
 294			fwrite($this->connection, 'CWD ' . $ftp_path . "\r\n");
 295			if (!$this->check_response(250))
 296			{
 297				$this->error = 'bad_path';
 298				return false;
 299			}
 300
 301			return true;
 302		}
 303
 304		function chmod($ftp_file, $chmod)
 305		{
 306			if (!is_resource($this->connection))
 307				return false;
 308
 309			// Convert the chmod value from octal (0777) to text ("777").
 310			fwrite($this->connection, 'SITE CHMOD ' . decoct($chmod) . ' ' . $ftp_file . "\r\n");
 311			if (!$this->check_response(200))
 312			{
 313				$this->error = 'bad_file';
 314				return false;
 315			}
 316
 317			return true;
 318		}
 319
 320		function unlink($ftp_file)
 321		{
 322			// We are actually connected, right?
 323			if (!is_resource($this->connection))
 324				return false;
 325
 326			// Delete file X.
 327			fwrite($this->connection, 'DELE ' . $ftp_file . "\r\n");
 328			if (!$this->check_response(250))
 329			{
 330				fwrite($this->connection, 'RMD ' . $ftp_file . "\r\n");
 331
 332				// Still no love?
 333				if (!$this->check_response(250))
 334				{
 335					$this->error = 'bad_file';
 336					return false;
 337				}
 338			}
 339
 340			return true;
 341		}
 342
 343		function check_response($desired)
 344		{
 345			// Wait for a response that isn't continued with -, but don't wait too long.
 346			$time = time();
 347			do
 348				$this->last_message = fgets($this->connection, 1024);
 349			while (substr($this->last_message, 3, 1) != ' ' && time() - $time < 5);
 350
 351			// Was the desired response returned?
 352			return is_array($desired) ? in_array(substr($this->last_message, 0, 3), $desired) : substr($this->last_message, 0, 3) == $desired;
 353		}
 354
 355		function passive()
 356		{
 357			// We can't create a passive data connection without a primary one first being there.
 358			if (!is_resource($this->connection))
 359				return false;
 360
 361			// Request a passive connection - this means, we'll talk to you, you don't talk to us.
 362			@fwrite($this->connection, 'PASV' . "\r\n");
 363			$time = time();
 364			do
 365				$response = fgets($this->connection, 1024);
 366			while (substr($response, 3, 1) != ' ' && time() - $time < 5);
 367
 368			// If it's not 227, we weren't given an IP and port, which means it failed.
 369			if (substr($response, 0, 4) != '227 ')
 370			{
 371				$this->error = 'bad_response';
 372				return false;
 373			}
 374
 375			// Snatch the IP and port information, or die horribly trying...
 376			if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $response, $match) == 0)
 377			{
 378				$this->error = 'bad_response';
 379				return false;
 380			}
 381
 382			// This is pretty simple - store it for later use ;).
 383			$this->pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]);
 384
 385			return true;
 386		}
 387
 388		function create_file($ftp_file)
 389		{
 390			// First, we have to be connected... very important.
 391			if (!is_resource($this->connection))
 392				return false;
 393
 394			// I'd like one passive mode, please!
 395			if (!$this->passive())
 396				return false;
 397
 398			// Seems logical enough, so far...
 399			fwrite($this->connection, 'STOR ' . $ftp_file . "\r\n");
 400
 401			// Okay, now we connect to the data port.  If it doesn't work out, it's probably "file already exists", etc.
 402			$fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
 403			if (!$fp || !$this->check_response(150))
 404			{
 405				$this->error = 'bad_file';
 406				@fclose($fp);
 407				return false;
 408			}
 409
 410			// This may look strange, but we're just closing it to indicate a zero-byte upload.
 411			fclose($fp);
 412			if (!$this->check_response(226))
 413			{
 414				$this->error = 'bad_response';
 415				return false;
 416			}
 417
 418			return true;
 419		}
 420
 421		function list_dir($ftp_path = '', $search = false)
 422		{
 423			// Are we even connected...?
 424			if (!is_resource($this->connection))
 425				return false;
 426
 427			// Passive... non-agressive...
 428			if (!$this->passive())
 429				return false;
 430
 431			// Get the listing!
 432			fwrite($this->connection, 'LIST -1' . ($search ? 'R' : '') . ($ftp_path == '' ? '' : ' ' . $ftp_path) . "\r\n");
 433
 434			// Connect, assuming we've got a connection.
 435			$fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5);
 436			if (!$fp || !$this->check_response(array(150, 125)))
 437			{
 438				$this->error = 'bad_response';
 439				@fclose($fp);
 440				return false;
 441			}
 442
 443			// Read in the file listing.
 444			$data = '';
 445			while (!feof($fp))
 446				$data .= fread($fp, 4096);
 447			fclose($fp);
 448
 449			// Everything go okay?
 450			if (!$this->check_response(226))
 451			{
 452				$this->error = 'bad_response';
 453				return false;
 454			}
 455
 456			return $data;
 457		}
 458
 459		function locate($file, $listing = null)
 460		{
 461			if ($listing === null)
 462				$listing = $this->list_dir('', true);
 463			$listing = explode("\n", $listing);
 464
 465			@fwrite($this->connection, 'PWD' . "\r\n");
 466			$time = time();
 467			do
 468				$response = fgets($this->connection, 1024);
 469			while (substr($response, 3, 1) != ' ' && time() - $time < 5);
 470
 471			// Check for 257!
 472			if (preg_match('~^257 "(.+?)" ~', $response, $match) != 0)
 473				$current_dir = strtr($match[1], array('""' => '"'));
 474			else
 475				$current_dir = '';
 476
 477			for ($i = 0, $n = count($listing); $i < $n; $i++)
 478			{
 479				if (trim($listing[$i]) == '' && isset($listing[$i + 1]))
 480				{
 481					$current_dir = substr(trim($listing[++$i]), 0, -1);
 482					$i++;
 483				}
 484
 485				// Okay, this file's name is:
 486				$listing[$i] = $current_dir . '/' . trim(strlen($listing[$i]) > 30 ? strrchr($listing[$i], ' ') : $listing[$i]);
 487
 488				if (substr($file, 0, 1) == '*' && substr($listing[$i], -(strlen($file) - 1)) == substr($file, 1))
 489					return $listing[$i];
 490				if (substr($file, -1) == '*' && substr($listing[$i], 0, strlen($file) - 1) == substr($file, 0, -1))
 491					return $listing[$i];
 492				if (basename($listing[$i]) == $file || $listing[$i] == $file)
 493					return $listing[$i];
 494			}
 495
 496			return false;
 497		}
 498
 499		function create_dir($ftp_dir)
 500		{
 501			// We must be connected to the server to do something.
 502			if (!is_resource($this->connection))
 503				return false;
 504
 505			// Make this new beautiful directory!
 506			fwrite($this->connection, 'MKD ' . $ftp_dir . "\r\n");
 507			if (!$this->check_response(257))
 508			{
 509				$this->error = 'bad_file';
 510				return false;
 511			}
 512
 513			return true;
 514		}
 515
 516		function detect_path($filesystem_path, $lookup_file = null)
 517		{
 518			$username = '';
 519
 520			if (isset($_SERVER['DOCUMENT_ROOT']))
 521			{
 522				if (preg_match('~^/home[2]?/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match))
 523				{
 524					$username = $match[1];
 525
 526					$path = strtr($_SERVER['DOCUMENT_ROOT'], array('/home/' . $match[1] . '/' => '', '/home2/' . $match[1] . '/' => ''));
 527
 528					if (substr($path, -1) == '/')
 529						$path = substr($path, 0, -1);
 530
 531					if (strlen(dirname($_SERVER['PHP_SELF'])) > 1)
 532						$path .= dirname($_SERVER['PHP_SELF']);
 533				}
 534				elseif (substr($filesystem_path, 0, 9) == '/var/www/')
 535					$path = substr($filesystem_path, 8);
 536				else
 537					$path = strtr(strtr($filesystem_path, array('\\' => '/')), array($_SERVER['DOCUMENT_ROOT'] => ''));
 538			}
 539			else
 540				$path = '';
 541
 542			if (is_resource($this->connection) && $this->list_dir($path) == '')
 543			{
 544				$data = $this->list_dir('', true);
 545
 546				if ($lookup_file === null)
 547					$lookup_file = $_SERVER['PHP_SELF'];
 548
 549				$found_path = dirname($this->locate('*' . basename(dirname($lookup_file)) . '/' . basename($lookup_file), $data));
 550				if ($found_path == false)
 551					$found_path = dirname($this->locate(basename($lookup_file)));
 552				if ($found_path != false)
 553					$path = $found_path;
 554			}
 555			elseif (is_resource($this->connection))
 556				$found_path = true;
 557
 558			return array($username, $path, isset($found_path));
 559		}
 560
 561		function close()
 562		{
 563			// Goodbye!
 564			fwrite($this->connection, 'QUIT' . "\r\n");
 565			fclose($this->connection);
 566
 567			return true;
 568		}
 569	}
 570}
 571
 572// Don't do security check if on Yabbse
 573if (!isset($modSettings['elkVersion']))
 574	$disable_security = true;
 575
 576// Does this exist?
 577if (isset($modSettings['elkVersion']))
 578{
 579	$request = $smcFunc['db_query']('', '
 580		SELECT variable, value
 581		FROM {db_prefix}themes
 582		WHERE id_theme = {int:id_theme}
 583			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
 584		array(
 585			'id_theme' => 1,
 586			'theme_url' => 'theme_url',
 587			'theme_dir' => 'theme_dir',
 588			'images_url' => 'images_url',
 589			'db_error_skip' => true,
 590		)
 591	);
 592	while ($row = $smcFunc['db_fetch_assoc']($request))
 593		$modSettings[$row['variable']] = $row['value'];
 594	$smcFunc['db_free_result']($request);
 595}
 596
 597if (!isset($modSettings['theme_url']))
 598{
 599	$modSettings['theme_dir'] = BOARDDIR . '/themes/default';
 600	$modSettings['theme_url'] = 'themes/default';
 601	$modSettings['images_url'] = 'themes/default/images';
 602}
 603if (!isset($settings['default_theme_url']))
 604	$settings['default_theme_url'] = $modSettings['theme_url'];
 605if (!isset($settings['default_theme_dir']))
 606	$settings['default_theme_dir'] = $modSettings['theme_dir'];
 607
 608$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
 609// Default title...
 610$upcontext['page_title'] = isset($modSettings['elkVersion']) ? 'Updating Your Elkarte Install!' : isset($modSettings['smfVersion']) ? 'Upgrading from SMF!' : 'Upgrading from YaBB SE!';
 611
 612$upcontext['right_to_left'] = isset($txt['lang_rtl']) ? $txt['lang_rtl'] : false;
 613
 614// Have we got log data - if so use it (It will be clean!)
 615if (isset($_GET['data']))
 616{
 617	$upcontext['upgrade_status'] = unserialize(base64_decode($_GET['data']));
 618	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
 619	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
 620	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
 621	$is_debug = $upcontext['upgrade_status']['debug'];
 622	$support_js = $upcontext['upgrade_status']['js'];
 623
 624	// Load the language.
 625	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
 626		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
 627}
 628// Set the defaults.
 629else
 630{
 631	$upcontext['current_step'] = 0;
 632	$upcontext['rid'] = mt_rand(0, 5000);
 633	$upcontext['upgrade_status'] = array(
 634		'curstep' => 0,
 635		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
 636		'rid' => $upcontext['rid'],
 637		'pass' => 0,
 638		'debug' => 0,
 639		'js' => 0,
 640	);
 641	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
 642}
 643
 644// If this isn't the first stage see whether they are logging in and resuming.
 645if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
 646	checkLogin();
 647
 648if ($command_line)
 649	cmdStep0();
 650
 651// Don't error if we're using xml.
 652if (isset($_GET['xml']))
 653	$upcontext['return_error'] = true;
 654
 655// Loop through all the steps doing each one as required.
 656$upcontext['overall_percent'] = 0;
 657foreach ($upcontext['steps'] as $num => $step)
 658{
 659	if ($num >= $upcontext['current_step'])
 660	{
 661		// The current weight of this step in terms of overall progress.
 662		$upcontext['step_weight'] = $step[3];
 663		// Make sure we reset the skip button.
 664		$upcontext['skip'] = false;
 665
 666		// We cannot proceed if we're not logged in.
 667		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
 668		{
 669			$upcontext['steps'][0][2]();
 670			break;
 671		}
 672
 673		// Call the step and if it returns false that means pause!
 674		if (function_exists($step[2]) && $step[2]() === false)
 675			break;
 676		elseif (function_exists($step[2]))
 677			$upcontext['current_step']++;
 678	}
 679	$upcontext['overall_percent'] += $step[3];
 680}
 681
 682upgradeExit();
 683
 684// Exit the upgrade script.
 685function upgradeExit($fallThrough = false)
 686{
 687	global $upcontext, $upgradeurl, $command_line;
 688
 689	// Save where we are...
 690	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
 691	{
 692		$upcontext['user']['step'] = $upcontext['current_step'];
 693		$upcontext['user']['substep'] = $_GET['substep'];
 694		$upcontext['user']['updated'] = time();
 695		$upgradeData = base64_encode(serialize($upcontext['user']));
 696		copy(BOARDDIR . '/Settings.php', BOARDDIR . '/Settings_bak.php');
 697		changeSettings(array('upgradeData' => '"' . $upgradeData . '"'));
 698		updateLastError();
 699	}
 700
 701	// Handle the progress of the step, if any.
 702	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
 703	{
 704		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
 705		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
 706	}
 707	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
 708
 709	// We usually dump our templates out.
 710	if (!$fallThrough)
 711	{
 712		// This should not happen my dear... HELP ME DEVELOPERS!!
 713		if (!empty($command_line))
 714		{
 715			if (function_exists('debug_print_backtrace'))
 716				debug_print_backtrace();
 717
 718			echo "\n" . 'Error: Unexpected call to use the ' . (isset($upcontext['sub_template']) ? $upcontext['sub_template'] : '') . ' template. Please copy and paste all the text above and visit the Elkarte Community to tell the Developers that they\'ve made a doh!; they\'ll get you up and running again.';
 719			flush();
 720			die();
 721		}
 722
 723		if (!isset($_GET['xml']))
 724			template_upgrade_above();
 725		else
 726		{
 727			header('Content-Type: text/xml; charset=ISO-8859-1');
 728			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
 729			$upcontext['get_data'] = array();
 730			foreach ($_GET as $k => $v)
 731			{
 732				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
 733				{
 734					$upcontext['get_data'][$k] = $v;
 735				}
 736			}
 737			template_xml_above();
 738		}
 739
 740		// Call the template.
 741		if (isset($upcontext['sub_template']))
 742		{
 743			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
 744			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(serialize($upcontext['upgrade_status']));
 745
 746			// Custom stuff to pass back?
 747			if (!empty($upcontext['query_string']))
 748				$upcontext['form_url'] .= $upcontext['query_string'];
 749
 750			call_user_func('template_' . $upcontext['sub_template']);
 751		}
 752
 753		// Was there an error?
 754		if (!empty($upcontext['forced_error_message']))
 755			echo $upcontext['forced_error_message'];
 756
 757		// Show the footer.
 758		if (!isset($_GET['xml']))
 759			template_upgrade_below();
 760		else
 761			template_xml_below();
 762	}
 763
 764	// Bang - gone!
 765	die();
 766}
 767
 768// Used to direct the user to another location.
 769function redirectLocation($location, $addForm = true)
 770{
 771	global $upgradeurl, $upcontext, $command_line;
 772
 773	// Command line users can't be redirected.
 774	if ($command_line)
 775		upgradeExit(true);
 776
 777	// Are we providing the core info?
 778	if ($addForm)
 779	{
 780		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
 781		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(serialize($upcontext['upgrade_status'])) . $location;
 782	}
 783
 784	while (@ob_end_clean());
 785	header('Location: ' . strtr($location, array('&amp;' => '&')));
 786
 787	// Exit - saving status as we go.
 788	upgradeExit(true);
 789}
 790
 791// Load all essential data and connect to the DB as this is pre SSI.php
 792function loadEssentialData()
 793{
 794	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
 795	global $modSettings, $smcFunc, $upcontext;
 796
 797	// Do the non-SSI stuff...
 798	@set_magic_quotes_runtime(0);
 799	error_reporting(E_ALL);
 800	define('ELKARTE', 1);
 801
 802	// Start the session.
 803	if (@ini_get('session.save_handler') == 'user')
 804		@ini_set('session.save_handler', 'files');
 805	@session_start();
 806
 807	if (empty($smcFunc))
 808		$smcFunc = array();
 809
 810	// Initialize everything...
 811	initialize_inputs();
 812
 813	// Get the database going!
 814	if (empty($db_type))
 815		$db_type = 'mysql';
 816	if (file_exists(SOURCEDIR . '/database/Db-' . $db_type . '.subs.php'))
 817	{
 818		require_once(SOURCEDIR . '/database/Db-' . $db_type . '.subs.php');
 819
 820		// Make the connection...
 821		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
 822
 823		// Oh dear god!!
 824		if ($db_connection === null)
 825			die('Unable to connect to database - please check username and password are correct in Settings.php');
 826
 827		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
 828			$smcFunc['db_query']('', '
 829			SET NAMES ' . $db_character_set,
 830			array(
 831				'db_error_skip' => true,
 832			)
 833		);
 834
 835		// Load the modSettings data...
 836		$request = $smcFunc['db_query']('', '
 837			SELECT variable, value
 838			FROM {db_prefix}settings',
 839			array(
 840				'db_error_skip' => true,
 841			)
 842		);
 843		$modSettings = array();
 844		while ($row = $smcFunc['db_fetch_assoc']($request))
 845			$modSettings[$row['variable']] = $row['value'];
 846		$smcFunc['db_free_result']($request);
 847	}
 848	else
 849	{
 850		return throw_error('Cannot find ' . SOURCEDIR . '/database/Db-' . $db_type . '.subs.php' . '. Please check you have uploaded all source files and have the correct paths set.');
 851	}
 852
 853	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
 854	if (file_exists(SOURCEDIR . '/QueryString.php'))
 855	{
 856		require_once(SOURCEDIR . '/QueryString.php');
 857		cleanRequest();
 858	}
 859
 860	if (!isset($_GET['substep']))
 861		$_GET['substep'] = 0;
 862}
 863
 864function initialize_inputs()
 865{
 866	global $start_time, $upcontext, $db_type;
 867
 868	$start_time = time();
 869
 870	umask(0);
 871
 872	// Fun.  Low PHP version...
 873	if (!isset($_GET))
 874	{
 875		$GLOBALS['_GET']['step'] = 0;
 876		return;
 877	}
 878
 879	ob_start();
 880
 881	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
 882	ignore_user_abort(true);
 883
 884	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
 885	if (isset($_GET['delete']))
 886	{
 887		deleteUpgrader();
 888	}
 889
 890	// Are we calling the backup css file?
 891	if (isset($_GET['infile_css']))
 892	{
 893		header('Content-Type: text/css');
 894		template_css();
 895		exit;
 896	}
 897
 898	// Something is causing this to happen, and it's annoying.  Stop it.
 899	$temp = 'upgrade_php?step';
 900	while (strlen($temp) > 4)
 901	{
 902		if (isset($_GET[$temp]))
 903			unset($_GET[$temp]);
 904		$temp = substr($temp, 1);
 905	}
 906
 907	// Force a step, defaulting to 0.
 908	$_GET['step'] = (int) @$_GET['step'];
 909	$_GET['substep'] = (int) @$_GET['substep'];
 910}
 911
 912// Step 0 - Let's welcome them in and ask them to login!
 913function action_welcomeLogin()
 914{
 915	global $db_prefix, $language, $modSettings, $upgradeurl, $upcontext, $disable_security;
 916	global $smcFunc, $db_type, $databases, $txt;
 917
 918	$upcontext['sub_template'] = 'welcome_message';
 919
 920	// Check for some key files - one template, one language, and a new and an old source file.
 921	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
 922		&& @file_exists(SOURCEDIR . '/QueryString.php')
 923		&& @file_exists(SOURCEDIR . '/database/Db-' . $db_type . '.subs.php')
 924		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
 925
 926	// Need legacy scripts?
 927	if (!isset($modSettings['elkVersion']) || $modSettings['elkVersion'] < 2.1)
 928		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
 929	if (!isset($modSettings['elkVersion']) || $modSettings['elkVersion'] < 2.0)
 930		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
 931	if (!isset($modSettings['elkVersion']) || $modSettings['elkVersion'] < 1.1)
 932		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
 933
 934	if (!$check)
 935		// Don't tell them what files exactly because it's a spot check - just like teachers don't tell which problems they are spot checking, that's dumb.
 936		return throw_error('The upgrader was unable to find some crucial files.<br /><br />Please make sure you uploaded all of the files included in the package, including the themes, sources, and other directories.');
 937
 938	// Do they meet the install requirements?
 939	if (!php_version_check())
 940		return throw_error('Warning!  You do not appear to have a version of PHP installed on your webserver that meets ELKARTE\'s minimum installations requirements.<br /><br />Please ask your host to upgrade.');
 941
 942	if (!db_version_check())
 943		return throw_error('Your ' . $databases[$db_type]['name'] . ' version does not meet the minimum requirements of ELKARTE.<br /><br />Please ask your host to upgrade.');
 944
 945	// Do they have ALTER privileges?
 946	if (!empty($databases[$db_type]['alter_support']) && $smcFunc['db_query']('alter_boards', 'ALTER TABLE {db_prefix}boards ORDER BY id_board', array()) === false)
 947		return throw_error('The ' . $databases[$db_type]['name'] . ' user you have set in Settings.php does not have proper privileges.<br /><br />Please ask your host to give this user the ALTER, CREATE, and DROP privileges.');
 948
 949	// Do a quick version spot check.
 950	$temp = substr(@implode('', @file(BOARDDIR . '/index.php')), 0, 4096);
 951	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
 952	if (empty($match[1]) || $match[1] != CURRENT_VERSION)
 953		return throw_error('The upgrader found some old or outdated files.<br /><br />Please make certain you uploaded the new versions of all the files included in the package.');
 954
 955	// What absolutely needs to be writable?
 956	$writable_files = array(
 957		BOARDDIR . '/Settings.php',
 958		BOARDDIR . '/Settings_bak.php',
 959	);
 960
 961	require_once(SOURCEDIR . '/Security.php');
 962	$upcontext += createToken('login');
 963
 964	// Check the cache directory.
 965	$CACHEDIR_temp = !defined('CACHEDIR') ? BOARDDIR . '/cache' : CACHEDIR;
 966	if (!file_exists($CACHEDIR_temp))
 967		@mkdir($CACHEDIR_temp);
 968	if (!file_exists($CACHEDIR_temp))
 969		return throw_error('The cache directory could not be found.<br /><br />Please make sure you have a directory called &quot;cache&quot; in your forum directory before continuing.');
 970
 971	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['elkVersion']) && !isset($_GET['lang']))
 972		return throw_error('The upgrader was unable to find language files for the language specified in Settings.php.<br />ELKARTE will not work without the primary language files installed.<br /><br />Please either install them, or <a href="' . $upgradeurl . '?step=0;lang=english">use english instead</a>.');
 973	elseif (!isset($_GET['skiplang']))
 974	{
 975		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
 976		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
 977
 978		if (empty($match[1]) || $match[1] != CURRENT_LANG_VERSION)
 979			return throw_error('The upgrader found some old or outdated language files, for the forum default language, ' . $upcontext['language'] . '.<br /><br />Please make certain you uploaded the new versions of all the files included in the package, even the theme and language files for the default theme.<br />&nbsp;&nbsp;&nbsp;[<a href="' . $upgradeurl . '?skiplang">SKIP</a>] [<a href="' . $upgradeurl . '?lang=english">Try English</a>]');
 980	}
 981
 982	// This needs to exist!
 983	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
 984		return throw_error('The upgrader could not find the &quot;Install&quot; language file for the forum default language, ' . $upcontext['language'] . '.<br /><br />Please make certain you uploaded all the files included in the package, even the theme and language files for the default theme.<br />&nbsp;&nbsp;&nbsp;[<a href="' . $upgradeurl . '?lang=english">Try English</a>]');
 985	else
 986		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
 987
 988	if (!makeFilesWritable($writable_files))
 989		return false;
 990
 991	// Check agreement.txt. (it may not exist, in which case BOARDDIR must be writable.)
 992	if (isset($modSettings['agreement']) && (!is_writable(BOARDDIR) || file_exists(BOARDDIR . '/agreement.txt')) && !is_writable(BOARDDIR . '/agreement.txt'))
 993		return throw_error('The upgrader was unable to obtain write access to agreement.txt.<br /><br />If you are using a linux or unix based server, please ensure that the file is chmod\'d to 777, or if it does not exist that the directory this upgrader is in is 777.<br />If your server is running Windows, please ensure that the internet guest account has the proper permissions on it or its folder.');
 994
 995	// Upgrade the agreement.
 996	elseif (isset($modSettings['agreement']))
 997	{
 998		$fp = fopen(BOARDDIR . '/agreement.txt', 'w');
 999		fwrite($fp, $modSettings['agreement']);
1000		fclose($fp);
1001	}
1002
1003	// We're going to check that their board dir setting is right incase they've been moving stuff around.
1004	if (strtr(BOARDDIR, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
1005		$upcontext['warning'] = '
1006			It looks as if your board directory settings <em>might</em> be incorrect. Your board directory is currently set to &quot;' . BOARDDIR . '&quot; but should probably be &quot;' . dirname(__FILE__) . '&quot;. Settings.php currently lists your paths as:<br />
1007			<ul>
1008				<li>Board Directory: ' . BOARDDIR . '</li>
1009				<li>Source Directory: ' . BOARDDIR . '</li>
1010				<li>Cache Directory: ' . $CACHEDIR_temp . '</li>
1011			</ul>
1012			If these seem incorrect please open Settings.php in a text editor before proceeding with this upgrade. If they are incorrect due to you moving your forum to a new location please download and execute the <a href="https://github.com/emanuele45/tools/downloads">Repair Settings</a> tool from the Elkarte website before continuing.';
1013
1014	// Either we're logged in or we're going to present the login.
1015	if (checkLogin())
1016		return true;
1017
1018	return false;
1019}
1020
1021// Step 0.5: Does the login work?
1022function checkLogin()
1023{
1024	global $db_prefix, $language, $modSettings, $upgradeurl, $upcontext, $disable_security;
1025	global $smcFunc, $db_type, $databases, $support_js, $txt;
1026
1027	// Are we trying to login?
1028	if (isset($_POST['contbutt']) && (!empty($_POST['user']) || $disable_security))
1029	{
1030		// If we've disabled security pick a suitable name!
1031		if (empty($_POST['user']))
1032			$_POST['user'] = 'Administrator';
1033
1034		// Before 2.0 these column names were different!
1035		$oldDB = false;
1036		if (empty($db_type) || $db_type == 'mysql')
1037		{
1038			$request = $smcFunc['db_query']('', '
1039				SHOW COLUMNS
1040				FROM {db_prefix}members
1041				LIKE {string:member_name}',
1042				array(
1043					'member_name' => 'memberName',
1044					'db_error_skip' => true,
1045				)
1046			);
1047			if ($smcFunc['db_num_rows']($request) != 0)
1048				$oldDB = true;
1049			$smcFunc['db_free_result']($request);
1050		}
1051
1052		// Get what we believe to be their details.
1053		if (!$disable_security)
1054		{
1055			if ($oldDB)
1056				$request = $smcFunc['db_query']('', '
1057					SELECT id_member, memberName AS member_name, passwd, id_group,
1058					additionalGroups AS additional_groups, lngfile
1059					FROM {db_prefix}members
1060					WHERE memberName = {string:member_name}',
1061					array(
1062						'member_name' => $_POST['user'],
1063						'db_error_skip' => true,
1064					)
1065				);
1066			else
1067				$request = $smcFunc['db_query']('', '
1068					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
1069					FROM {db_prefix}members
1070					WHERE member_name = {string:member_name}',
1071					array(
1072						'member_name' => $_POST['user'],
1073						'db_error_skip' => true,
1074					)
1075				);
1076			if ($smcFunc['db_num_rows']($request) != 0)
1077			{
1078				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
1079
1080				$groups = explode(',', $addGroups);
1081				$groups[] = $id_group;
1082
1083				foreach ($groups as $k => $v)
1084					$groups[$k] = (int) $v;
1085
1086				// Figure out the password using our encryption - if what they typed is right.
1087				if (isset($_REQUEST['hash_passwrd']) && strlen($_REQUEST['hash_passwrd']) == 40)
1088				{
1089					// Challenge passed.
1090					if ($_REQUEST['hash_passwrd'] == sha1($password . $upcontext['rid']))
1091						$sha_passwd = $password;
1092				}
1093				else
1094					$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
1095			}
1096			else
1097				$upcontext['username_incorrect'] = true;
1098			$smcFunc['db_free_result']($request);
1099		}
1100		$upcontext['username'] = $_POST['user'];
1101
1102		// Track whether javascript works!
1103		if (!empty($_POST['js_works']))
1104		{
1105			$upcontext['upgrade_status']['js'] = 1;
1106			$support_js = 1;
1107		}
1108		else
1109			$support_js = 0;
1110
1111		// Note down the version we are coming from.
1112		if (!empty($modSettings['elkVersion']) && empty($upcontext['user']['version']))
1113			$upcontext['user']['version'] = $modSettings['elkVersion'];
1114
1115		// Didn't get anywhere?
1116		if ((empty($sha_passwd) || $password != $sha_passwd) && empty($upcontext['username_incorrect']) && !$disable_security)
1117		{
1118			// MD5?
1119			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
1120			if ($md5pass != $password)
1121			{
1122				$upcontext['password_failed'] = true;
1123				// Disable the hashing this time.
1124				$upcontext['disable_login_hashing'] = true;
1125			}
1126		}
1127
1128		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
1129		{
1130			// Set the password.
1131			if (!$disable_security)
1132			{
1133				// Do we actually have permission?
1134				if (!in_array(1, $groups))
1135				{
1136					$request = $smcFunc['db_query']('', '
1137						SELECT permission
1138						FROM {db_prefix}permissions
1139						WHERE id_group IN ({array_int:groups})
1140							AND permission = {string:admin_forum}',
1141						array(
1142							'groups' => $groups,
1143							'admin_forum' => 'admin_forum',
1144							'db_error_skip' => true,
1145						)
1146					);
1147					if ($smcFunc['db_num_rows']($request) == 0)
1148						return throw_error('You need to be an admin to perform an upgrade!');
1149					$smcFunc['db_free_result']($request);
1150				}
1151
1152				$upcontext['user']['id'] = $id_member;
1153				$upcontext['user']['name'] = $name;
1154			}
1155			else
1156			{
1157				$upcontext['user']['id'] = 1;
1158				$upcontext['user']['name'] = 'Administrator';
1159			}
1160			$upcontext['user']['pass'] = mt_rand(0,60000);
1161			// This basically is used to match the GET variables to Settings.php.
1162			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
1163
1164			// Set the language to that of the user?
1165			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
1166			{
1167				$user_language = basename($user_language, '.lng');
1168				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
1169				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
1170
1171				if (empty($match[1]) || $match[1] != CURRENT_LANG_VERSION)
1172					$upcontext['upgrade_options_warning'] = 'The language files for your selected language, ' . $user_language . ', have not been updated to the latest version. Upgrade will continue with the forum default, ' . $upcontext['language'] . '.';
1173				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
1174					$upcontext['upgrade_options_warning'] = 'The language files for your selected language, ' . $user_language . ', have not been uploaded/updated as the &quot;Install&quot; language file is missing. Upgrade will continue with the forum default, ' . $upcontext['language'] . '.';
1175				else
1176				{
1177					// Set this as the new language.
1178					$upcontext['language'] = $user_language;
1179					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
1180
1181					// Include the file.
1182					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
1183				}
1184			}
1185
1186			// If we're resuming set the step and substep to be correct.
1187			if (isset($_POST['cont']))
1188			{
1189				$upcontext['current_step'] = $upcontext['user']['step'];
1190				$_GET['substep'] = $upcontext['user']['substep'];
1191			}
1192
1193			return true;
1194		}
1195	}
1196
1197	return false;
1198}
1199
1200// Step 1: Do the maintenance and backup.
1201function action_upgradeOptions()
1202{
1203	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc;
1204	global $boardurl, $maintenance, $mmessage, $upcontext, $db_type;
1205
1206	$upcontext['sub_template'] = 'upgrade_options';
1207	$upcontext['page_title'] = 'Upgrade Options';
1208
1209	// If we've not submitted then we're done.
1210	if (empty($_POST['upcont']))
1211		return false;
1212
1213	// No one opts in so why collect incomplete stats
1214	$smcFunc['db_query']('', '
1215		DELETE FROM {db_prefix}settings
1216		WHERE variable = {string:allow_sm_stats}',
1217		array(
1218			'allow_sm_stats' => false,
1219			'db_error_skip' => true,
1220		)
1221	);
1222
1223	// Emptying the error log?
1224	if (!empty($_POST['empty_error']))
1225		$smcFunc['db_query']('truncate_table', '
1226			TRUNCATE {db_prefix}log_errors',
1227			array(
1228			)
1229		);
1230
1231	$changes = array();
1232
1233	// If we're overriding the language follow it through.
1234	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1235		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1236
1237	if (!empty($_POST['maint']))
1238	{
1239		$changes['maintenance'] = '2';
1240		// Remember what it was...
1241		$upcontext['user']['main'] = $maintenance;
1242
1243		if (!empty($_POST['maintitle']))
1244		{
1245			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1246			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1247		}
1248		else
1249		{
1250			$changes['mtitle'] = '\'Upgrading the forum...\'';
1251			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1252		}
1253	}
1254
1255	if ($command_line)
1256		echo ' * Updating Settings.php...';
1257
1258	// Backup the current one first.
1259	copy(BOARDDIR . '/Settings.php', BOARDDIR . '/Settings_bak.php');
1260
1261	// Fix some old paths.
1262	if (substr(BOARDDIR, 0, 1) == '.')
1263		$changes['boarddir'] = '\'' . fixRelativePath(BOARDDIR) . '\'';
1264
1265	if (substr(SOURCEDIR, 0, 1) == '.')
1266		$changes['sourcedir'] = '\'' . fixRelativePath(SOURCEDIR) . '\'';
1267
1268	if (!defined('CACHEDIR') || substr(CACHEDIR, 0, 1) == '.')
1269		$changes['cachedir'] = '\'' . fixRelativePath(BOARDDIR) . '/cache\'';
1270
1271	// Not had the database type added before?
1272	if (empty($db_type))
1273		$changes['db_type'] = 'mysql';
1274
1275	// @todo Maybe change the cookie name if going to 1.1, too?
1276
1277	// Update Settings.php with the new settings.
1278	changeSettings($changes);
1279
1280	if ($command_line)
1281		echo ' Successful.' . "\n";
1282
1283	// Are we doing debug?
1284	if (isset($_POST['debug']))
1285	{
1286		$upcontext['upgrade_status']['debug'] = true;
1287		$is_debug = true;
1288	}
1289
1290	// If we're not backing up then jump one.
1291	if (empty($_POST['backup']))
1292		$upcontext['current_step']++;
1293
1294	// If we've got here then let's proceed to the next step!
1295	return true;
1296}
1297
1298// Backup the database - why not...
1299function action_backupDatabase()
1300{
1301	global $upcontext, $db_prefix, $command_line, $is_debug, $support_js, $file_steps, $smcFunc;
1302
1303	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1304	$upcontext['page_title'] = 'Backup Database';
1305
1306	// Done it already - js wise?
1307	if (!empty($_POST['backup_done']))
1308		return true;
1309
1310	// Some useful stuff here.
1311	db_extend();
1312
1313	// Get all the table names.
1314	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1315	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1316	$tables = $smcFunc['db_list_tables']($db, $filter);
1317
1318	$table_names = array();
1319	foreach ($tables as $table)
1320		if (substr($table, 0, 7) !== 'backup_')
1321			$table_names[] = $table;
1322
1323	$upcontext['table_count'] = count($table_names);
1324	$upcontext['cur_table_num'] = $_GET['substep'];
1325	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1326	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1327	// For non-java auto submit...
1328	$file_steps = $upcontext['table_count'];
1329
1330	// What ones have we already done?
1331	foreach ($table_names as $id => $table)
1332		if ($id < $_GET['substep'])
1333			$upcontext['previous_tables'][] = $table;
1334
1335	if ($command_line)
1336		echo 'Backing Up Tables.';
1337
1338	// If we don't support javascript we backup here.
1339	if (!$support_js || isset($_GET['xml']))
1340	{
1341		// Backup each table!
1342		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1343		{
1344			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1345			$upcontext['cur_table_num'] = $substep + 1;
1346
1347			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1348
1349			// Do we need to pause?
1350			nextSubstep($substep);
1351
1352			backupTable($table_names[$substep]);
1353
1354			// If this is XML to keep it nice for the user do one table at a time anyway!
1355			if (isset($_GET['xml']))
1356				return upgradeExit();
1357		}
1358
1359		if ($is_debug && $command_line)
1360		{
1361			echo "\n" . ' Successful.\'' . "\n";
1362			flush();
1363		}
1364		$upcontext['step_progress'] = 100;
1365
1366		$_GET['substep'] = 0;
1367		// Make sure we move on!
1368		return true;
1369	}
1370
1371	// Either way next place to post will be database changes!
1372	$_GET['substep'] = 0;
1373	return false;
1374}
1375
1376// Backup one table...
1377function backupTable($table)
1378{
1379	global $is_debug, $command_line, $db_prefix, $smcFunc;
1380
1381	if ($is_debug && $command_line)
1382	{
1383		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1384		flush();
1385	}
1386
1387	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1388
1389	if ($is_debug && $command_line)
1390		echo ' done.';
1391}
1392
1393// Step 2: Everything.
1394function action_databaseChanges()
1395{
1396	global $db_prefix, $modSettings, $command_line, $smcFunc;
1397	global $language, $boardurl, $upcontext, $support_js, $db_type;
1398
1399	// Have we just completed this?
1400	if (!empty($_POST['database_done']))
1401		return true;
1402
1403	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1404	$upcontext['page_title'] = 'Database Changes';
1405
1406	// All possible files.
1407	// Name, version, insert_on_complete.
1408	$files = array(
1409		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1410		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1411		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1412		// array('upgrade_2-1_' . $db_type . '.sql', '3.0', '3.0 dev0'),
1413		array('upgrade_dia_1-0_' . $db_type . '.sql', '1.1', CURRENT_VERSION),
1414	);
1415
1416	// How many files are there in total?
1417	if (isset($_GET['filecount']))
1418		$upcontext['file_count'] = (int) $_GET['filecount'];
1419	else
1420	{
1421		$upcontext['file_count'] = 0;
1422		foreach ($files as $file)
1423		{
1424			if (!isset($modSettings['elkVersion']) && isset($modSettings['smfVersion']) && strpos($file[0], '_dia_') === false && $modSettings['smfVersion'] < $file[1])
1425				$upcontext['file_count']++;
1426			elseif (!isset($modSettings['elkVersion']) || (strpos($file[0], '_dia_') !== false && $modSettings['elkVersion'] < $file[1]))
1427				$upcontext['file_count']++;
1428		}
1429	}
1430
1431	// Do each file!
1432	$did_not_do = count($files) - $upcontext['file_count'];
1433	$upcontext['step_progress'] = 0;
1434	$upcontext['cur_file_num'] = 0;
1435	foreach ($files as $file)
1436	{
1437		if ($did_not_do)
1438			$did_not_do--;
1439		else
1440		{
1441			$upcontext['cur_file_num']++;
1442			$upcontext['cur_file_name'] = $file[0];
1443			// Do we actually need to do this still?
1444			if (!isset($modSettings['elkVersion']) || $modSettings['elkVersion'] < $file[1])
1445			{
1446				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1447				if ($nextFile)
1448				{
1449					// Only update the version of this if complete.
1450					$smcFunc['db_insert']('replace',
1451						$db_prefix . 'settings',
1452						array('variable' => 'string', 'value' => 'string'),
1453						array('elkVersion', $file[2]),
1454						array('variable')
1455					);
1456
1457					$modSettings['elkVersion'] = $file[2];
1458				}
1459
1460				// If this is XML we only do this stuff once.
1461				if (isset($_GET['xml']))
1462				{
1463					// Flag to move on to the next.
1464					$upcontext['completed_step'] = true;
1465					// Did we complete the whole file?
1466					if ($nextFile)
1467						$upcontext['current_debug_item_num'] = -1;
1468					return upgradeExit();
1469				}
1470				elseif ($support_js)
1471					break;
1472			}
1473			// Set the progress bar to be right as if we had - even if we hadn't...
1474			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1475		}
1476	}
1477
1478	$_GET['substep'] = 0;
1479	// So the template knows we're done.
1480	if (!$support_js)
1481	{
1482		$upcontext['changes_complete'] = true;
1483
1484		// If this is the command line we can't do any more.
1485		if ($command_line)
1486			return action_deleteUpgrade();
1487
1488		return true;
1489	}
1490	return false;
1491}
1492
1493// Clean up any mods installed...
1494function action_cleanupMods()
1495{
1496	global $db_prefix, $modSettings, $upcontext, $settings, $smcFunc, $command_line;
1497
1498	// Sorry. Not supported for command line users.
1499	if ($command_line)
1500		return true;
1501
1502	// Skipping first?
1503	if (!empty($_POST['skip']))
1504	{
1505		unset($_POST['skip']);
1506		return true;
1507	}
1508
1509	// If we get here withOUT SSI we need to redirect to ensure we get it!
1510	if (!isset($_GET['ssi']) || !function_exists('mktree'))
1511		redirectLocation('&ssi=1');
1512
1513	$upcontext['sub_template'] = 'clean_mods';
1514	$upcontext['page_title'] = 'Cleanup Modifications';
1515
1516	// This can be skipped.
1517	$upcontext['skip'] = true;
1518
1519	// If we're on the second redirect continue...
1520	if (isset($_POST['cleandone2']))
1521		return true;
1522
1523	// Do we already know about some writable files?
1524	if (isset($_POST['writable_files']))
1525	{
1526		$writable_files = unserialize(base64_decode($_POST['writable_files']));
1527		if (!makeFilesWritable($writable_files))
1528		{
1529			// What have we left?
1530			$upcontext['writable_files'] = $writable_files;
1531			return false;
1532		}
1533	}
1534
1535	// Load all theme paths....
1536	$request = $smcFunc['db_query']('', '
1537		SELECT id_theme, variable, value
1538		FROM {db_prefix}themes
1539		WHERE id_member = {int:id_member}
1540			AND variable IN ({string:theme_dir}, {string:images_url})',
1541		array(
1542			'id_member' => 0,
1543			'theme_dir' => 'theme_dir',
1544			'images_url' => 'images_url',
1545			'db_error_skip' => true,
1546		)
1547	);
1548	$theme_paths = array();
1549	while ($row = $smcFunc['db_fetch_assoc']($request))
1550	{
1551		if ($row['id_theme'] == 1)
1552			$settings['default_' . $row['variable']] = $row['value'];
1553		elseif ($row['variable'] == 'theme_dir')
1554			$theme_paths[$row['id_theme']][$row['variable']] = $row['value'];
1555	}
1556	$smcFunc['db_free_result']($request);
1557
1558	// Are there are mods installed that may need uninstalling?
1559	$request = $smcFunc['db_query']('', '
1560		SELECT id_install, filename, name, themes_installed, version
1561		FROM {db_prefix}log_packages
1562		WHERE install_state = {int:installed}
1563		ORDER BY time_installed DESC',
1564		array(
1565			'installed' => 1,
1566			'db_error_skip' => true,
1567		)
1568	);
1569	$upcontext['packages'] = array();
1570	while ($row = $smcFunc['db_fetch_assoc']($request))
1571	{
1572		// Work out the status.
1573		if (!file_exists(BOARDDIR . '/packages/' . $row['filename']))
1574		{
1575			$status = 'Missing';
1576			$status_color = 'red';
1577			$result = 'Removed';
1578		}
1579		else
1580		{
1581			$status = 'Installed';
1582			$status_color = 'green';
1583			$result = 'No Action Needed';
1584		}
1585
1586		$upcontext['packages'][$row['id_install']] = array(
1587			'id' => $row['id_install'],
1588			'themes' => explode(',', $row['themes_installed']),
1589			'name' => $row['name'],
1590			'filename' => $row['filename'],
1591			'missing_file' => file_exists(BOARDDIR . '/packages/' . $row['filename']) ? 0 : 1,
1592			'files' => array(),
1593			'file_count' => 0,
1594			'status' => $status,
1595			'result' => $result,
1596			'color' => $status_color,
1597			'version' => $row['version'],
1598			'needs_removing' => false,
1599		);
1600	}
1601	$smcFunc['db_free_result']($request);
1602
1603	// Don't carry on if there are none.
1604	if (empty($upcontext['packages']))
1605		return true;
1606
1607	// Setup some basics.
1608	if (!empty($upcontext['user']['version']))
1609		$_SESSION['version_emulate'] = $upcontext['user']['version'];
1610
1611	// Before we get started, don't report notice errors.
1612	$oldErrorReporting = error_reporting(E_ALL ^ E_NOTICE);
1613
1614	if (!mktree(BOARDDIR . '/packages/temp', 0755))
1615	{
1616		deltree(BOARDDIR . '/packages/temp', false);
1617		if (!mktree(BOARDDIR . '/packages/temp', 0777))
1618		{
1619			deltree(BOARDDIR . '/packages/temp', false);
1620			// @todo Error here - plus chmod!
1621		}
1622	}
1623
1624	// Anything which reinstalled should not have its entry removed.
1625	$reinstall_worked = array();
1626
1627	// We're gonna be doing some removin'
1628	$test = isset($_POST['cleandone']) ? false : true;
1629	foreach ($upcontext['packages'] as $id => $package)
1630	{
1631		// Can't do anything about this....
1632		if ($package['missing_file'])
1633			continue;
1634
1635		// Not testing *and* this wasn't checked?
1636		if (!$test && (!isset($_POST['remove']) || !isset($_POST['remove'][$id])))
1637			continue;
1638
1639		// What are the themes this was installed into?
1640		$cur_theme_paths = array();
1641		foreach ($theme_paths as $tid => $data)
1642			if ($tid != 1 && in_array($tid, $package['themes']))
1643				$cur_theme_paths[$tid] = $data;
1644
1645		// Get the modifications data if applicable.
1646		$filename = $package['filename'];
1647		$packageInfo = getPackageInfo($filename);
1648		if (!is_array($packageInfo))
1649			continue;
1650
1651		$info = parsePackageInfo($packageInfo['xml'], $test, 'uninstall');
1652		// Also get the reinstall details...
1653		if (isset($_POST['remove']))
1654			$infoInstall = parsePackageInfo($packageInfo['xml'], true);
1655
1656		if (is_file(BOARDDIR . '/packages/' . $filename))
1657			read_tgz_file(BOARDDIR . '/packages/' . $filename, BOARDDIR . '/packages/temp');
1658		else
1659			copytree(BOARDDIR . '/packages/' . $filename, BOARDDIR . '/packages/temp');
1660
1661		// Work out how we uninstall...
1662		$files = array();
1663		foreach ($info as $change)
1664		{
1665			// Work out two things:
1666			// 1) Whether it's installed at the moment - and if so whether its fully installed, and:
1667			// 2) Whether it could be installed on the new version.
1668			if ($change['type'] == 'modification')
1669			{
1670				$contents = @file_get_contents(BOARDDIR . '/packages/temp/' . $upcontext['base_path'] . $change['filename']);
1671				if ($change['boardmod'])
1672					$results = parseBoardMod($contents, $test, $change['reverse'], $cur_theme_paths);
1673				else
1674					$results = parseModification($contents, $test, $change['reverse'], $cur_theme_paths);
1675
1676				foreach ($results as $action)
1677				{
1678					// Something we can remove? Probably means it existed!
1679					if (($action['type'] == 'replace' || $action['type'] == 'append' || (!empty($action['filename']) && $action['type'] == 'failure')) && !in_array($action['filename'], $files))
1680						$files[] = $action['filename'];
1681					if ($action['type'] == 'failure')
1682					{
1683						$upcontext['packages'][$id]['needs_removing'] = true;
1684						$upcontext['packages'][$id]['status'] = 'Reinstall Required';
1685						$upcontext['packages'][$id]['color'] = '#FD6435';
1686					}
1687				}
1688			}
1689		}
1690
1691		// Store this info for the template as appropriate.
1692		$upcontext['packages'][$id]['files'] = $files;
1693		$upcontext['packages'][$id]['file_count'] = count($files);
1694
1695		// If we've done something save the changes!
1696		if (!$test)
1697			package_flush_cache();
1698
1699		// Are we attempting to reinstall this thing?
1700		if (isset($_POST['remove']) && !$test && isset($infoInstall))
1701		{
1702			// Need to extract again I'm afraid.
1703			if (is_file(BOARDDIR . '/packages/' . $filename))
1704				read_tgz_file(BOARDDIR . '/packages/' . $filename, BOARDDIR . '/packages/temp');
1705			else
1706				copytree(BOARDDIR . '/packages/' . $filename, BOARDDIR . '/packages/temp');
1707
1708			$errors = false;
1709			$upcontext['packages'][$id]['result'] = 'Removed';
1710			foreach ($infoInstall as $change)
1711			{
1712				if ($change['type'] == 'modification')
1713				{
1714					$contents = @file_get_contents(BOARDDIR . '/packages/temp/' . $upcontext['base_path'] . $change['filename']);
1715					if ($change['boardmod'])
1716						$results = parseBoardMod($contents, true, $change['reverse'], $cur_theme_paths);
1717					else
1718						$results = parseModification($contents, true, $change['reverse'], $cur_theme_paths);
1719
1720					// Are there any errors?
1721					foreach ($results as $action)
1722						if ($action['type'] == 'failure')
1723							$errors = true;
1724				}
1725			}
1726			if (!$errors)
1727			{
1728				$reinstall_worked[] = $id;
1729				$upcontext['packages'][$id]['result'] = 'Reinstalled';
1730				$upcontext['packages'][$id]['color'] = 'green';
1731				foreach ($infoInstall as $change)
1732				{
1733					if ($change['type'] == 'modification')
1734					{
1735						$contents = @file_get_contents(BOARDDIR . '/packages/temp/' . $upcontext['base_path'] . $change['filename']);
1736						if ($change['boardmod'])
1737							$results = parseBoardMod($contents, false, $change['reverse'], $cur_theme_paths);
1738						else
1739							$results = parseModification($contents, false, $change['reverse'], $cur_theme_paths);
1740					}
1741				}
1742
1743				// Save the changes.
1744				package_flush_cache();
1745			}
1746		}
1747	}
1748
1749	// Put errors back on a sec.
1750	error_reporting($oldErrorReporting);
1751
1752	// Check everything is writable.
1753	if ($test && !empty($upcontext['packages']))
1754	{
1755		$writable_files = array();
1756		foreach ($upcontext['packages'] as $package)
1757		{
1758			if (!empty($package['files']))
1759				foreach ($package['files'] as $file)
1760					$writable_files[] = $file;
1761		}
1762
1763		if (!empty($writable_files))
1764		{
1765			$writable_files = array_unique($writable_files);
1766			$upcontext['writable_files'] = $writable_files;
1767
1768			if (!makeFilesWritable($writable_files))
1769				return false;
1770		}
1771	}
1772
1773	if (file_exists(BOARDDIR . '/packages/temp'))
1774		deltree(BOARDDIR . '/packages/temp');
1775
1776	// Removing/Reinstalling any packages?
1777	if (isset($_POST['remove']))
1778	{
1779		$deletes = array();
1780		foreach ($_POST['remove'] as $id => $dummy)
1781		{
1782			if (!in_array((int) $id, $reinstall_worked))
1783				$deletes[] = (int) $id;
1784		}
1785
1786		if (!empty($deletes))
1787			upgrade_query( '
1788				UPDATE ' . $db_prefix . 'log_packages
1789				SET install_state = 0
1790				WHERE id_install IN (' . implode(',', $deletes) . ')');
1791
1792		// Ensure we don't lose our changes!
1793		package_put_contents(BOARDDIR . '/packages/installed.list', time());
1794
1795		$upcontext['sub_template'] = 'cleanup_done';
1796		return false;
1797	}
1798	else
1799	{
1800		$allgood = true;
1801		// Is there actually anything that needs our attention?
1802		foreach ($upcontext['packages'] as $package)
1803			if ($package['color'] != 'green')
1804				$allgood = false;
1805
1806		if ($allgood)
1807			return true;
1808	}
1809
1810	$_GET['substep'] = 0;
1811	return isset($_POST['cleandone']) ? true : false;
1812}
1813
1814
1815// Delete the damn thing!
1816function action_deleteUpgrade()
1817{
1818	global $command_line, $language, $upcontext, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
1819
1820	// Now it's nice to have some of the basic source files.
1821	if (!isset($_GET['ssi']) && !$command_line)
1822		redirectLocation('&ssi=1');
1823
1824	$upcontext['sub_template'] = 'upgrade_complete';
1825	$upcontext['page_title'] = 'Upgrade Complete';
1826
1827	$endl = $command_line ? "\n" : '<br />' . "\n";
1828
1829	$changes = array(
1830		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1831		'db_error_send' => '1',
1832		'upgradeData' => '#remove#',
1833	);
1834
1835	// Are we in maintenance mode?
1836	if (isset($upcontext['user']['main']))
1837	{
1838		if ($command_line)
1839			echo ' * ';
1840		$upcontext['removed_maintenance'] = true;
1841		$changes['maintenance'] = $upcontext['user']['main'];
1842	}
1843	// Otherwise if somehow we are in 2 let's go to 1.
1844	elseif (!empty($maintenance) && $maintenance == 2)
1845		$changes['maintenance'] = 1;
1846
1847	// Wipe this out...
1848	$upcontext['user'] = array();
1849
1850	// Make a backup of Settings.php first as otherwise earlier changes are lost.
1851	copy(BOARDDIR . '/Settings.php', BOARDDIR . '/Settings_bak.php');
1852	changeSettings($changes);
1853
1854	// Clean any old cache files away.
1855	clean_cache();
1856
1857	// Can we delete the file?
1858	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1859
1860	// Now is the perfect time to fetch the SM files.
1861	if ($command_line)
1862		cli_scheduled_fetchFiles();
1863	else
1864	{
1865		require_once(SOURCEDIR . '/ScheduledTasks.php');
1866		$forum_version = CURRENT_VERSION;  // The variable is usually defined in index.php so lets just use the constant to do it for us.
1867		scheduled_fetchFiles(); // Now go get those files!
1868	}
1869
1870	// Log what we've done.
1871	if (empty($user_info['id']))
1872		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1873
1874	// Log the action manually, so CLI still works.
1875	$smcFunc['db_insert']('',
1876		'{db_prefix}log_actions',
1877		array(
1878			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string',
1879			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1880		),
1881		array(
1882			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1883			0, 0, 0, serialize(array('version' => $forum_version, 'member' => $user_info['id'])),
1884		),
1885		array('id_action')
1886	);
1887	$user_info['id'] = 0;
1888
1889	// Save the current database version.
1890	$server_version = $smcFunc['db_server_info']();
1891	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1892		updateSettings(array('db_mysql_group_by_fix' => '1'));
1893
1894	if ($command_line)
1895	{
1896		echo $endl;
1897		echo 'Upgrade Complete!', $endl;
1898		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1899		exit;
1900	}
1901
1902	// Make sure it says we're done.
1903	$upcontext['overall_percent'] = 100;
1904	if (isset($upcontext['step_progress']))
1905		unset($upcontext['step_progress']);
1906
1907	$_GET['substep'] = 0;
1908	return false;
1909}
1910
1911// Just like the built in one, but setup for CLI to not use themes.
1912function cli_scheduled_fetchFiles()
1913{
1914	global $txt, $language, $settings, $forum_version, $modSettings, $smcFunc;
1915
1916	if (empty($modSettings['time_format']))
1917		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1918
1919	// What files do we want to get
1920	$request = $smcFunc['db_query']('', '
1921		SELECT id_file, filename, path, parameters
1922		FROM {db_prefix}admin_info_files',
1923		array(
1924		)
1925	);
1926
1927	$js_files = array();
1928	while ($row = $smcFunc['db_fetch_assoc']($request))
1929	{
1930		$js_files[$row['id_file']] = array(
1931			'filename' => $row['filename'],
1932			'path' => $row['path'],
1933			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1934		);
1935	}
1936	$smcFunc['db_free_result']($request);
1937
1938	// We're gonna need fetch_web_data() to pull this off.
1939	require_once(SUBSDIR . '/Package.subs.php');
1940
1941	foreach ($js_files as $ID_FILE => $file)
1942	{
1943		// Create the url
1944		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'http://www.elkarte.net' : '';
1945		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1946
1947		// Get the file
1948		$file_data = fetch_web_data($url);
1949
1950		// If we got an error - give up - the site might be down.
1951		if ($file_data === false)
1952			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1953
1954		// Save the file to the database.
1955		$smcFunc['db_query']('substring', '
1956			UPDATE {db_prefix}admin_info_files
1957			SET data = SUBSTRING({string:file_data}, 1, 65534)
1958			WHERE id_file = {int:id_file}',
1959			array(
1960				'id_file' => $ID_FILE,
1961				'file_data' => $file_data,
1962			)
1963		);
1964	}
1965	return true;
1966}
1967
1968function convertSettingsToTheme()
1969{
1970	global $db_prefix, $modSettings, $smcFunc;
1971
1972	$values = array(
1973		'show_latest_member' => @$GLOBALS['showlatestmember'],
1974		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1975		'show_modify' => @$GLOBALS['showmodify'],
1976		'show_user_images' => @$GLOBALS['showuserpic'],
1977		'show_blurb' => @$GLOBALS['showusertext'],
1978		'show_gender' => @$GLOBALS['showgenderimage'],
1979		'show_newsfader' => @$GLOBALS['shownewsfader'],
1980		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1981		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1982		'linktree_link' => @$GLOBALS['curposlinks'],
1983		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1984		'show_mark_read' => @$GLOBALS['showmarkread'],
1985		'show_board_desc' => @$GLOBALS['ShowBDescrip'],
1986		'newsfader_time' => @$GLOBALS['fadertime'],
1987		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1988		'enable_news' => @$GLOBALS['enable_news'],
1989		'return_to_post' => @$modSettings['returnToPost'],
1990	);
1991
1992	$themeData = array();
1993	foreach ($values as $variable => $value)
1994	{
1995		if (!isset($value) || $value === null)
1996			$value = 0;
1997
1998		$themeData[] = array(0, 1, $variable, $value);
1999	}
2000	if (!empty($themeData))
2001	{
2002		$smcFunc['db_insert']('ignore',
2003			$db_prefix . 'themes',
2004			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
2005			$themeData,
2006			array('id_member', 'id_theme', 'variable')
2007		);
2008	}
2009}
2010
2011// This function only works with MySQL but that's fine as it is only used for v1.0.
2012function convertSettingstoOptions()
2013{
2014	global $db_prefix, $modSettings, $smcFunc;
2015
2016	// Format: new_setting -> old_setting_name.
2017	$values = array(
2018		'calendar_start_day' => 'cal_startmonday',
2019		'view_newest_first' => 'viewNewestFirst',
2020		'view_newest_pm_first' => 'viewNewestFirst',
2021	);
2022
2023	foreach ($values as $variable => $value)
2024	{
2025		if (empty($modSettings[$value[0]]))
2026			continue;
2027
2028		$smcFunc['db_query']('', '
2029			INSERT IGNORE INTO {db_prefix}themes
2030				(id_member, id_theme, variable, value)
2031			SELECT id_member, 1, {string:variable}, {string:value}
2032			FROM {db_prefix}members',
2033			array(
2034				'variable' => $variable,
2035				'value' => $modSettings[$value[0]],
2036				'db_error_skip' => true,
2037			)
2038		);
2039
2040		$smcFunc['db_query']('', '
2041			INSERT IGNORE INTO {db_prefix}themes
2042				(id_member, id_theme, variable, value)
2043			VALUES (-1, 1, {string:variable}, {string:value})',
2044			array(
2045				'variable' => $variable,
2046				'value' => $modSettings[$value[0]],
2047				'db_error_skip' => true,
2048			)
2049		);
2050	}
2051}
2052
2053function changeSettings($config_vars)
2054{
2055	$settingsArray = file(BOARDDIR . '/Settings_bak.php');
2056
2057	if (count($settingsArray) == 1)
2058		$settingsArray = preg_split('~[\r\n]~', $settingsArray[0]);
2059
2060	for ($i = 0, $n = count($settingsArray); $i < $n; $i++)
2061	{
2062		// Don't trim or bother with it if it's not a variable.
2063		if (substr($settingsArray[$i], 0, 1) == '$')
2064		{
2065			$settingsArray[$i] = trim($settingsArray[$i]) . "\n";
2066
2067			foreach ($config_vars as $var => $val)
2068			{
2069				if (isset($settingsArray[$i]) && strncasecmp($settingsArray[$i], '$' . $var, 1 + strlen($var)) == 0)
2070				{
2071					if ($val == '#remove#')
2072						unset($settingsArray[$i]);
2073					else
2074					{
2075						$comment = strstr(substr($settingsArray[$i], strpos($settingsArray[$i], ';')), '#');
2076						$settingsArray[$i] = '$' . $var . ' = ' . $val . ';' . ($comment != '' ? "\t\t" . $comment : "\n");
2077					}
2078
2079					unset($config_vars[$var]);
2080				}
2081			}
2082		}
2083	}
2084
2085	if (!empty($config_vars))
2086	{
2087		$settingsArray[$i++] = '';
2088		foreach ($config_vars as $var => $val)
2089		{
2090			if ($val != '#remove#')
2091				$settingsArray[$i++] = '$' . $var . ' = ' . $val . ';' . "\n";
2092		}
2093	}
2094
2095	// Blank out the file - done to fix a oddity with some servers.
2096	$fp = fopen(BOARDDIR . '/Settings.php', 'w');
2097	fclose($fp);
2098
2099	$fp = fopen(BOARDDIR . '/Settings.php', 'r+');
2100	$lines = count($settingsArray);
2101	for ($i = 0; $i < $lines; $i++)
2102	{
2103		if (isset($settingsArray[$i]))
2104			fwrite($fp, strtr($settingsArray[$i], "\r", ''));
2105	}
2106	fclose($fp);
2107}
2108function updateLastError()
2109{
2110	// clear out the db_last_error file
2111	file_put_contents(dirname(__FILE__) . '/db_last_error.php', '<' . '?' . "php\n" . '$db_last_error = 0;');
2112}
2113
2114function php_version_check()
2115{
2116	$minver = explode('.', $GLOBALS['required_php_version']);
2117	$curver = explode('.', PHP_VERSION);
2118
2119	return !(($curver[0] <= $minver[0]) && ($curver[1] <= $minver[1]) && ($curver[1] <= $minver[1]) && ($curver[2][0] < $minver[2][0]));
2120}
2121
2122function db_version_check()
2123{
2124	global $db_type, $databases;
2125
2126	$curver = eval($databases[$db_type]['version_check']);
2127	$curver = preg_replace('~\-.+?$~', '', $curver);
2128
2129	return version_compare($databases[$db_type]['version'], $curver, '<=');
2130}
2131
2132function getMemberGroups()
2133{
2134	global $db_prefix, $smcFunc;
2135	static $member_groups = array();
2136
2137	if (!empty($member_groups))
2138		return $member_groups;
2139
2140	$request = $smcFunc['db_query']('', '
2141		SELECT group_name, id_group
2142		FROM {db_prefix}membergroups
2143		WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
2144		array(
2145			'admin_group' => 1,
2146			'old_group' => 7,
2147			'db_error_skip' => true,
2148		)
2149	);
2150	if ($request === false)
2151	{
2152		$request = $smcFunc['db_query']('', '
2153			SELECT membergroup, id_group
2154			FROM {db_prefix}membergroups
2155			WHERE id_group = {int:admin_group} OR id_group > {int:old_group}',
2156			array(
2157				'admin_group' => 1,
2158				'old_group' => 7,
2159				'db_error_skip' => true,
2160			)
2161		);
2162	}
2163	while ($row = $smcFunc['db_fetch_row']($request))
2164		$member_groups[trim($row[0])] = $row[1];
2165	$smcFunc['db_free_result']($request);
2166
2167	return $member_groups;
2168}
2169
2170function fixRelativePath($path)
2171{
2172	global $install_path;
2173
2174	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
2175	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
2176}
2177
2178function parse_sql($filename)
2179{
2180	global $db_prefix, $db_collation, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
2181	global $upcontext, $support_js, $is_debug, $smcFunc, $db_connection, $databases, $db_type, $db_character_set;
2182
2183/*
2184	Failure allowed on:
2185		- INSERT INTO but not INSERT IGNORE INTO.
2186		- UPDATE IGNORE but not UPDATE.
2187		- ALTER TABLE and ALTER IGNORE TABLE.
2188		- DROP TABLE.
2189	Yes, I realize that this is a bit confusing... maybe it should be done differently?
2190
2191	If a comment...
2192		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
2193		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
2194		- is only ---#, it is "done." and then a break - only shown in debug.
2195		- begins with ---{ it is a code block terminating at ---}.
2196
2197	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
2198
2199	Replaces the following variables:
2200		- {BOARDDIR}
2201		- {$boardurl}
2202		- {$db_prefix}
2203		- {$db_collation}
2204*/
2205
2206	// May want to use extended functionality.
2207	db_extend();
2208	db_extend('packages');
2209
2210	// Our custom error handler - does nothing but does stop public errors from XML!
2211	if (!function_exists('sql_error_handler'))
2212	{
2213		function sql_error_handler($errno, $errstr, $errfile, $errline)
2214		{
2215			global $support_js;
2216
2217			if ($support_js)
2218				return true;
2219			else
2220				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
2221		}
2222	}
2223
2224	// Make our own error handler.
2225	set_error_handler('sql_error_handler');
2226
2227	// If we're on MySQL supporting collations then let's find out what the members table uses and put it in a global var - to allow upgrade script to match collations!
2228	if (!empty($databases[$db_type]['utf8_support']) && version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
2229	{
2230		$request = $smcFunc['db_query']('', '
2231			SHOW TABLE STATUS
2232			LIKE {string:table_name}',
2233			array(
2234				'table_name' => "{$db_prefix}members",
2235				'db_error_skip' => true,
2236			)
2237		);
2238		if ($smcFunc['db_num_rows']($request) === 0)
2239			die('Unable to find members table!');
2240		$table_status = $smcFunc['db_fetch_assoc']($request);
2241		$smcFunc['db_free_result']($request);
2242
2243		if (!empty($table_status['Collation']))
2244		{
2245			$request = $smcFunc['db_query']('', '
2246				SHOW COLLATION
2247				LIKE {string:collation}',
2248				array(
2249					'collation' => $table_status['Collation'],
2250					'db_error_skip' => true,
2251				)
2252			);
2253			// Got something?
2254			if ($smcFunc['db_num_rows']($request) !== 0)
2255				$collation_info = $smcFunc['db_fetch_assoc']($request);
2256			$smcFunc['db_free_result']($request);
2257
2258			// Excellent!
2259			if (!empty($collation_info['Collation']) && !empty($collation_info['Charset']))
2260				$db_collation = ' CHARACTER SET ' . $collation_info['Charset'] . ' COLLATE ' . $collation_info['Collation'];
2261		}
2262	}
2263	if (empty($db_collation))
2264		$db_collation = '';
2265
2266	$endl = $command_line ? "\n" : '<br />' . "\n";
2267
2268	$lines = file($filename);
2269
2270	$current_type = 'sql';
2271	$current_data = '';
2272	$substep = 0;
2273	$last_step = '';
2274
2275	// Make sure all newly created tables will have the proper characters set.
2276	if (isset($db_character_set) && $db_character_set === 'utf8')
2277		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
2278
2279	// Count the total number of steps within this file - for progress.
2280	$file_steps = substr_count(implode('', $lines), '---#');
2281	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
2282	$upcontext['debug_items'] = $file_steps;
2283	$upcontext['current_item_num'] = 0;
2284	$upcontext['current_item_name'] = '';
2285	$upcontext['current_debug_item_num'] = 0;
2286	$upcontext['current_debug_item_name'] = '';
2287	// This array keeps a record of what we've done in case java is dead...
2288	$upcontext['actioned_items'] = array();
2289
2290	$done_something = false;
2291
2292	foreach ($lines as $line_number => $line)
2293	{
2294		$do_current = $substep >= $_GET['substep'];
2295
2296		// Get rid of any comments in the beginning of the line...
2297		if (substr(trim($line), 0, 2) === '/*')
2298			$line = preg_replace('~/\*.+?\*/~', '', $line);
2299
2300		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
2301		if ($is_debug && !$support_js && $command_line)
2302			flush();
2303
2304		if (trim($line) === '')
2305			continue;
2306
2307		if (trim(substr($line, 0, 3)) === '---')
2308		{
2309			$type = substr($line, 3, 1);
2310
2311			// An error??
2312			if (trim($current_data) != '' && $type !== '}')
2313			{
2314				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
2315				if ($command_line)
2316					echo $upcontext['error_message'];
2317			}
2318
2319			if ($type == ' ')
2320			{
2321				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
2322				{
2323					echo ' Successful.', $endl;
2324					flush();
2325				}
2326
2327				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
2328				$upcontext['current_item_num']++;
2329				$upcontext['current_item_name'] = $last_step;
2330
2331				if ($do_current)
2332				{
2333					$upcontext['actioned_items'][] = $last_step;
2334					if ($command_line)
2335						echo ' * ';
2336				}
2337			}
2338			elseif ($type == '#')
2339			{
2340				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
2341
2342				$upcontext['current_debug_item_num']++;
2343				if (trim($line) != '---#')
2344					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
2345
2346				// Have we already done something?
2347				if (isset($_GET['xml']) && $done_something)
2348				{
2349					restore_error_handler();
2350					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
2351				}
2352
2353				if ($do_current)
2354				{
2355					if (trim($line) == '---#' && $command_line)
2356						echo ' done.', $endl;
2357					elseif ($command_line)
2358						echo ' +++ ', rtrim(substr($line, 4));
2359					elseif (trim($line) != '---#')
2360					{
2361						if ($is_debug)
2362							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
2363					}
2364				}
2365
2366				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
2367				{
2368					if ($command_line)
2369						echo ' * ';
2370					else
2371						$upcontext['actioned_items'][] = $last_step;
2372				}
2373
2374				// Small step - only if we're actually doing stuff.
2375				if ($do_current)
2376					nextSubstep(++$substep);
2377				else
2378					$substep++;
2379			}
2380			elseif ($type == '{')
2381				$current_type = 'code';
2382			elseif ($type == '}')
2383			{
2384				$current_type = 'sql';
2385
2386				if (!$do_current)
2387				{
2388					$current_data = '';
2389					continue;
2390				}
2391
2392				if (eval('global $db_prefix, $modSettings, $smcFunc; ' . $current_data) === false)
2393				{
2394					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
2395					if ($command_line)
2396						echo $upcontext['error_message'];
2397				}
2398
2399				// Done with code!
2400				$current_data = '';
2401				$done_something = true;
2402			}
2403
2404			continue;
2405		}
2406
2407		$current_data .= $line;
2408		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
2409		{
2410			if ((!$support_js || isset($_GET['xml'])))
2411			{
2412				if (!$do_current)
2413				{
2414					$current_data = '';
2415					continue;
2416				}
2417
2418				$current_data = strtr(substr(rtrim($current_data), 0, -1), array('{$db_prefix}' => $db_prefix, '{BOARDDIR}' => BOARDDIR, '{$sboarddir}' => addslashes(BOARDDIR), '{$boardurl}' => $boardurl, '{$db_collation}' => $db_collation));
2419
2420				upgrade_query($current_data);
2421
2422				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
2423				/*
2424				$result = $smcFunc['db_query']('', $current_data, false, false);
2425				// Went wrong?
2426				if (!$result)
2427				{
2428					// Bit of a bodge - do we want the error?
2429					if (!empty($upcontext['return_error']))
2430					{
2431						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
2432						return false;
2433					}
2434				}*/
2435				$done_something = true;
2436			}
2437			$current_data = '';
2438		}
2439		// If this is xml based and we're just getting the item name then that's grand.
2440		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
2441		{
2442			restore_error_handler();
2443			return false;
2444		}
2445
2446		// Clean up by cleaning any step info.
2447		$step_progress = array();
2448		$custom_warning = '';
2449	}
2450
2451	// Put back the error handler.
2452	restore_error_handler();
2453
2454	if ($command_line)
2455	{
2456		echo ' Successful.' . "\n";
2457		flush();
2458	}
2459
2460	$_GET['substep'] = 0;
2461	return true;
2462}
2463
2464function upgrade_query($string, $unbuffered = false)
2465{
2466	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
2467	global $db_name, $db_unbuffered, $smcFunc;
2468
2469	// Get the query result - working around some specific security - just this once!
2470	$modSettings['disableQueryCheck'] = true;
2471	$db_unbuffered = $unbuffered;
2472	$result = $smcFunc['db_query']('', $string, 'security_override');
2473	$db_unbuffered = false;
2474
2475	// Failure?!
2476	if ($result !== false)
2477		return $result;
2478
2479	$db_error_message = $smcFunc['db_error']($db_connection);
2480	// If MySQL we do something more clever.
2481	if ($db_type == 'mysql')
2482	{
2483		$mysql_errno = mysql_errno($db_connection);
2484		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
2485
2486		// Error numbers:
2487		//    1016: Can't open file '....MYI'
2488		//    1050: Table already exists.
2489		//    1054: Unknown column name.
2490		//    1060: Duplicate column name.
2491		//    1061: Duplicate key name.
2492		//    1062: Duplicate entry for unique key.
2493		//    1068: Multiple primary keys.
2494		//    1072: Key column '%s' doesn't exist in table.
2495		//    1091: Can't drop key, doesn't exist.
2496		//    1146: Table doesn't exist.
2497		//    2013: Lost connection to server during query.
2498
2499		if ($mysql_errno == 1016)
2500		{
2501			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
2502				mysql_query( '
2503					REPAIR TABLE `' . $match[1] . '`');
2504
2505			$result = mysql_query($string);
2506			if ($result !== false)
2507				return $result;
2508		}
2509		elseif ($mysql_errno == 2013)
2510		{
2511			$db_connection = mysql_connect($db_server, $db_user, $db_passwd);
2512			mysql_select_db($db_name, $db_connection);
2513
2514			if ($db_connection)
2515			{
2516				$result = mysql_query($string);
2517
2518				if ($result !== false)
2519					return $result;
2520			}
2521		}
2522		// Duplicate column name... should be okay ;).
2523		elseif (in_array($mysql_errno, array(1060, 1061, 1068, 1091)))
2524			return false;
2525		// Duplicate insert... make sure it's the proper type of query ;).
2526		elseif (in_array($mysql_errno, array(1054, 1062, 1146)) && $error_query)
2527			return false;
2528		// Creating an index on a non-existent column.
2529		elseif ($mysql_errno == 1072)
2530			return false;
2531		elseif ($mysql_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
2532			return false;
2533	}
2534	// If a table already exists don't go potty.
2535	else
2536	{
2537		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I')))
2538		{
2539			if (strpos($db_error_message, 'exist') !== false)
2540				return true;
2541			// SQLite
2542			if (strpos($db_error_message, 'missing') !== false)
2543				return true;
2544		}
2545		elseif (strpos(trim($string), 'INSERT ') !== false)
2546		{
2547			if (strpos($db_error_message, 'duplicate') !== false)
2548				return true;
2549		}
2550	}
2551
2552	// Get the query string so we pass everything.
2553	$query_string = '';
2554	foreach ($_GET as $k => $v)
2555		$query_string .= ';' . $k . '=' . $v;
2556	if (strlen($query_string) != 0)
2557		$query_string = '?' . substr($query_string, 1);
2558
2559	if ($command_line)
2560	{
2561		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
2562		die;
2563	}
2564
2565	// Bit of a bodge - do we want the error?
2566	if (!empty($upcontext['return_error']))
2567	{
2568		$upcontext['error_message'] = $db_error_message;
2569		return false;
2570	}
2571
2572	// Otherwise we have to display this somewhere appropriate if possible.
2573	$upcontext['forced_error_message'] = '
2574			<strong>Unsuccessful!</strong><br />
2575
2576			<div style="margin: 2ex;">
2577				This query:
2578				<blockquote><tt>' . nl2br(htmlspecialchars(trim($string))) . ';</tt></blockquote>
2579
2580				Caused the error:
2581				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
2582			</div>
2583
2584			<form action="' . $upgradeurl . $query_string . '" method="post">
2585				<input type="submit" value="Try again" class="button_submit" />
2586			</form>
2587		</div>';
2588
2589	upgradeExit();
2590}
2591
2592// This performs a table alter, but does it unbuffered so the script can time out professionally.
2593function protected_alter($change, $substep, $is_test = false)
2594{
2595	global $db_prefix, $smcFunc;
2596
2597	db_extend('packages');
2598
2599	// Firstly, check whether the current index/column exists.
2600	$found = false;
2601	if ($change['type'] === 'column')
2602	{
2603		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
2604		foreach ($columns as $column)
2605		{
2606			// Found it?
2607			if ($column['name'] === $change['name'])
2608			{
2609				$found |= 1;
2610				// Do some checks on the data if we have it set.
2611				if (isset($change['col_type']))
2612					$found &= $change['col_type'] === $column['type'];
2613				if (isset($change['null_allowed']))
2614					$found &= $column['null'] == $change['null_allowed'];
2615				if (isset($change['default']))
2616					$found &= $change['default'] === $column['default'];
2617			}
2618		}
2619	}
2620	elseif ($change['type'] === 'index')
2621	{
2622		$request = upgrade_query( '
2623			SHOW INDEX
2624			FROM ' . $db_prefix . $change['table']);
2625		if ($request !== false)
2626		{
2627			$cur_index = array();
2628
2629			while ($row = $smcFunc['db_fetch_assoc']($request))
2630				if ($row['Key_name'] === $change['name'])
2631					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2632
2633			ksort($cur_index, SORT_NUMERIC);
2634			$found = array_values($cur_index) === $change['target_columns'];
2635
2636			$smcFunc['db_free_result']($request);
2637		}
2638	}
2639
2640	// If we're trying to add and it's added, we're done.
2641	if ($found && in_array($change['method'], array('add', 'change')))
2642		return true;
2643	// Otherwise if we're removing and it wasn't found we're also done.
2644	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2645		return true;
2646	// Otherwise is it just a test?
2647	elseif ($is_test)
2648		return false;
2649
2650	// Not found it yet? Bummer! How about we see if we're currently doing it?
2651	$running = false;
2652	$found = false;
2653	while (1 == 1)
2654	{
2655		$request = upgrade_query('
2656			SHOW FULL PROCESSLIST');
2657		while ($row = $smcFunc['db_fetch_assoc']($request))
2658		{
2659			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2660				$found = true;
2661		}
2662
2663		// Can't find it? Then we need to run it fools!
2664		if (!$found && !$running)
2665		{
2666			$smcFunc['db_free_result']($request);
2667
2668			$success = upgrade_query('
2669				ALTER TABLE ' . $db_prefix . $change['table'] . '
2670				' . $change['text'], true) !== false;
2671
2672			if (!$success)
2673				return false;
2674
2675			// Return
2676			$running = true;
2677		}
2678		// What if we've not found it, but we'd ran it already? Must of completed.
2679		elseif (!$found)
2680		{
2681			$smcFunc['db_free_result']($request);
2682			return true;
2683		}
2684
2685		// Pause execution for a sec or three.
2686		sleep(3);
2687
2688		// Can never be too well protected.
2689		nextSubstep($substep);
2690	}
2691
2692	// Protect it.
2693	nextSubstep($substep);
2694}
2695
2696// Alter a text column definition preserving its character set.
2697function textfield_alter($change, $substep)
2698{
2699	global $db_prefix, $databases, $db_type, $smcFunc;
2700
2701	// Versions of MySQL < 4.1 wouldn't benefit from character set detection.
2702	if (empty($databases[$db_type]['utf8_support']) || version_compare($databases[$db_type]['utf8_version'], eval($databases[$db_type]['utf8_version_check']), '>'))
2703	{
2704		$column_fix = true;
2705		$null_fix = !$change['null_allowed'];
2706	}
2707	else
2708	{
2709		$request = $smcFunc['db_query']('', '
2710			SHOW FULL COLUMNS
2711			FROM {db_prefix}' . $change['table'] . '
2712			LIKE {string:column}',
2713			array(
2714				'column' => $change['column'],
2715				'db_error_skip' => true,
2716			)
2717		);
2718		if ($smcFunc['db_num_rows']($request) === 0)
2719			die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2720		$table_row = $smcFunc['db_fetch_assoc']($request);
2721		$smcFunc['db_free_result']($request);
2722
2723		// If something of the current column definition is different, fix it.
2724		$column_fix = $table_row['Type'] !== $change['type'] || (strtolower($table_row['Null']) === 'yes') !== $change['null_allowed'] || ($table_row['Default'] === null) !== !isset($change['default']) || (isset($change['default']) && $change['default'] !== $table_row['Default']);
2725
2726		// Columns that previously allowed null, need to be converted first.
2727		$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2728
2729		// Get the character set that goes with the collation of the column.
2730		if ($column_fix && !empty($table_row['Collation']))
2731		{
2732			$request = $smcFunc['db_query']('', '
2733				SHOW COLLATION
2734				LIKE {string:collation}',
2735				array(
2736					'collation' => $table_row['Collation'],
2737					'db_error_skip' => true,
2738				)
2739			);
2740			// No results? Just forget it all together.
2741			if ($smcFunc['db_num_rows']($request) === 0)
2742				unset($table_row['Collation']);
2743			else
2744				$collation_info = $smcFunc['db_fetch_assoc']($request);
2745			$smcFunc['db_free_result']($request);
2746		}
2747	}
2748
2749	if ($column_fix)
2750	{
2751		// Make sure there are no NULL's left.
2752		if ($null_fix)
2753			$smcFunc['db_query']('', '
2754				UPDATE {db_prefix}' . $change['table'] . '
2755				SET ' . $change['column'] . ' = {string:default}
2756				WHERE ' . $change['column'] . ' IS NULL',
2757				array(
2758					'default' => isset($change['default']) ? $change['default'] : '',
2759					'db_error_skip' => true,
2760				)
2761			);
2762
2763		// Do the actual alteration.
2764		$smcFunc['db_query']('', '
2765			ALTER TABLE {db_prefix}' . $change['table'] . '
2766			CHANGE COLUMN ' . $change['column'] . ' ' . $change['column'] . ' ' . $change['type'] . (isset($collation_info['Charset']) ? ' CHARACTER SET ' . $collation_info['Charset'] . ' COLLATE ' . $collation_info['Collation'] : '') . ($change['null_allowed'] ? '' : ' NOT NULL') . (isset($change['default']) ? ' default {string:default}' : ''),
2767			array(
2768				'default' => isset($change['default']) ? $change['default'] : '',
2769				'db_error_skip' => true,
2770			)
2771		);
2772	}
2773	nextSubstep($substep);
2774}
2775
2776// Check if we need to alter this query.
2777function checkChange(&$change)
2778{
2779	global $smcFunc, $db_type, $databases;
2780	static $database_version, $where_field_support;
2781
2782	// Attempt to find a database_version.
2783	if (empty($database_version))
2784	{
2785		$database_version = $databases[$db_type]['version_check'];
2786		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2787	}
2788
2789	// Not a column we need to check on?
2790	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2791		return;
2792
2793	// Break it up you (six|seven).
2794	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2795
2796	// Can we support a shortcut method?
2797	if ($where_field_support)
2798	{
2799		// Get the details about this change.
2800		$request = $smcFunc['db_query']('', '
2801			SHOW FIELDS
2802			FROM {db_prefix}{raw:table}
2803			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2804			array(
2805				'table' => $change['table'],
2806				'old_name' => $temp[1],
2807				'new_name' => $temp[2],
2808		));
2809		if ($smcFunc['db_num_rows'] != 1)
2810			return;
2811
2812		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2813		$smcFunc['db_free_result']($request);
2814	}
2815	else
2816	{
2817		// Do this the old fashion, sure method way.
2818		$request = $smcFunc['db_query']('', '
2819			SHOW FIELDS
2820			FROM {db_prefix}{raw:table}',
2821			array(
2822				'table' => $change['table'],
2823		));
2824		// Mayday!
2825		if ($smcFunc['db_num_rows'] == 0)
2826			return;
2827
2828		// Oh where, oh where has my little field gone. Oh where can it be...
2829		while ($row = $smcFunc['db_query']($request))
2830			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2831			{
2832				$current_type = $row['Type'];
2833				break;
2834			}
2835	}
2836
2837	// If this doesn't match, the column may of been altered for a reason.
2838	if (trim($current_type) != trim($temp[3]))
2839		$temp[3] = $current_type;
2840
2841	// Piece this back together.
2842	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2843}
2844
2845// The next substep.
2846function nextSubstep($substep)
2847{
2848	global $start_time, $timeLimitThreshold, $command_line, $file_steps, $modSettings, $custom_warning;
2849	global $step_progress, $is_debug, $upcontext;
2850
2851	if ($_GET['substep'] < $substep)
2852		$_GET['substep'] = $substep;
2853
2854	if ($command_line)
2855	{
2856		if (time() - $start_time > 1 && empty($is_debug))
2857		{
2858			echo '.';
2859			$start_time = time();
2860		}
2861		return;
2862	}
2863
2864	@set_time_limit(300);
2865	if (function_exists('apache_reset_timeout'))
2866		@apache_reset_timeout();
2867
2868	if (time() - $start_time <= $timeLimitThreshold)
2869		return;
2870
2871	// Do we have some custom step progress stuff?
2872	if (!empty($step_progress))
2873	{
2874		$upcontext['substep_progress'] = 0;
2875		$upcontext['substep_progress_name'] = $step_progress['name'];
2876		if ($step_progress['current'] > $step_progress['total'])
2877			$upcontext['substep_progress'] = 99.9;
2878		else
2879			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2880
2881		// Make it nicely rounded.
2882		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2883	}
2884
2885	// If this is XML we just exit right away!
2886	if (isset($_GET['xml']))
2887		return upgradeExit();
2888
2889	// We're going to pause after this!
2890	$upcontext['pause'] = true;
2891
2892	$upcontext['query_string'] = '';
2893	foreach ($_GET as $k => $v)
2894	{
2895		if ($k != 'data' && $k != 'substep' && $k != 'step')
2896			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2897	}
2898
2899	// Custom warning?
2900	if (!empty($custom_warning))
2901		$upcontext['custom_warning'] = $custom_warning;
2902
2903	upgradeExit();
2904}
2905
2906function cmdStep0()
2907{
2908	global $db_prefix, $language, $modSettings, $start_time, $databases, $db_type, $smcFunc, $upcontext;
2909	global $language, $is_debug, $txt;
2910	$start_time = time();
2911
2912	ob_end_clean();
2913	ob_implicit_flush(true);
2914	@set_time_limit(600);
2915
2916	if (!isset($_SERVER['argv']))
2917		$_SERVER['argv'] = array();
2918	$_GET['maint'] = 1;
2919
2920	foreach ($_SERVER['argv'] as $i => $arg)
2921	{
2922		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2923			$_GET['lang'] = $match[1];
2924		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2925			continue;
2926		elseif ($arg == '--no-maintenance')
2927			$_GET['maint'] = 0;
2928		elseif ($arg == '--debug')
2929			$is_debug = true;
2930		elseif ($arg == '--backup')
2931			$_POST['backup'] = 1;
2932		elseif ($arg == '--template' && (file_exists(BOARDDIR . '/template.php') || file_exists(BOARDDIR . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2933			$_GET['conv'] = 1;
2934		elseif ($i != 0)
2935		{
2936			echo 'ELKARTE Command-line Upgrader
2937Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2938
2939    --language=LANG         Reset the forum\'s language to LANG.
2940    --no-maintenance        Don\'t put the forum into maintenance mode.
2941    --debug                 Output debugging information.
2942    --backup                Create backups of tables with "backup_" prefix.';
2943			echo "\n";
2944			exit;
2945		}
2946	}
2947
2948	if (!php_version_check())
2949		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2950	if (!db_version_check())
2951		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2952
2953	if (!empty($databases[$db_type]['alter_support']) && $smcFunc['db_query']('alter_boards', 'ALTER TABLE {db_prefix}boards ORDER BY id_board', array()) === false)
2954		print_error('Error: The ' . $databases[$db_type]['name'] . ' account in Settings.php does not have sufficient privileges.', true);
2955
2956	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2957		&& @file_exists(SOURCEDIR . '/QueryString.php')
2958		&& @file_exists(SOURCEDIR . '/ManageBoards.php');
2959	if (!$check && !isset($modSettings['elkVersion']))
2960		print_error('Error: Some files are missing or out-of-date.', true);
2961
2962	// Do a quick version spot check.
2963	$temp = substr(@implode('', @file(BOARDDIR . '/index.php')), 0, 4096);
2964	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2965	if (empty($match[1]) || $match[1] != CURRENT_VERSION)
2966		print_error('Error: Some files have not yet been updated properly.');
2967
2968	// Make sure Settings.php is writable.
2969	if (!is_writable(BOARDDIR . '/Settings.php'))
2970		@chmod(BOARDDIR . '/Settings.php', 0777);
2971	if (!is_writable(BOARDDIR . '/Settings.php'))
2972		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2973
2974	// Make sure Settings.php is writable.
2975	if (!is_writable(BOARDDIR . '/Settings_bak.php'))
2976		@chmod(BOARDDIR . '/Settings_bak.php', 0777);
2977	if (!is_writable(BOARDDIR . '/Settings_bak.php'))
2978		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2979
2980	if (isset($modSettings['agreement']) && (!is_writable(BOARDDIR) || file_exists(BOARDDIR . '/agreement.txt')) && !is_writable(BOARDDIR . '/agreement.txt'))
2981		print_error('Error: Unable to obtain write access to "agreement.txt".');
2982	elseif (isset($modSettings['agreement']))
2983	{
2984		$fp = fopen(BOARDDIR . '/agreement.txt', 'w');
2985		fwrite($fp, $modSettings['agreement']);
2986		fclose($fp);
2987	}
2988
2989	// Make sure themes is writable.
2990	if (!is_writable($modSettings['theme_dir']))
2991		@chmod($modSettings['theme_dir'], 0777);
2992
2993	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['elkVersion']))
2994		print_error('Error: Unable to obtain write access to "themes".');
2995
2996	// Make sure cache directory exists and is writable!
2997	$CACHEDIR_temp = !defined('CACHEDIR') ? BOARDDIR . '/cache' : CACHEDIR;
2998	if (!file_exists($CACHEDIR_temp))
2999		@mkdir($CACHEDIR_temp);
3000
3001	if (!is_writable($CACHEDIR_temp))
3002		@chmod($CACHEDIR_temp, 0777);
3003
3004	if (!is_writable($CACHEDIR_temp))
3005		print_error('Error: Unable to obtain write access to "cache".', true);
3006
3007	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['elkVersion']) && !isset($_GET['lang']))
3008		print_error('Error: Unable to find language files!', true);
3009	else
3010	{
3011		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
3012		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
3013
3014		if (empty($match[1]) || $match[1] != CURRENT_LANG_VERSION)
3015			print_error('Error: Language files out of date.', true);
3016		if (!file_exists($modSettings['theme_dir'] . 'languages/Install.' . $upcontext['language'] . '.php'))
3017			print_error('Error: Install language is missing for selected language.', true);
3018
3019		// Otherwise include it!
3020		require_once($modSettings['theme_dir'] . 'languages/Install.' . $upcontext['language'] . '.php');
3021	}
3022
3023	// Make sure we skip the HTML for login.
3024	$_POST['upcont'] = true;
3025	$upcontext['current_step'] = 1;
3026}
3027
3028function print_error($message, $fatal = false)
3029{
3030	static $fp = null;
3031
3032	if ($fp === null)
3033		$fp = fopen('php://stderr', 'wb');
3034
3035	fwrite($fp, $message . "\n");
3036
3037	if ($fatal)
3038		exit;
3039}
3040
3041function throw_error($message)
3042{
3043	global $upcontext;
3044
3045	$upcontext['error_msg'] = $message;
3046	$upcontext['sub_template'] = 'error_message';
3047
3048	return false;
3049}
3050
3051// Check files are writable - make them writable if necessary...
3052function makeFilesWritable(&$files)
3053{
3054	global $upcontext;
3055
3056	if (empty($files))
3057		return true;
3058
3059	$failure = false;
3060	// On linux, it's easy - just use is_writable!
3061	if (substr(__FILE__, 1, 2) != ':\\')
3062	{
3063		foreach ($files as $k => $file)
3064		{
3065			if (!is_writable($file))
3066			{
3067				@chmod($file, 0755);
3068
3069				// Well, 755 hopefully worked... if not, try 777.
3070				if (!is_writable($file) && !@chmod($file, 0777))
3071					$failure = true;
3072				// Otherwise remove it as it's good!
3073				else
3074					unset($files[$k]);
3075			}
3076			else
3077				unset($files[$k]);
3078		}
3079	}
3080	// Windows is trickier.  Let's try opening for r+...
3081	else
3082	{
3083		foreach ($files as $k => $file)
3084		{
3085			// Folders can't be opened for write... but the index.php in them can ;).
3086			if (is_dir($file))
3087				$file .= '/index.php';
3088
3089			// Funny enough, chmod actually does do something on windows - it removes the read only attribute.
3090			@chmod($file, 0777);
3091			$fp = @fopen($file, 'r+');
3092
3093			// Hmm, okay, try just for write in that case...
3094			if (!$fp)
3095				$fp = @fopen($file, 'w');
3096
3097			if (!$fp)
3098				$failure = true;
3099			else
3100				unset($files[$k]);
3101			@fclose($fp);
3102		}
3103	}
3104
3105	if (empty($files))
3106		return true;
3107
3108	if (!isset($_SERVER))
3109		return !$failure;
3110
3111	// What still needs to be done?
3112	$upcontext['chmod']['files'] = $files;
3113
3114	// If it's windows it's a mess...
3115	if ($failure && substr(__FILE__, 1, 2) == ':\\')
3116	{
3117		$upcontext['chmod']['ftp_error'] = 'total_mess';
3118
3119		return false;
3120	}
3121	// We're going to have to use... FTP!
3122	elseif ($failure)
3123	{
3124		// Load any session data we might have...
3125		if (!isset($_POST['ftp_username']) && isset($_SESSION['installer_temp_ftp']))
3126		{
3127			$upcontext['chmod']['server'] = $_SESSION['installer_temp_ftp']['server'];
3128			$upcontext['chmod']['port'] = $_SESSION['installer_temp_ftp']['port'];
3129			$upcontext['chmod']['username'] = $_SESSION['installer_temp_ftp']['username'];
3130			$upcontext['chmod']['password'] = $_SESSION['installer_temp_ftp']['password'];
3131			$upcontext['chmod']['path'] = $_SESSION['installer_temp_ftp']['path'];
3132		}
3133		// Or have we submitted?
3134		elseif (isset($_POST['ftp_username']))
3135		{
3136			$upcontext['chmod']['server'] = $_POST['ftp_server'];
3137			$upcontext['chmod']['port'] = $_POST['ftp_port'];
3138			$upcontext['chmod']['username'] = $_POST['ftp_username'];
3139			$upcontext['chmod']['password'] = $_POST['ftp_password'];
3140			$upcontext['chmod']['path'] = $_POST['ftp_path'];
3141		}
3142
3143		if (isset($upcontext['chmod']['username']))
3144		{
3145			$ftp = new Ftp_Connection($upcontext['chmod']['server'], $upcontext['chmod']['port'], $upcontext['chmod']['username'], $upcontext['chmod']['password']);
3146
3147			if ($ftp->error === false)
3148			{
3149				// Try it without /home/abc just in case they messed up.
3150				if (!$ftp->chdir($upcontext['chmod']['path']))
3151				{
3152					$upcontext['chmod']['ftp_error'] = $ftp->last_message;
3153					$ftp->chdir(preg_replace('~^/home[2]?/[^/]+?~', '', $upcontext['chmod']['path']));
3154				}
3155			}
3156		}
3157
3158		if (!isset($ftp) || $ftp->error !== false)
3159		{
3160			if (!isset($ftp))
3161				$ftp = new Ftp_Connection(null);
3162			// Save the error so we can mess with listing...
3163			elseif ($ftp->error !== false && !isset($upcontext['chmod']['ftp_error']))
3164				$upcontext['chmod']['ftp_error'] = $ftp->last_message === null ? '' : $ftp->last_message;
3165
3166			list ($username, $detect_path, $found_path) = $ftp->detect_path(dirname(__FILE__));
3167
3168			if ($found_path || !isset($upcontext['chmod']['path']))
3169				$upcontext['chmod']['path'] = $detect_path;
3170
3171			if (!isset($upcontext['chmod']['username']))
3172				$upcontext['chmod']['username'] = $username;
3173
3174			return false;
3175		}
3176		else
3177		{
3178			// We want to do a relative path for FTP.
3179			if (!in_array($upcontext['chmod']['path'], array('', '/')))
3180			{
3181				$ftp_root = strtr(BOARDDIR, array($upcontext['chmod']['path'] => ''));
3182				if (substr($ftp_root, -1) == '/' && ($upcontext['chmod']['path'] == '' || $upcontext['chmod']['path'][0] === '/'))
3183				$ftp_root = substr($ftp_root, 0, -1);
3184			}
3185			else
3186				$ftp_root = BOARDDIR;
3187
3188			// Save the info for next time!
3189			$_SESSION['installer_temp_ftp'] = array(
3190				'server' => $upcontext['chmod']['server'],
3191				'port' => $upcontext['chmod']['port'],
3192				'username' => $upcontext['chmod']['username'],
3193				'password' => $upcontext['chmod']['password'],
3194				'path' => $upcontext['chmod']['path'],
3195				'root' => $ftp_root,
3196			);
3197
3198			foreach ($files as $k => $file)
3199			{
3200				if (!is_writable($file))
3201					$ftp->chmod($file, 0755);
3202				if (!is_writable($file))
3203					$ftp->chmod($file, 0777);
3204
3205				// Assuming that didn't work calculate the path without the boarddir.
3206				if (!is_writable($file))
3207				{
3208					if (strpos($file, BOARDDIR) === 0)
3209					{
3210						$ftp_file = strtr($file, array($_SESSION['installer_temp_ftp']['root'] => ''));
3211						$ftp->chmod($ftp_file, 0755);
3212						if (!is_writable($file))
3213							$ftp->chmod($ftp_file, 0777);
3214						// Sometimes an extra slash can help...
3215						$ftp_file = '/' . $ftp_file;
3216						if (!is_writable($file))
3217							$ftp->chmod($ftp_file, 0755);
3218						if (!is_writable($file))
3219							$ftp->chmod($ftp_file, 0777);
3220					}
3221				}
3222
3223				if (is_writable($file))
3224					unset($files[$k]);
3225			}
3226
3227			$ftp->close();
3228		}
3229	}
3230
3231	// What remains?
3232	$upcontext['chmod']['files'] = $files;
3233
3234	if (empty($files))
3235		return true;
3236
3237	return false;
3238}
3239
3240function deleteUpgrader()
3241{
3242	@unlink(__FILE__);
3243
3244	// And the extra little files ;).
3245	@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
3246	@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
3247	@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
3248	@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
3249	@unlink(dirname(__FILE__) . '/webinstall.php');
3250
3251	$dh = opendir(dirname(__FILE__));
3252	while ($file = readdir($dh))
3253	{
3254		if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
3255			@unlink(dirname(__FILE__) . '/' . $file);
3256	}
3257	closedir($dh);
3258
3259	header('Location: http://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']) . dirname($_SERVER['PHP_SELF']) . '/themes/default/images/blank.png');
3260	exit;
3261}
3262
3263/******************************************************************************
3264******************* Templates are below this point ****************************
3265******************************************************************************/
3266
3267// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3268function template_chmod()
3269{
3270	global $upcontext, $upgradeurl, $settings;
3271
3272	// Don't call me twice!
3273	if (!empty($upcontext['chmod_called']))
3274		return;
3275
3276	$upcontext['chmod_called'] = true;
3277
3278	// Nothing?
3279	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3280		return;
3281
3282	// @todo Temporary!
3283	$txt['error_ftp_no_connect'] = 'Unable to connect to FTP server with this combination of details.';
3284	$txt['ftp_login'] = 'Your FTP connection information';
3285	$txt['ftp_login_info'] = 'This web installer needs your FTP information in order to automate the installation for you.  Please note that none of this information is saved in your installation, it is just used to setup ELKARTE.';
3286	$txt['ftp_server'] = 'Server';
3287	$txt['ftp_server_info'] = 'The address (often localhost) and port for your FTP server.';
3288	$txt['ftp_port'] = 'Port';
3289	$txt['ftp_username'] = 'Username';
3290	$txt['ftp_username_info'] = 'The username to login with. <em>This will not be saved anywhere.</em>';
3291	$txt['ftp_password'] = 'Password';
3292	$txt['ftp_password_info'] = 'The password to login with. <em>This will not be saved anywhere.</em>';
3293	$txt['ftp_path'] = 'Install Path';
3294	$txt['ftp_path_info'] = 'This is the <em>relative</em> path you use in your FTP client <a href="' . $_SERVER['PHP_SELF'] . '?ftphelp" onclick="window.open(this.href, \'\', \'width=450,height=250\');return false;" target="_blank">(more help)</a>.';
3295	$txt['ftp_path_found_info'] = 'The path in the box above was automatically detected.';
3296	$txt['ftp_path_help'] = 'Your FTP path is the path you see when you log in to your FTP client.  It commonly starts with &quot;<tt>www</tt>&quot;, &quot;<tt>public_html</tt>&quot;, or &quot;<tt>httpdocs</tt>&quot; - but it should include the directory ELKARTE is in too, such as &quot;/public_html/forum&quot;.  It is different from your URL and full path.<br /><br />Files in this path may be overwritten, so make sure it\'s correct.';
3297	$txt['ftp_path_help_close'] = 'Close';
3298	$txt['ftp_connect'] = 'Connect';
3299
3300	// Was it a problem with Windows?
3301	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3302	{
3303		echo '
3304			<div class="error_message">
3305				<div style="color: red;">The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:</div>
3306				<ul style="margin: 2.5ex; font-family: monospace;">
3307				<li>' . implode('</li>
3308				<li>', $upcontext['chmod']['files']). '</li>
3309			</ul>
3310			</div>';
3311
3312		return false;
3313	}
3314
3315	echo '
3316		<div class="panel">
3317			<h2>Your FTP connection information</h2>
3318			<h3>The upgrader can fix any issues with file permissions to make upgrading as simple as possible. Simply enter your connection information below or alternatively click <a href="#" onclick="warning_popup();">here</a> for a list of files which need to be changed.</h3>
3319			<script type="text/javascript"><!-- // --><![CDATA[
3320				function warning_popup()
3321				{
3322					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3323					var content = popup.document;
3324					content.write(\'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n\');
3325					content.write(\'<html xmlns="http://www.w3.org/1999/xhtml"', $upcontext['right_to_left'] ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex" />\n\t\t\');
3326					content.write(\'<title>Warning</title>\n\t\t<link rel="stylesheet" type="text/css" href="', $settings['default_theme_url'], '/css/index.css" />\n\t</head>\n\t<body id="popup">\n\t\t\');
3327					content.write(\'<div class="windowbg description">\n\t\t\t<h4>The following files needs to be made writable to continue:</h4>\n\t\t\t\');
3328					content.write(\'<p>', implode('<br />\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');
3329					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3330					content.close();
3331				}
3332		// ]]></script>';
3333
3334	if (!empty($upcontext['chmod']['ftp_error']))
3335		echo '
3336			<div class="error_message">
3337				<div style="color: red;">
3338					The following error was encountered when trying to connect:<br />
3339					<br />
3340					<code>', $upcontext['chmod']['ftp_error'], '</code>
3341				</div>
3342			</div>
3343			<br />';
3344
3345	if (empty($upcontext['chmod_in_form']))
3346		echo '
3347	<form action="', $upcontext['form_url'], '" method="post">';
3348
3349	echo '
3350		<table width="520" cellspacing="0" cellpadding="0" border="0" align="center" style="margin-bottom: 1ex;">
3351			<tr>
3352				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3353				<td>
3354					<div style="float: right; margin-right: 1px;"><label for="ftp_port" class="textbox"><strong>', $txt['ftp_port'], ':&nbsp;</strong></label> <input type="text" size="3" name="ftp_port" id="ftp_port" value="', isset($upcontext['chmod']['port']) ? $upcontext['chmod']['port'] : '21', '" class="input_text" /></div>
3355					<input type="text" size="30" name="ftp_server" id="ftp_server" value="', isset($upcontext['chmod']['server']) ? $upcontext['chmod']['server'] : 'localhost', '" style="width: 70%;" class="input_text" />
3356					<div style="font-size: smaller; margin-bottom: 2ex;">', $txt['ftp_server_info'], '</div>
3357				</td>
3358			</tr><tr>
3359				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3360				<td>
3361					<input type="text" size="50" name="ftp_username" id="ftp_username" value="', isset($upcontext['chmod']['username']) ? $upcontext['chmod']['username'] : '', '" style="width: 99%;" class="input_text" />
3362					<div style="font-size: smaller; margin-bottom: 2ex;">', $txt['ftp_username_info'], '</div>
3363				</td>
3364			</tr><tr>
3365				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3366				<td>
3367					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password" />
3368					<div style="font-size: smaller; margin-bottom: 3ex;">', $txt['ftp_password_info'], '</div>
3369				</td>
3370			</tr><tr>
3371				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3372				<td style="padding-bottom: 1ex;">
3373					<input type="text" size="50" name="ftp_path" id="ftp_path" value="', isset($upcontext['chmod']['path']) ? $upcontext['chmod']['path'] : '', '" style="width: 99%;" class="input_text" />
3374					<div style="font-size: smaller; margin-bottom: 2ex;">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3375				</td>
3376			</tr>
3377		</table>
3378
3379		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit" /></div>
3380	</div>';
3381
3382	if (empty($upcontext['chmod_in_form']))
3383		echo '
3384	</form>';
3385}
3386
3387function template_upgrade_above()
3388{
3389	global $modSettings, $txt, $oursite, $settings, $upcontext, $upgradeurl;
3390
3391	echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3392<html xmlns="http://www.w3.org/1999/xhtml"', $upcontext['right_to_left'] ? ' dir="rtl"' : '', '>
3393	<head>
3394		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
3395		<meta name="robots" content="noindex" />
3396		<title>', $txt['upgrade_upgrade_utility'], '</title>
3397		<link rel="stylesheet" type="text/css" href="', $settings['default_theme_url'], '/css/index.css?alp21" />
3398		<link rel="stylesheet" type="text/css" href="', $settings['default_theme_url'], '/css/install.css?alp21" />
3399				<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3400		<script type="text/javascript"><!-- // --><![CDATA[
3401			var smf_scripturl = \'', $upgradeurl, '\';
3402			var smf_charset = \'UTF-8\';
3403			var startPercent = ', $upcontext['overall_percent'], ';
3404
3405			// This function dynamically updates the step progress bar - and overall one as required.
3406			function updateStepProgress(current, max, overall_weight)
3407			{
3408				// What out the actual percent.
3409				var width = parseInt((current / max) * 100);
3410				if (document.getElementById(\'step_progress\'))
3411				{
3412					document.getElementById(\'step_progress\').style.width = width + "%";
3413					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3414				}
3415				if (overall_weight && document.getElementById(\'overall_progress\'))
3416				{
3417					overall_width = parseInt(startPercent + width * (overall_weight / 100));
3418					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3419					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3420				}
3421			}
3422		// ]]></script>
3423	</head>
3424	<body>
3425	<div id="header"><div class="frame">
3426		<div id="top_section">
3427			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3428			<img id="logo" src="', $settings['default_theme_url'], '/images/logo.png" alt="Elkarte Community" title="Elkarte Community" />
3429		</div>
3430		<div id="upper_section" class="middletext flow_hidden">
3431			<div class="user"></div>
3432			<div class="news normaltext">
3433			</div>
3434		</div>
3435	</div></div>
3436	<div id="content_section"><div class="frame">
3437		<div id="main_content_section">
3438			<div id="main-steps">
3439				<h2>', $txt['upgrade_progress'], '</h2>
3440				<ul>';
3441
3442	foreach ($upcontext['steps'] as $num => $step)
3443		echo '
3444						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3445
3446	echo '
3447					</ul>
3448			</div>
3449			<div style="float: left; width: 40%;">
3450				<div style="font-size: 8pt; height: 12pt; border: 1px solid black; background-color: white; width: 50%; margin: auto;">
3451					<div id="overall_text" style="color: #000; position: absolute; margin-left: -5em;">', $upcontext['overall_percent'], '%</div>
3452					<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%; height: 12pt; z-index: 1; background-color: lime;">&nbsp;</div>
3453					<div class="progress">', $txt['upgrade_overall_progress'], '</div>
3454				</div>
3455				';
3456
3457	if (isset($upcontext['step_progress']))
3458		echo '
3459				<div style="font-size: 8pt; height: 12pt; border: 1px solid black; background-color: white; width: 50%; margin: 5px auto; ">
3460					<div id="step_text" style="color: #000; position: absolute; margin-left: -5em;">', $upcontext['step_progress'], '%</div>
3461					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%; height: 12pt; z-index: 1; background-color: #ffd000;">&nbsp;</div>
3462					<div class="progress">', $txt['upgrade_step_progress'], '</div>
3463				</div>
3464				';
3465
3466	echo '
3467				<div id="substep_bar_div" class="smalltext" style="display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', ':</div>
3468				<div id="substep_bar_div2" style="font-size: 8pt; height: 12pt; border: 1px solid black; background-color: white; width: 50%; margin: 5px auto; display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">
3469					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3470				<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3471								</div>';
3472
3473	// How long have we been running this?
3474	$elapsed = time() - $upcontext['started'];
3475	$mins = (int) ($elapsed / 60);
3476	$seconds = $elapsed - $mins * 60;
3477	echo '
3478								<div class="smalltext" style="padding: 5px; text-align: center;">', $txt['upgrade_time_elapsed'], ':
3479									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3480								</div>';
3481	echo '
3482			</div>
3483			<div id="main_screen" class="clear">
3484				<h2>', $upcontext['page_title'], '</h2>
3485				<div class="panel">
3486					<div style="max-height: 360px; overflow: auto;">';
3487}
3488
3489function template_upgrade_below()
3490{
3491	global $upcontext, $txt;
3492
3493	if (!empty($upcontext['pause']))
3494		echo '
3495								<em>', $txt['upgrade_incomplete'], '.</em><br />
3496
3497								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3498								<h3>
3499									', $txt['upgrade_paused_overload'], '
3500								</h3>';
3501
3502	if (!empty($upcontext['custom_warning']))
3503		echo '
3504								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3505									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3506									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br />
3507									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3508								</div>';
3509
3510	echo '
3511								<div class="righttext" style="margin: 1ex;">';
3512
3513	if (!empty($upcontext['continue']))
3514		echo '
3515									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled="disabled"' : '', ' class="button_submit" />';
3516	if (!empty($upcontext['skip']))
3517		echo '
3518									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit" />';
3519
3520	echo '
3521								</div>
3522							</form>
3523						</div>
3524				</div>
3525			</div>
3526		</div>
3527	</div></div>
3528	<div id="footer_section"><div class="frame" style="height: 40px;">
3529		<div class="smalltext"><a href="http://www.elkarte.net/" title="Elkarte Community" target="_blank" class="new_win">ELKARTE &copy;2011, Elkarte</a></div>
3530	</div></div>
3531	</body>
3532</html>';
3533
3534	// Are we on a pause?
3535	if (!empty($upcontext['pause']))
3536	{
3537		echo '
3538		<script type="text/javascript"><!-- // --><![CDATA[
3539			window.onload = doAutoSubmit;
3540			var countdown = 3;
3541			var dontSubmit = false;
3542
3543			function doAutoSubmit()
3544			{
3545				if (countdown == 0 && !dontSubmit)
3546					document.upform.submit();
3547				else if (countdown == -1)
3548					return;
3549
3550				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3551				countdown--;
3552
3553				setTimeout("doAutoSubmit();", 1000);
3554			}
3555		// ]]></script>';
3556	}
3557}
3558
3559function template_xml_above()
3560{
3561	global $upcontext;
3562
3563	echo '<', '?xml version="1.0" encoding="ISO-8859-1"?', '>
3564	<smf>';
3565
3566	if (!empty($upcontext['get_data']))
3567		foreach ($upcontext['get_data'] as $k => $v)
3568			echo '
3569		<get key="', $k, '">', $v, '</get>';
3570}
3571
3572function template_xml_below()
3573{
3574	global $upcontext;
3575
3576	echo '
3577		</smf>';
3578}
3579
3580function template_error_message()
3581{
3582	global $upcontext;
3583
3584	echo '
3585	<div class="error_message">
3586		<div style="color: red;">
3587			', $upcontext['error_msg'], '
3588		</div>
3589		<br />
3590		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
3591	</div>';
3592}
3593
3594function template_welcome_message()
3595{
3596	global $upcontext, $modSettings, $upgradeurl, $disable_security, $settings, $txt;
3597
3598	echo '
3599		<script type="text/javascript" src="http://www.elkarte.net/current-version.js?version=' . CURRENT_VERSION . '"></script>
3600		<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
3601			<h3>', sprintf($txt['upgrade_ready_proceed'], CURRENT_VERSION), '</h3>
3602	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform" ', empty($upcontext['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $upcontext['rid'] . '\', \'' . (!empty($upcontext['login_token']) ? $upcontext['login_token'] : '') . '\');"' : '', '>
3603		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '" />
3604		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3605			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3606			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br />
3607			<div style="padding-left: 6ex;">
3608				', sprintf($txt['upgrade_warning_out_of_date'], CURRENT_VERSION), '
3609			</div>
3610		</div>';
3611
3612	$upcontext['chmod_in_form'] = true;
3613	template_chmod();
3614
3615	// For large, SMF pre-1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3616	if ($upcontext['is_large_forum'])
3617		echo '
3618		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3619			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3620			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br />
3621			<div style="padding-left: 6ex;">
3622				', $txt['upgrade_warning_lots_data'], '
3623			</div>
3624		</div>';
3625
3626	// A warning message?
3627	if (!empty($upcontext['warning']))
3628		echo '
3629		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3630			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3631			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br />
3632			<div style="padding-left: 6ex;">
3633				', $upcontext['warning'], '
3634			</div>
3635		</div>';
3636
3637	// Paths are incorrect?
3638	echo '
3639		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #804840; color: black; background-color: #fe5a44; ', (file_exists($settings['default_theme_dir'] . '/scripts/script.js') ? 'display: none;' : ''), '" id="js_script_missing_error">
3640			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3641			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br />
3642			<div style="padding-left: 6ex;">
3643				', $txt['upgrade_error_script_js'], '
3644			</div>
3645		</div>';
3646
3647	// Is there someone already doing this?
3648	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3649	{
3650		$ago = time() - $upcontext['started'];
3651		if ($ago < 60)
3652			$ago = $ago . ' seconds';
3653		elseif ($ago < 3600)
3654			$ago = (int) ($ago / 60) . ' minutes';
3655		else
3656			$ago = (int) ($ago / 3600) . ' hours';
3657
3658		$active = time() - $upcontext['updated'];
3659		if ($active < 60)
3660			$updated = $active . ' seconds';
3661		elseif ($active < 3600)
3662			$updated = (int) ($active / 60) . ' minutes';
3663		else
3664			$updated = (int) ($active / 3600) . ' hours';
3665
3666		echo '
3667		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3668			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3669			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br />
3670			<div style="padding-left: 6ex;">
3671				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3672
3673		if ($active < 600)
3674			echo '
3675				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
3676
3677		if ($active > $upcontext['inactive_timeout'])
3678			echo '
3679				<br /><br />You can choose to either run the upgrade again from the beginning - or alternatively continue from the last step reached during the last upgrade.';
3680		else
3681			echo '
3682				<br /><br />This upgrade script cannot be run until ', $upcontext['user']['name'], ' has been inactive for at least ', ($upcontext['inactive_timeout'] > 120 ? round($upcontext['inactive_timeout'] / 60, 1) . ' minutes!' : $upcontext['inactive_timeout'] . ' seconds!');
3683
3684		echo '
3685			</div>
3686		</div>';
3687	}
3688
3689	echo '
3690			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
3691			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
3692			<table>
3693				<tr valign="top">
3694					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
3695					<td>
3696						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '" ', $disable_security ? 'disabled="disabled"' : '', ' class="input_text" />';
3697
3698	if (!empty($upcontext['username_incorrect']))
3699		echo '
3700						<div class="smalltext" style="color: red;">Username Incorrect</div>';
3701
3702	echo '
3703					</td>
3704				</tr>
3705				<tr valign="top">
3706					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
3707					<td>
3708						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled="disabled"' : '', ' class="input_password" />
3709						<input type="hidden" name="hash_passwrd" value="" />';
3710
3711	if (!empty($upcontext['password_failed']))
3712		echo '
3713						<div class="smalltext" style="color: red;">Password Incorrect</div>';
3714
3715	echo '
3716					</td>
3717				</tr>';
3718
3719	// Can they continue?
3720	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3721	{
3722		echo '
3723				<tr>
3724					<td colspan="2">
3725						<label for="cont"><input type="checkbox" id="cont" name="cont" checked="checked" class="input_check" />Continue from step reached during last execution of upgrade script.</label>
3726					</td>
3727				</tr>';
3728	}
3729
3730	echo '
3731			</table><br />
3732			<span class="smalltext">
3733				<strong>Note:</strong> If necessary the above security check can be bypassed for users who may administrate a server but not have admin rights on the forum. In order to bypass the above check simply open &quot;upgrade.php&quot; in a text editor and replace &quot;$disable_security = 0;&quot; with &quot;$disable_security = 1;&quot; and refresh this page.
3734			</span>
3735			<input type="hidden" name="login_attempt" id="login_attempt" value="1" />
3736			<input type="hidden" name="js_works" id="js_works" value="0" />';
3737
3738	// Say we want the continue button!
3739	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3740
3741	// This defines whether javascript is going to work elsewhere :D
3742	echo '
3743		<script type="text/javascript"><!-- // --><![CDATA[
3744			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3745				document.getElementById(\'js_works\').value = 1;
3746
3747			// Latest version?
3748			function ourCurrentVersion()
3749			{
3750				var ourVer, yourVer;
3751
3752				if (!(\'ourVersion\' in window))
3753					return;
3754
3755				window.ourVersion = window.ourVersion.replace(/ELKARTE\s?/g, \'\');
3756
3757				ourVer = document.getElementById(\'ourVersion\');
3758				yourVer = document.getElementById(\'yourVersion\');
3759
3760				setInnerHTML(ourVer, window.ourVersion);
3761
3762				var currentVersion = getInnerHTML(yourVer);
3763				if (currentVersion < window.ourVersion)
3764					document.getElementById(\'version_warning\').style.display = \'\';
3765			}
3766			addLoadEvent(ourCurrentVersion);
3767
3768			// This checks that the script file even exists!
3769			if (typeof(ourSelectText) == \'undefined\')
3770				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3771
3772		// ]]></script>';
3773}
3774
3775function template_upgrade_options()
3776{
3777	global $upcontext, $modSettings, $upgradeurl, $disable_security, $settings, $db_prefix, $mmessage, $mtitle, $db_type;
3778
3779	echo '
3780			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
3781			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3782
3783	// Warning message?
3784	if (!empty($upcontext['upgrade_options_warning']))
3785		echo '
3786		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3787			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3788			<strong style="text-decoration: underline;">Warning!</strong><br />
3789			<div style="padding-left: 4ex;">
3790				', $upcontext['upgrade_options_warning'], '
3791			</div>
3792		</div>';
3793
3794	echo '
3795				<table cellpadding="1" cellspacing="0">
3796					<tr valign="top">
3797						<td width="2%">
3798							<input type="checkbox" name="backup" id="backup" value="1"', $db_type != 'mysql' && $db_type != 'postgresql' ? ' disabled="disabled"' : '', ' class="input_check" />
3799						</td>
3800						<td width="100%">
3801							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label>', isset($modSettings['elkVersion']) ? '' : ' (recommended!)', '
3802						</td>
3803					</tr>
3804					<tr valign="top">
3805						<td width="2%">
3806							<input type="checkbox" name="maint" id="maint" value="1" checked="checked" class="input_check" />
3807						</td>
3808						<td width="100%">
3809							<label for="maint">Put the forum into maintenance mode during upgrade.</label> <span class="smalltext">(<a href="#" onclick="document.getElementById(\'mainmess\').style.display = document.getElementById(\'mainmess\').style.display == \'\' ? \'none\' : \'\'">Customize</a>)</span>
3810							<div id="mainmess" style="display: none;">
3811								<strong class="smalltext">Maintenance Title: </strong><br />
3812								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text" /><br />
3813								<strong class="smalltext">Maintenance Message: </strong><br />
3814								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3815							</div>
3816						</td>
3817					</tr>
3818					<tr valign="top">
3819						<td width="2%">
3820							<input type="checkbox" name="debug" id="debug" value="1" class="input_check" />
3821						</td>
3822						<td width="100%">
3823							<label for="debug">Output extra debugging information</label>
3824						</td>
3825					</tr>
3826					<tr valign="top">
3827						<td width="2%">
3828							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check" />
3829						</td>
3830						<td width="100%">
3831							<label for="empty_error">Empty error log before upgrading</label>
3832						</td>
3833					</tr>
3834				</table>
3835				<input type="hidden" name="upcont" value="1" />';
3836
3837	// We need a normal continue button here!
3838	$upcontext['continue'] = 1;
3839}
3840
3841// Template for the database backup tool/
3842function template_backup_database()
3843{
3844	global $upcontext, $modSettings, $upgradeurl, $disable_security, $settings, $support_js, $is_debug;
3845
3846	echo '
3847			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
3848
3849	echo '
3850			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3851			<input type="hidden" name="backup_done" id="backup_done" value="0" />
3852			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3853			<span id="debuginfo"></span>';
3854
3855	// Dont any tables so far?
3856	if (!empty($upcontext['previous_tables']))
3857		foreach ($upcontext['previous_tables'] as $table)
3858			echo '
3859			<br />Completed Table: &quot;', $table, '&quot;.';
3860
3861	echo '
3862			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3863			<br /><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Backup Complete! Click Continue to Proceed.</span>';
3864
3865	// Continue please!
3866	$upcontext['continue'] = $support_js ? 2 : 1;
3867
3868	// If javascript allows we want to do this using XML.
3869	if ($support_js)
3870	{
3871		echo '
3872		<script type="text/javascript"><!-- // --><![CDATA[
3873			var lastTable = ', $upcontext['cur_table_num'], ';
3874			function getNextTables()
3875			{
3876				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3877			}
3878
3879			// Got an update!
3880			function onBackupUpdate(oXMLDoc)
3881			{
3882				var sCurrentTableName = "";
3883				var iTableNum = 0;
3884				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3885				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3886					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3887				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3888
3889				// Update the page.
3890				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3891				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3892				lastTable = iTableNum;
3893				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3894
3895		// If debug flood the screen.
3896		if ($is_debug)
3897			echo '
3898				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br />Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');';
3899
3900		echo '
3901				// Get the next update...
3902				if (iTableNum == ', $upcontext['table_count'], ')
3903				{
3904					document.getElementById(\'commess\').style.display = "";
3905					document.getElementById(\'current_tab_div\').style.display = "none";
3906					document.getElementById(\'contbutt\').disabled = 0;
3907					document.getElementById(\'backup_done\').value = 1;
3908				}
3909				else
3910					getNextTables();
3911			}
3912			getNextTables();
3913		// ]]></script>';
3914	}
3915}
3916
3917function template_backup_xml()
3918{
3919	global $upcontext, $settings, $options, $txt;
3920
3921	echo '
3922	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3923}
3924
3925// Here is the actual "make the changes" template!
3926function template_database_changes()
3927{
3928	global $upcontext, $modSettings, $upgradeurl, $disable_security, $settings, $support_js, $is_debug, $timeLimitThreshold;
3929
3930	echo '
3931		<h3>Executing database changes</h3>
3932		<h4 style="font-style: italic;">Please be patient - this may take some time on large forums. The time elapsed increments from the server to show progress is being made!</h4>';
3933
3934	echo '
3935		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3936		<input type="hidden" name="database_done" id="database_done" value="0" />';
3937
3938	// No javascript looks rubbish!
3939	if (!$support_js)
3940	{
3941		foreach ($upcontext['actioned_items'] as $num => $item)
3942		{
3943			if ($num != 0)
3944				echo ' Successful!';
3945			echo '<br />' . $item;
3946		}
3947		if (!empty($upcontext['changes_complete']))
3948			echo ' Successful!<br /><br /><span id="commess" style="font-weight: bold;">Database Updates Complete! Click Continue to Proceed.</span><br />';
3949	}
3950	else
3951	{
3952		// Tell them how many files we have in total.
3953		if ($upcontext['file_count'] > 1)
3954			echo '
3955		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3956
3957		echo '
3958		<h3 id="info2"><strong>Executing:</strong> &quot;<span id="cur_item_name">', $upcontext['current_item_name'], '</span>&quot; (<span id="item_num">', $upcontext['current_item_num'], '</span> of <span id="total_items"><span id="item_count">', $upcontext['total_items'], '</span>', $upcontext['file_count'] > 1 ? ' - of this script' : '', ')</span></h3>
3959		<br /><span id="commess" style="font-weight: bold; display: ', !empty($upcontext['changes_complete']) || $upcontext['current_debug_item_num'] == $upcontext['debug_items'] ? 'inline' : 'none', ';">Database Updates Complete! Click Continue to Proceed.</span>';
3960
3961		if ($is_debug)
3962		{
3963			echo '
3964			<div id="debug_section" style="height: 200px; overflow: auto;">
3965			<span id="debuginfo"></span>
3966			</div>';
3967		}
3968	}
3969
3970	// Place for the XML error message.
3971	echo '
3972		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
3973			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3974			<strong style="text-decoration: underline;">Error!</strong><br />
3975			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
3976		</div>';
3977
3978	// We want to continue at some point!
3979	$upcontext['continue'] = $support_js ? 2 : 1;
3980
3981	// If javascript allows we want to do this using XML.
3982	if ($support_js)
3983	{
3984		echo '
3985		<script type="text/javascript"><!-- // --><![CDATA[
3986			var lastItem = ', $upcontext['current_debug_item_num'], ';
3987			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
3988			var iLastSubStepProgress = -1;
3989			var curFile = ', $upcontext['cur_file_num'], ';
3990			var totalItems = 0;
3991			var prevFile = 0;
3992			var retryCount = 0;
3993			var testvar = 0;
3994			var timeOutID = 0;
3995			var getData = "";
3996			var debugItems = ', $upcontext['debug_items'], ';
3997			function getNextItem()
3998			{
3999				// We want to track this...
4000				if (timeOutID)
4001					clearTimeout(timeOutID);
4002				timeOutID = window.setTimeout("retTimeout()", ', (10 * $timeLimitThreshold), '000);
4003
4004				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4005			}
4006
4007			// Got an update!
4008			function onItemUpdate(oXMLDoc)
4009			{
4010				var sItemName = "";
4011				var sDebugName = "";
4012				var iItemNum = 0;
4013				var iSubStepProgress = -1;
4014				var iDebugNum = 0;
4015				var bIsComplete = 0;
4016				getData = "";
4017
4018				// We\'ve got something - so reset the timeout!
4019				if (timeOutID)
4020					clearTimeout(timeOutID);
4021
4022				// Assume no error at this time...
4023				document.getElementById("error_block").style.display = "none";
4024
4025				// Are we getting some duff info?
4026				if (!oXMLDoc.getElementsByTagName("item")[0])
4027				{
4028					// Too many errors?
4029					if (retryCount > 15)
4030					{
4031						document.getElementById("error_block").style.display = "";
4032						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4033
4034	if ($is_debug)
4035		echo '
4036						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4037
4038	echo '
4039					}
4040					else
4041					{
4042						retryCount++;
4043						getNextItem();
4044					}
4045					return false;
4046				}
4047
4048				// Never allow loops.
4049				if (curFile == prevFile)
4050				{
4051					retryCount++;
4052					if (retryCount > 10)
4053					{
4054						document.getElementById("error_block").style.display = "";
4055						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4056
4057	if ($is_debug)
4058		echo '
4059						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4060
4061	echo '
4062					}
4063				}
4064				retryCount = 0;
4065
4066				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4067					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4068				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4069					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4070				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4071				{
4072					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4073					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4074					{
4075						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4076					}
4077				}
4078
4079				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4080				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4081				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4082				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4083				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4084
4085				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4086				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4087				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4088
4089				// If we have an error we haven\'t completed!
4090				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4091					iDebugNum = lastItem;
4092
4093				// Do we have the additional progress bar?
4094				if (iSubStepProgress != -1)
4095				{
4096					document.getElementById("substep_bar_div").style.display = "";
4097					document.getElementById("substep_bar_div2").style.display = "";
4098					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4099					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4100					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4101				}
4102				else
4103				{
4104					document.getElementById("substep_bar_div").style.display = "none";
4105					document.getElementById("substep_bar_div2").style.display = "none";
4106				}
4107
4108				// Move onto the next item?
4109				if (bIsComplete)
4110					lastItem = iDebugNum;
4111				else
4112					lastItem = iDebugNum - 1;
4113
4114				// Are we finished?
4115				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4116				{';
4117
4118		if ($is_debug)
4119			echo '
4120					document.getElementById(\'debug_section\').style.display = "none";';
4121
4122		echo '
4123
4124					document.getElementById(\'commess\').style.display = "";
4125					document.getElementById(\'contbutt\').disabled = 0;
4126					document.getElementById(\'database_done\').value = 1;';
4127
4128		if ($upcontext['file_count'] > 1)
4129			echo '
4130					document.getElementById(\'info1\').style.display = "none";';
4131
4132		echo '
4133					document.getElementById(\'info2\').style.display = "none";
4134					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4135					return true;
4136				}
4137				// Was it the last step in the file?
4138				else if (bIsComplete && iDebugNum == -1)
4139				{
4140					lastItem = 0;
4141					prevFile = curFile;';
4142
4143		if ($is_debug)
4144			echo '
4145					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br /><span id="debuginfo"><\' + \'/span>\');';
4146
4147		echo '
4148					getNextItem();
4149					return true;
4150				}';
4151
4152		// If debug scroll the screen.
4153		if ($is_debug)
4154			echo '
4155				if (iLastSubStepProgress == -1)
4156				{
4157					// Give it consistent dots.
4158					dots = sDebugName.match(/\./g);
4159					numDots = dots ? dots.length : 0;
4160					for (var i = numDots; i < 3; i++)
4161						sDebugName += ".";
4162					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4163				}
4164				iLastSubStepProgress = iSubStepProgress;
4165
4166				if (bIsComplete)
4167					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br /><span id="debuginfo"><\' + \'/span>\');
4168				else
4169					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4170
4171				if (document.getElementById(\'debug_section\').scrollHeight)
4172					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4173
4174		echo '
4175				// Update the page.
4176				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4177				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4178
4179		if ($upcontext['file_count'] > 1)
4180		{
4181			echo '
4182				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4183				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4184		}
4185
4186		echo '
4187				// Is there an error?
4188				if (oXMLDoc.getElementsByTagName("error")[0])
4189				{
4190					var sErrorMsg = "";
4191					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4192						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4193					document.getElementById("error_block").style.display = "";
4194					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4195					return false;
4196				}
4197
4198				// Get the progress bar right.
4199				barTotal = debugItems * ', $upcontext['file_count'], ';
4200				barDone = (debugItems * (curFile - 1)) + lastItem;
4201
4202				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4203
4204				// Finally - update the time here as it shows the server is responding!
4205				curTime = new Date();
4206				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4207				mins = parseInt(iElapsed / 60);
4208				secs = parseInt(iElapsed - mins * 60);
4209				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4210				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4211
4212				getNextItem();
4213				return true;
4214			}
4215
4216			// What if we timeout?!
4217			function retTimeout(attemptAgain)
4218			{
4219				// Oh noes...
4220				if (!attemptAgain)
4221				{
4222					document.getElementById("error_block").style.display = "";
4223					setInnerHTML(document.getElementById("error_message"), "Server has not responded for ', ($timeLimitThreshold * 10), ' seconds. It may be worth waiting a little longer or otherwise please click <a href=\"#\" onclick=\"retTimeout(true); return false;\">here<" + "/a> to try this step again");
4224				}
4225				else
4226				{
4227					document.getElementById("error_block").style.display = "none";
4228					getNextItem();
4229				}
4230			}';
4231
4232		// Start things off assuming we've not errored.
4233		if (empty($upcontext['error_message']))
4234			echo '
4235			getNextItem();';
4236
4237		echo '
4238		// ]]></script>';
4239	}
4240	return;
4241}
4242
4243function template_database_xml()
4244{
4245	global $upcontext, $settings, $options, $txt;
4246
4247	echo '
4248	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4249	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4250	<debug num="', $upcontext['current_debug_item_num'], '" percent="', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '-1', '" complete="', empty($upcontext['completed_step']) ? 0 : 1, '">', $upcontext['current_debug_item_name'], '</debug>';
4251
4252	if (!empty($upcontext['error_message']))
4253		echo '
4254	<error>', $upcontext['error_message'], '</error>';
4255}
4256
4257function template_clean_mods()
4258{
4259	global $upcontext, $modSettings, $upgradeurl, $disable_security, $settings, $db_prefix, $boardurl;
4260
4261	$upcontext['chmod_in_form'] = true;
4262
4263	echo '
4264	<h3>ELKARTE has detected some packages which were installed but not fully removed prior to upgrade. We recommend you remove the following mods and reinstall upon completion of the upgrade.</h3>
4265	<form action="', $upcontext['form_url'], '&amp;ssi=1" name="upform" id="upform" method="post">';
4266
4267	// In case it's required.
4268	template_chmod();
4269
4270	echo '
4271		<table width="90%" align="center" cellspacing="1" cellpadding="2" style="background-color: black;">
4272			<tr style="background-color: #eeeeee;">
4273				<td width="40%"><strong>Modification Name</strong></td>
4274				<td width="10%" align="center"><strong>Version</strong></td>
4275				<td width="15%"><strong>Files Affected</strong></td>
4276				<td width="20%"><strong>Status</strong></td>
4277				<td width="5%" align="center"><strong>Fix?</strong></td>
4278			</tr>';
4279
4280	foreach ($upcontext['packages'] as $package)
4281	{
4282		echo '
4283			<tr style="background-color: #cccccc;">
4284				<td width="40%">', $package['name'], '</td>
4285				<td width="10%">', $package['version'], '</td>
4286				<td width="15%">', $package['file_count'], ' <span class="smalltext">[<a href="#" onclick="alert(\'The following files are affected by this modification:\\n\\n', strtr(implode('<br />', $package['files']), array('\\' => '\\\\', '<br />' => '\\n')), '\'); return false;">details</a>]</td>
4287				<td width="20%"><span style="font-weight: bold; color: ', $package['color'], '">', $package['status'], '</span></td>
4288				<td width="5%" align="center">
4289					<input type="hidden" name="remove[', $package['id'], ']" value="0" />
4290					<input type="checkbox" name="remove[', $package['id'], ']"', $package['color'] == 'green' ? ' disabled="disabled"' : '', ' class="input_check" />
4291				</td>
4292			</tr>';
4293	}
4294	echo '
4295		</table>
4296		<input type="hidden" name="cleandone" value="1" />';
4297
4298	// Files to make writable?
4299	if (!empty($upcontext['writable_files']))
4300		echo '
4301		<input type="hidden" name="writable_files" value="', base64_encode(serialize($upcontext['writable_files'])), '" />';
4302
4303	// We'll want a continue button...
4304	if (empty($upcontext['chmod']['files']))
4305		$upcontext['continue'] = 1;
4306}
4307
4308// Finished with the mods - let them know what we've done.
4309function template_cleanup_done()
4310{
4311	global $upcontext, $modSettings, $upgradeurl, $disable_security, $settings, $db_prefix, $boardurl;
4312
4313	echo '
4314	<h3>ELKARTE has attempted to fix and reinstall mods as required. We recommend you visit the package manager upon completing upgrade to check the status of your modifications.</h3>
4315	<form action="', $upcontext['form_url'], '&amp;ssi=1" name="upform" id="upform" method="post">
4316		<table width="90%" align="center" cellspacing="1" cellpadding="2" style="background-color: black;">
4317			<tr style="background-color: #eeeeee;">
4318				<td width="100%"><strong>Actions Completed:</strong></td>
4319			</tr>';
4320
4321	foreach ($upcontext['packages'] as $package)
4322	{
4323		echo '
4324			<tr style="background-color: #cccccc;">
4325				<td>', $package['name'], '... <span style="font-weight: bold; color: ', $package['color'], ';">', $package['result'], '</span></td>
4326			</tr>';
4327	}
4328	echo '
4329		</table>
4330		<input type="hidden" name="cleandone2" value="1" />';
4331
4332	// We'll want a continue button...
4333	$upcontext['continue'] = 1;
4334}
4335
4336// Do they want to upgrade their templates?
4337function template_upgrade_templates()
4338{
4339	global $upcontext, $modSettings, $upgradeurl, $disable_security, $settings, $db_prefix, $boardurl;
4340
4341	echo '
4342	<h3>There have been numerous language and template changes since the previous version of ELKARTE. On this step the upgrader can attempt to automatically make these changes in your templates to save you from doing so manually.</h3>
4343	<form action="', $upcontext['form_url'], '&amp;ssi=1', $upcontext['is_test'] ? '' : ';forreal=1', '" name="upform" id="upform" method="post">';
4344
4345	// Any files need to be writable?
4346	$upcontext['chmod_in_form'] = true;
4347	template_chmod();
4348
4349	// Language/Template files need an update?
4350	if ($upcontext['temp_progress'] == 0 && !$upcontext['is_test'] && (!empty($upcontext['languages']) || !empty($upcontext['themes'])))
4351	{
4352		echo '
4353		The following template files will be updated to ensure they are compatible with this version of ELKARTE. Note that this can only fix a limited number of compatibility issues and in general you should seek out the latest version of these themes/language files.
4354		<table width="90%" align="center" cellspacing="1" cellpadding="2" style="background-color: black;">
4355			<tr style="background-color: #eeeeee;">
4356				<td width="80%"><strong>Area</strong></td>
4357				<td width="20%" align="center"><strong>Changes Required</strong></td>
4358			</tr>';
4359
4360		foreach ($upcontext['languages'] as $language)
4361		{
4362			echo '
4363				<tr style="background-color: #cccccc;">
4364					<td width="80%">
4365						&quot;', $language['name'], '&quot; Language Pack
4366						<div class="smalltext">(';
4367
4368			foreach ($language['files'] as $k => $file)
4369				echo $file['name'], $k + 1 != count($language['files']) ? ', ' : ')';
4370
4371			echo '
4372						</div>
4373					</td>
4374					<td width="20%" align="center">', $language['edit_count'] == 0 ? 1 : $language['edit_count'], '</td>
4375				</tr>';
4376		}
4377
4378		foreach ($upcontext['themes'] as $theme)
4379		{
4380			echo '
4381				<tr style="background-color: #CCCCCC;">
4382					<td width="80%">
4383						&quot;', $theme['name'], '&quot; Theme
4384						<div class="smalltext">(';
4385
4386			foreach ($theme['files'] as $k => $file)
4387				echo $file['name'], $k + 1 != count($theme['files']) ? ', ' : ')';
4388
4389			echo '
4390						</div>
4391					</td>
4392					<td width="20%" align="center">', $theme['edit_count'] == 0 ? 1 : $theme['edit_count'], '</td>
4393				</tr>';
4394		}
4395
4396		echo '
4397		</table>';
4398	}
4399	else
4400	{
4401		$langFiles = 0;
4402		$themeFiles = 0;
4403		if (!empty($upcontext['languages']))
4404			foreach ($upcontext['languages'] as $lang)
4405				$langFiles += count($lang['files']);
4406		if (!empty($upcontext['themes']))
4407			foreach ($upcontext['themes'] as $theme)
4408				$themeFiles += count($theme['files']);
4409		echo sprintf('Found <strong>%d</strong> language files and <strong>%d</strong> templates requiring an update so far.', $langFiles, $themeFiles) . '<br />';
4410
4411		// What we're currently doing?
4412		if (!empty($upcontext['current_message']))
4413			echo '
4414				', $upcontext['current_message'];
4415	}
4416
4417	echo '
4418		<input type="hidden" name="uptempdone" value="1" />';
4419
4420	if (!empty($upcontext['languages']))
4421		echo '
4422		<input type="hidden" name="languages" value="', base64_encode(serialize($upcontext['languages'])), '" />';
4423	if (!empty($upcontext['themes']))
4424		echo '
4425		<input type="hidden" name="themes" value="', base64_encode(serialize($upcontext['themes'])), '" />';
4426	if (!empty($upcontext['writable_files']))
4427		echo '
4428		<input type="hidden" name="writable_files" value="', base64_encode(serialize($upcontext['writable_files'])), '" />';
4429
4430	// Offer them the option to upgrade from YaBB SE?
4431	if (!empty($upcontext['can_upgrade_yabbse']))
4432		echo '
4433		<br /><label for="conv"><input type="checkbox" name="conv" id="conv" value="1" class="input_check" /> Convert the existing YaBB SE template and set it as default.</label><br />';
4434
4435	// We'll want a continue button... assuming chmod is OK (Otherwise let them use connect!)
4436	if (empty($upcontext['chmod']['files']) || $upcontext['is_test'])
4437		$upcontext['continue'] = 1;
4438}
4439
4440function template_upgrade_complete()
4441{
4442	global $upcontext, $modSettings, $upgradeurl, $disable_security, $settings, $db_prefix, $boardurl;
4443
4444	echo '
4445	<h3>That wasn\'t so hard, was it?  Now you are ready to use <a href="', $boardurl, '/index.php">your installation of ELKARTE</a>.  Hope you like it!</h3>
4446	<form action="', $boardurl, '/index.php">';
4447
4448	if (!empty($upcontext['can_delete_script']))
4449		echo '
4450			<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete(this);" class="input_check" /> Delete this upgrade.php and its data files now.</label> <em>(doesn\'t work on all servers.)</em>
4451			<script type="text/javascript"><!-- // --><![CDATA[
4452				function doTheDelete(theCheck)
4453				{
4454					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4455
4456					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4457					theCheck.disabled = true;
4458				}
4459			// ]]></script>
4460			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader" /><br />';
4461
4462	echo '<br />
4463			If you had any problems with this upgrade, or have any problems using Elkarte, please don\'t hesitate to <a href="http://www.elkarte.net/index.php">look to us for assistance</a>.<br />
4464			<br />
4465			Best of luck,<br />
4466			Elkarte';
4467}