Completed
Push — release-2.1 ( 15f485...ae1be0 )
by Mathias
08:27
created

other/upgrade.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2017 Simple Machines and individual contributors
9
 * @license http://www.simplemachines.org/about/smf/license.php BSD
10
 *
11
 * @version 2.1 Beta 3
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 Beta 3');
16
define('SMF_LANG_VERSION', '2.1 Beta 3');
17
18
/**
19
 * The minimum required PHP version.
20
 * @var string
21
 */
22
$GLOBALS['required_php_version'] = '5.4.45';
23
24
/**
25
 * A list of supported database systems.
26
 * @var array
27
 */
28
$databases = array(
29
	'mysql' => array(
30
		'name' => 'MySQL',
31
		'version' => '5.0.3',
32
		'version_check' => 'global $db_connection; return min(mysqli_get_server_info($db_connection), mysqli_get_client_info());',
33
		'utf8_support' => true,
34
		'utf8_version' => '5.0.3',
35
		'utf8_version_check' => 'global $db_connection; return mysqli_get_server_info($db_connection);',
36
		'alter_support' => true,
37
	),
38
	'postgresql' => array(
39
		'name' => 'PostgreSQL',
40
		'version' => '9.1',
41
		'version_check' => '$version = pg_version(); return $version[\'client\'];',
42
		'always_has_db' => true,
43
	),
44
);
45
46
/**
47
 * The maximum time a single substep may take, in seconds.
48
 * @var int
49
 */
50
$timeLimitThreshold = 30;
51
52
/**
53
 * The current path to the upgrade.php file.
54
 * @var string
55
 */
56
$upgrade_path = dirname(__FILE__);
57
58
/**
59
 * The URL of the current page.
60
 * @var string
61
 */
62
$upgradeurl = $_SERVER['PHP_SELF'];
63
64
/**
65
 * Flag to disable the required administrator login.
66
 * @var bool
67
 */
68
$disable_security = false;
69
70
/**
71
 * The amount of seconds allowed between logins.
72
 * If the first user to login is inactive for this amount of seconds, a second login is allowed.
73
 * @var int
74
 */
75
$upcontext['inactive_timeout'] = 10;
76
77
// The helper is crucial. Include it first thing.
78
if (!file_exists($upgrade_path . '/upgrade-helper.php'))
79
    die('upgrade-helper.php not found where it was expected: ' . $upgrade_path . '/upgrade-helper.php! Make sure you have uploaded ALL files from the upgrade package. The upgrader cannot continue.');
80
81
require_once($upgrade_path . '/upgrade-helper.php');
82
83
// All the steps in detail.
84
// Number,Name,Function,Progress Weight.
85
$upcontext['steps'] = array(
86
	0 => array(1, 'Login', 'WelcomeLogin', 2),
87
	1 => array(2, 'Upgrade Options', 'UpgradeOptions', 2),
88
	2 => array(3, 'Backup', 'BackupDatabase', 10),
89
	3 => array(4, 'Database Changes', 'DatabaseChanges', 50),
90
	4 => array(5, 'Convert to UTF-8', 'ConvertUtf8', 20),
91
	5 => array(6, 'Convert serialized strings to JSON', 'serialize_to_json', 10),
92
	6 => array(7, 'Delete Upgrade.php', 'DeleteUpgrade', 1),
93
);
94
// Just to remember which one has files in it.
95
$upcontext['database_step'] = 3;
96
@set_time_limit(600);
97
if (!ini_get('safe_mode'))
98
{
99
	ini_set('mysql.connect_timeout', -1);
100
	ini_set('default_socket_timeout', 900);
101
}
102
// Clean the upgrade path if this is from the client.
103
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
104
	for ($i = 1; $i < $_SERVER['argc']; $i++)
105
	{
106
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
107
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
108
	}
109
110
// Are we from the client?
111
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
112
{
113
	$command_line = true;
114
	$disable_security = true;
115
}
116
else
117
	$command_line = false;
118
119
// Load this now just because we can.
120
require_once($upgrade_path . '/Settings.php');
121
122
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
123
if (isset($language))
124
	$language = str_ireplace('-utf8', '', $language);
125
126
// Are we logged in?
127
if (isset($upgradeData))
128
{
129
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
130
131
	// Check for sensible values.
132 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
133
		$upcontext['user']['started'] = time();
134 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
135
		$upcontext['user']['updated'] = 0;
136
137
	$upcontext['started'] = $upcontext['user']['started'];
138
	$upcontext['updated'] = $upcontext['user']['updated'];
139
140
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
141
}
142
143
// Nothing sensible?
144
if (empty($upcontext['updated']))
145
{
146
	$upcontext['started'] = time();
147
	$upcontext['updated'] = 0;
148
	$upcontext['user'] = array(
149
		'id' => 0,
150
		'name' => 'Guest',
151
		'pass' => 0,
152
		'started' => $upcontext['started'],
153
		'updated' => $upcontext['updated'],
154
	);
155
}
156
157
// Load up some essential data...
158
loadEssentialData();
159
160
// Are we going to be mimic'ing SSI at this point?
161
if (isset($_GET['ssi']))
162
{
163
	require_once($sourcedir . '/Errors.php');
164
	require_once($sourcedir . '/Logging.php');
165
	require_once($sourcedir . '/Load.php');
166
	require_once($sourcedir . '/Security.php');
167
	require_once($sourcedir . '/Subs-Package.php');
168
169
	loadUserSettings();
170
	loadPermissions();
171
}
172
173
// Include our helper functions.
174
require_once($sourcedir . '/Subs.php');
175
require_once($sourcedir . '/LogInOut.php');
176
177
// This only exists if we're on SMF ;)
178
if (isset($modSettings['smfVersion']))
179
{
180
	$request = $smcFunc['db_query']('', '
181
		SELECT variable, value
182
		FROM {db_prefix}themes
183
		WHERE id_theme = {int:id_theme}
184
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
185
		array(
186
			'id_theme' => 1,
187
			'theme_url' => 'theme_url',
188
			'theme_dir' => 'theme_dir',
189
			'images_url' => 'images_url',
190
			'db_error_skip' => true,
191
		)
192
	);
193 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
194
		$modSettings[$row['variable']] = $row['value'];
195
	$smcFunc['db_free_result']($request);
196
}
197
198
if (!isset($modSettings['theme_url']))
199
{
200
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
201
	$modSettings['theme_url'] = 'Themes/default';
202
	$modSettings['images_url'] = 'Themes/default/images';
203
}
204
if (!isset($settings['default_theme_url']))
205
	$settings['default_theme_url'] = $modSettings['theme_url'];
206
if (!isset($settings['default_theme_dir']))
207
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
208
209
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
210
// Default title...
211
$upcontext['page_title'] = 'Updating Your SMF Installation!';
212
213
// Have we got tracking data - if so use it (It will be clean!)
214
if (isset($_GET['data']))
215
{
216
	global $is_debug;
217
218
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
219
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
220
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
221
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
222
	$support_js = $upcontext['upgrade_status']['js'];
223
224
	// Only set this if the upgrader status says so.
225
	if (empty($is_debug))
226
		$is_debug = $upcontext['upgrade_status']['debug'];
227
228
	// Load the language.
229 View Code Duplication
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
230
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
231
}
232
// Set the defaults.
233
else
234
{
235
	$upcontext['current_step'] = 0;
236
	$upcontext['rid'] = mt_rand(0, 5000);
237
	$upcontext['upgrade_status'] = array(
238
		'curstep' => 0,
239
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
240
		'rid' => $upcontext['rid'],
241
		'pass' => 0,
242
		'debug' => 0,
243
		'js' => 0,
244
	);
245
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
246
}
247
248
// If this isn't the first stage see whether they are logging in and resuming.
249
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
250
	checkLogin();
251
252
if ($command_line)
253
	cmdStep0();
254
255
// Don't error if we're using xml.
256
if (isset($_GET['xml']))
257
	$upcontext['return_error'] = true;
258
259
// Loop through all the steps doing each one as required.
260
$upcontext['overall_percent'] = 0;
261
foreach ($upcontext['steps'] as $num => $step)
262
{
263
	if ($num >= $upcontext['current_step'])
264
	{
265
		// The current weight of this step in terms of overall progress.
266
		$upcontext['step_weight'] = $step[3];
267
		// Make sure we reset the skip button.
268
		$upcontext['skip'] = false;
269
270
		// We cannot proceed if we're not logged in.
271
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
272
		{
273
			$upcontext['steps'][0][2]();
274
			break;
275
		}
276
277
		// Call the step and if it returns false that means pause!
278
		if (function_exists($step[2]) && $step[2]() === false)
279
			break;
280
		elseif (function_exists($step[2])) {
281
			//Start each new step with this unset, so the 'normal' template is called first
282
			unset($_GET['xml']);
283
			$_GET['substep'] = 0;
284
			$upcontext['current_step']++;
285
		}
286
	}
287
	$upcontext['overall_percent'] += $step[3];
288
}
289
290
upgradeExit();
291
292
// Exit the upgrade script.
293
function upgradeExit($fallThrough = false)
294
{
295
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug;
296
297
	// Save where we are...
298
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
299
	{
300
		$upcontext['user']['step'] = $upcontext['current_step'];
301
		$upcontext['user']['substep'] = $_GET['substep'];
302
		$upcontext['user']['updated'] = time();
303
		$upcontext['debug'] = $is_debug;
304
		$upgradeData = base64_encode(json_encode($upcontext['user']));
305
		require_once($sourcedir . '/Subs-Admin.php');
306
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
307
		updateDbLastError(0);
308
	}
309
310
	// Handle the progress of the step, if any.
311
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
312
	{
313
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
314
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
315
	}
316
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
317
318
	// We usually dump our templates out.
319
	if (!$fallThrough)
320
	{
321
		// This should not happen my dear... HELP ME DEVELOPERS!!
322
		if (!empty($command_line))
323
		{
324
			if (function_exists('debug_print_backtrace'))
325
				debug_print_backtrace();
326
327
			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 SMF support forum to tell the Developers that they\'ve made a boo boo; they\'ll get you up and running again.';
328
			flush();
329
			die();
330
		}
331
332
		if (!isset($_GET['xml']))
333
			template_upgrade_above();
334
		else
335
		{
336
			header('Content-Type: text/xml; charset=UTF-8');
337
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
338
			$upcontext['get_data'] = array();
339
			foreach ($_GET as $k => $v)
340
			{
341
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
342
				{
343
					$upcontext['get_data'][$k] = $v;
344
				}
345
			}
346
			template_xml_above();
347
		}
348
349
		// Call the template.
350
		if (isset($upcontext['sub_template']))
351
		{
352
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
353
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
354
355
			// Custom stuff to pass back?
356
			if (!empty($upcontext['query_string']))
357
				$upcontext['form_url'] .= $upcontext['query_string'];
358
359
			// Call the appropriate subtemplate
360
			if (is_callable('template_' . $upcontext['sub_template']))
361
				call_user_func('template_' . $upcontext['sub_template']);
362
			else
363
				die('Upgrade aborted!  Invalid template: template_' . $upcontext['sub_template']);
364
		}
365
366
		// Was there an error?
367
		if (!empty($upcontext['forced_error_message']))
368
			echo $upcontext['forced_error_message'];
369
370
		// Show the footer.
371
		if (!isset($_GET['xml']))
372
			template_upgrade_below();
373
		else
374
			template_xml_below();
375
	}
376
377
378
	if (!empty($command_line) && $is_debug)
379
	{
380
		$active = time() - $upcontext['started'];
381
		$hours = floor($active / 3600);
382
		$minutes = intval(($active / 60) % 60);
383
		$seconds = intval($active % 60);
384
385
		$totalTime = '';
386
		if ($hours > 0)
387
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
388
		if ($minutes > 0)
389
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
390
		if ($seconds > 0)
391
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
392
393
		if (!empty($totalTime))
394
			echo "\n" . 'Upgrade completed in ' . $totalTime . "\n";
395
	}
396
397
	// Bang - gone!
398
	die();
399
}
400
401
// Used to direct the user to another location.
402
function redirectLocation($location, $addForm = true)
403
{
404
	global $upgradeurl, $upcontext, $command_line;
405
406
	// Command line users can't be redirected.
407
	if ($command_line)
408
		upgradeExit(true);
409
410
	// Are we providing the core info?
411
	if ($addForm)
412
	{
413
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
414
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
415
	}
416
417
	while (@ob_end_clean());
418
	header('Location: ' . strtr($location, array('&amp;' => '&')));
419
420
	// Exit - saving status as we go.
421
	upgradeExit(true);
422
}
423
424
// Load all essential data and connect to the DB as this is pre SSI.php
425
function loadEssentialData()
426
{
427
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type;
428
	global $modSettings, $sourcedir, $smcFunc;
429
430
	error_reporting(E_ALL);
431
	define('SMF', 1);
432
433
	// Start the session.
434
	if (@ini_get('session.save_handler') == 'user')
435
		@ini_set('session.save_handler', 'files');
436
	@session_start();
437
438
	if (empty($smcFunc))
439
		$smcFunc = array();
440
441
	// We need this for authentication and some upgrade code
442
	require_once($sourcedir . '/Subs-Auth.php');
443
	require_once($sourcedir . '/Class-Package.php');
444
445
	$smcFunc['strtolower'] = 'smf_strtolower';
446
447
	// Initialize everything...
448
	initialize_inputs();
449
450
	// Get the database going!
451
	if (empty($db_type) || $db_type == 'mysqli')
452
		$db_type = 'mysql';
453
454
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
455
	{
456
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
457
458
		// Make the connection...
459
		if (empty($db_connection))
460
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('non_fatal' => true));
461
		else
462
			// If we've returned here, ping/reconnect to be safe
463
			$smcFunc['db_ping']($db_connection);
464
465
		// Oh dear god!!
466
		if ($db_connection === null)
467
			die('Unable to connect to database - please check username and password are correct in Settings.php');
468
469
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
470
			$smcFunc['db_query']('', '
471
			SET NAMES {string:db_character_set}',
472
			array(
473
				'db_error_skip' => true,
474
				'db_character_set' => $db_character_set,
475
			)
476
		);
477
478
		// Load the modSettings data...
479
		$request = $smcFunc['db_query']('', '
480
			SELECT variable, value
481
			FROM {db_prefix}settings',
482
			array(
483
				'db_error_skip' => true,
484
			)
485
		);
486
		$modSettings = array();
487 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
488
			$modSettings[$row['variable']] = $row['value'];
489
		$smcFunc['db_free_result']($request);
490
	}
491
	else
492
	{
493
		return throw_error('Cannot find ' . $sourcedir . '/Subs-Db-' . $db_type . '.php' . '. Please check you have uploaded all source files and have the correct paths set.');
494
	}
495
496
	require_once($sourcedir . '/Subs.php');
497
498
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
499
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
500
	{
501
		require_once($sourcedir . '/QueryString.php');
502
		cleanRequest();
503
	}
504
505
	if (!isset($_GET['substep']))
506
		$_GET['substep'] = 0;
507
}
508
509
function initialize_inputs()
510
{
511
	global $start_time, $db_type;
512
513
	$start_time = time();
514
515
	umask(0);
516
517
	ob_start();
518
519
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
520
	ignore_user_abort(true);
521
522
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
523
	if (isset($_GET['delete']))
524
	{
525
		@unlink(__FILE__);
526
527
		// And the extra little files ;).
528
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
529
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
530
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
531
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
532
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
533
534
		$dh = opendir(dirname(__FILE__));
535
		while ($file = readdir($dh))
536
		{
537
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
538
				@unlink(dirname(__FILE__) . '/' . $file);
539
		}
540
		closedir($dh);
541
542
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
543
		// 1.1 Sources files not in 2.0+
544
		@unlink(dirname(__FILE__) . '/Sources/ModSettings.php');
545
		// 1.1 Templates that don't exist any more (e.g. renamed)
546
		@unlink(dirname(__FILE__) . '/Themes/default/Combat.template.php');
547
		@unlink(dirname(__FILE__) . '/Themes/default/Modlog.template.php');
548
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
549
		@unlink(dirname(__FILE__) . '/Themes/default/fader.js');
550
		@unlink(dirname(__FILE__) . '/Themes/default/script.js');
551
		@unlink(dirname(__FILE__) . '/Themes/default/spellcheck.js');
552
		@unlink(dirname(__FILE__) . '/Themes/default/xml_board.js');
553
		@unlink(dirname(__FILE__) . '/Themes/default/xml_topic.js');
554
555
		// 2.0 Sources files not in 2.1+
556
		@unlink(dirname(__FILE__) . '/Sources/DumpDatabase.php');
557
		@unlink(dirname(__FILE__) . '/Sources/LockTopic.php');
558
559
		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');
560
		exit;
561
	}
562
563
	// Something is causing this to happen, and it's annoying.  Stop it.
564
	$temp = 'upgrade_php?step';
565
	while (strlen($temp) > 4)
566
	{
567
		if (isset($_GET[$temp]))
568
			unset($_GET[$temp]);
569
		$temp = substr($temp, 1);
570
	}
571
572
	// Force a step, defaulting to 0.
573
	$_GET['step'] = (int) @$_GET['step'];
574
	$_GET['substep'] = (int) @$_GET['substep'];
575
}
576
577
// Step 0 - Let's welcome them in and ask them to login!
578
function WelcomeLogin()
579
{
580
	global $boarddir, $sourcedir, $modSettings, $cachedir, $upgradeurl, $upcontext;
581
	global $smcFunc, $db_type, $databases, $boardurl;
582
583
	// We global $txt here so that the language files can add to them. This variable is NOT unused.
584
	global $txt;
585
586
	$upcontext['sub_template'] = 'welcome_message';
587
588
	// Check for some key files - one template, one language, and a new and an old source file.
589
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
590
		&& @file_exists($sourcedir . '/QueryString.php')
591
		&& @file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php')
592
		&& @file_exists(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
593
594
	// Need legacy scripts?
595 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.1)
596
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
597 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 2.0)
598
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-1.sql');
599 View Code Duplication
	if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < 1.1)
600
		$check &= @file_exists(dirname(__FILE__) . '/upgrade_1-0.sql');
601
602
	// We don't need "-utf8" files anymore...
603
	$upcontext['language'] = str_ireplace('-utf8', '', $upcontext['language']);
604
605
	// This needs to exist!
606
	if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
607
		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>]');
608
	else
609
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
610
611
	if (!$check)
612
		// 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.
613
		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.');
614
615
	// Do they meet the install requirements?
616
	if (!php_version_check())
617
		return throw_error('Warning!  You do not appear to have a version of PHP installed on your webserver that meets SMF\'s minimum installations requirements.<br><br>Please ask your host to upgrade.');
618
619
	if (!db_version_check())
620
		return throw_error('Your ' . $databases[$db_type]['name'] . ' version does not meet the minimum requirements of SMF.<br><br>Please ask your host to upgrade.');
621
622
	// Do some checks to make sure they have proper privileges
623
	db_extend('packages');
624
625
	// CREATE
626
	$create = $smcFunc['db_create_table']('{db_prefix}priv_check', array(array('name' => 'id_test', 'type' => 'int', 'size' => 10, 'unsigned' => true, 'auto' => true)), array(array('columns' => array('id_test'), 'type' => 'primary')), array(), 'overwrite');
627
628
	// ALTER
629
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'varchar', 'size' => 4, 'null' => false, 'default' => ''));
630
631
	// DROP
632
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
633
634
	// Sorry... we need CREATE, ALTER and DROP
635 View Code Duplication
	if (!$create || !$alter || !$drop)
636
		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.');
637
638
	// Do a quick version spot check.
639
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
640
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
641
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
642
		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.');
643
644
	// What absolutely needs to be writable?
645
	$writable_files = array(
646
		$boarddir . '/Settings.php',
647
		$boarddir . '/Settings_bak.php',
648
		$boarddir . '/db_last_error.php',
649
		$modSettings['theme_dir'] . '/css/minified.css',
650
		$modSettings['theme_dir'] . '/scripts/minified.js',
651
		$modSettings['theme_dir'] . '/scripts/minified_deferred.js',
652
	);
653
654
	// Do we need to add this setting?
655
	$need_settings_update = empty($modSettings['custom_avatar_dir']);
656
657
	$custom_av_dir = !empty($modSettings['custom_avatar_dir']) ? $modSettings['custom_avatar_dir'] : $GLOBALS['boarddir'] . '/custom_avatar';
658
	$custom_av_url = !empty($modSettings['custom_avatar_url']) ? $modSettings['custom_avatar_url'] : $boardurl . '/custom_avatar';
659
660
	// This little fellow has to cooperate...
661
	quickFileWritable($custom_av_dir);
662
663
	// Are we good now?
664
	if (!is_writable($custom_av_dir))
665
		return throw_error(sprintf('The directory: %1$s has to be writable to continue the upgrade. Please make sure permissions are correctly set to allow this.', $custom_av_dir));
666
	elseif ($need_settings_update)
667
	{
668
		if (!function_exists('cache_put_data'))
669
			require_once($sourcedir . '/Load.php');
670
		updateSettings(array('custom_avatar_dir' => $custom_av_dir));
671
		updateSettings(array('custom_avatar_url' => $custom_av_url));
672
	}
673
674
	require_once($sourcedir . '/Security.php');
675
676
	// Check the cache directory.
677
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
678
	if (!file_exists($cachedir_temp))
679
		@mkdir($cachedir_temp);
680
	if (!file_exists($cachedir_temp))
681
		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.');
682
683
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
684
		return throw_error('The upgrader was unable to find language files for the language specified in Settings.php.<br>SMF 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>.');
685
	elseif (!isset($_GET['skiplang']))
686
	{
687
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
688
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
689
690
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
691
			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>]');
692
	}
693
694
	if (!makeFilesWritable($writable_files))
695
		return false;
696
697
	// Check agreement.txt. (it may not exist, in which case $boarddir must be writable.)
698 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
699
		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.');
700
701
	// Upgrade the agreement.
702
	elseif (isset($modSettings['agreement']))
703
	{
704
		$fp = fopen($boarddir . '/agreement.txt', 'w');
705
		fwrite($fp, $modSettings['agreement']);
706
		fclose($fp);
707
	}
708
709
	// We're going to check that their board dir setting is right in case they've been moving stuff around.
710
	if (strtr($boarddir, array('/' => '', '\\' => '')) != strtr(dirname(__FILE__), array('/' => '', '\\' => '')))
711
		$upcontext['warning'] = '
712
			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>
713
			<ul>
714
				<li>Board Directory: ' . $boarddir . '</li>
715
				<li>Source Directory: ' . $boarddir . '</li>
716
				<li>Cache Directory: ' . $cachedir_temp . '</li>
717
			</ul>
718
			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://download.simplemachines.org/?tools">Repair Settings</a> tool from the Simple Machines website before continuing.';
719
720
	// Either we're logged in or we're going to present the login.
721
	if (checkLogin())
722
		return true;
723
724
	$upcontext += createToken('login');
725
726
	return false;
727
}
728
729
// Step 0.5: Does the login work?
730
function checkLogin()
731
{
732
	global $modSettings, $upcontext, $disable_security;
733
	global $smcFunc, $db_type, $support_js;
734
735
	// Don't bother if the security is disabled.
736
	if ($disable_security)
737
		return true;
738
739
	// Are we trying to login?
740
	if (isset($_POST['contbutt']) && (!empty($_POST['user'])))
741
	{
742
		// If we've disabled security pick a suitable name!
743
		if (empty($_POST['user']))
744
			$_POST['user'] = 'Administrator';
745
746
		// Before 2.0 these column names were different!
747
		$oldDB = false;
748
		if (empty($db_type) || $db_type == 'mysql')
749
		{
750
			$request = $smcFunc['db_query']('', '
751
				SHOW COLUMNS
752
				FROM {db_prefix}members
753
				LIKE {string:member_name}',
754
				array(
755
					'member_name' => 'memberName',
756
					'db_error_skip' => true,
757
				)
758
			);
759
			if ($smcFunc['db_num_rows']($request) != 0)
760
				$oldDB = true;
761
			$smcFunc['db_free_result']($request);
762
		}
763
764
		// Get what we believe to be their details.
765
		if (!$disable_security)
766
		{
767
			if ($oldDB)
768
				$request = $smcFunc['db_query']('', '
769
					SELECT id_member, memberName AS member_name, passwd, id_group,
770
					additionalGroups AS additional_groups, lngfile
771
					FROM {db_prefix}members
772
					WHERE memberName = {string:member_name}',
773
					array(
774
						'member_name' => $_POST['user'],
775
						'db_error_skip' => true,
776
					)
777
				);
778
			else
779
				$request = $smcFunc['db_query']('', '
780
					SELECT id_member, member_name, passwd, id_group, additional_groups, lngfile
781
					FROM {db_prefix}members
782
					WHERE member_name = {string:member_name}',
783
					array(
784
						'member_name' => $_POST['user'],
785
						'db_error_skip' => true,
786
					)
787
				);
788
			if ($smcFunc['db_num_rows']($request) != 0)
789
			{
790
				list ($id_member, $name, $password, $id_group, $addGroups, $user_language) = $smcFunc['db_fetch_row']($request);
791
792
				$groups = explode(',', $addGroups);
793
				$groups[] = $id_group;
794
795
				foreach ($groups as $k => $v)
796
					$groups[$k] = (int) $v;
797
798
				$sha_passwd = sha1(strtolower($name) . un_htmlspecialchars($_REQUEST['passwrd']));
799
800
				// We don't use "-utf8" anymore...
801
				$user_language = str_ireplace('-utf8', '', $user_language);
802
			}
803
			else
804
				$upcontext['username_incorrect'] = true;
805
			$smcFunc['db_free_result']($request);
806
		}
807
		$upcontext['username'] = $_POST['user'];
808
809
		// Track whether javascript works!
810
		if (!empty($_POST['js_works']))
811
		{
812
			$upcontext['upgrade_status']['js'] = 1;
813
			$support_js = 1;
814
		}
815
		else
816
			$support_js = 0;
817
818
		// Note down the version we are coming from.
819
		if (!empty($modSettings['smfVersion']) && empty($upcontext['user']['version']))
820
			$upcontext['user']['version'] = $modSettings['smfVersion'];
821
822
		// Didn't get anywhere?
823
		if (!$disable_security && (empty($sha_passwd) || (!empty($password) ? $password : '') != $sha_passwd) && !hash_verify_password((!empty($name) ? $name : ''), $_REQUEST['passwrd'], (!empty($password) ? $password : '')) && empty($upcontext['username_incorrect']))
824
		{
825
			// MD5?
826
			$md5pass = md5_hmac($_REQUEST['passwrd'], strtolower($_POST['user']));
827
			if ($md5pass != $password)
828
			{
829
				$upcontext['password_failed'] = true;
830
				// Disable the hashing this time.
831
				$upcontext['disable_login_hashing'] = true;
832
			}
833
		}
834
835
		if ((empty($upcontext['password_failed']) && !empty($name)) || $disable_security)
836
		{
837
			// Set the password.
838
			if (!$disable_security)
839
			{
840
				// Do we actually have permission?
841
				if (!in_array(1, $groups))
842
				{
843
					$request = $smcFunc['db_query']('', '
844
						SELECT permission
845
						FROM {db_prefix}permissions
846
						WHERE id_group IN ({array_int:groups})
847
							AND permission = {string:admin_forum}',
848
						array(
849
							'groups' => $groups,
850
							'admin_forum' => 'admin_forum',
851
							'db_error_skip' => true,
852
						)
853
					);
854
					if ($smcFunc['db_num_rows']($request) == 0)
855
						return throw_error('You need to be an admin to perform an upgrade!');
856
					$smcFunc['db_free_result']($request);
857
				}
858
859
				$upcontext['user']['id'] = $id_member;
860
				$upcontext['user']['name'] = $name;
861
			}
862
			else
863
			{
864
				$upcontext['user']['id'] = 1;
865
				$upcontext['user']['name'] = 'Administrator';
866
			}
867
			$upcontext['user']['pass'] = mt_rand(0, 60000);
868
			// This basically is used to match the GET variables to Settings.php.
869
			$upcontext['upgrade_status']['pass'] = $upcontext['user']['pass'];
870
871
			// Set the language to that of the user?
872
			if (isset($user_language) && $user_language != $upcontext['language'] && file_exists($modSettings['theme_dir'] . '/languages/index.' . basename($user_language, '.lng') . '.php'))
873
			{
874
				$user_language = basename($user_language, '.lng');
875
				$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $user_language . '.php')), 0, 4096);
876
				preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
877
878
				if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
879
					$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'] . '.';
880
				elseif (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . basename($user_language, '.lng') . '.php'))
881
					$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'] . '.';
882
				else
883
				{
884
					// Set this as the new language.
885
					$upcontext['language'] = $user_language;
886
					$upcontext['upgrade_status']['lang'] = $upcontext['language'];
887
888
					// Include the file.
889
					require_once($modSettings['theme_dir'] . '/languages/Install.' . $user_language . '.php');
890
				}
891
			}
892
893
			// If we're resuming set the step and substep to be correct.
894
			if (isset($_POST['cont']))
895
			{
896
				$upcontext['current_step'] = $upcontext['user']['step'];
897
				$_GET['substep'] = $upcontext['user']['substep'];
898
			}
899
900
			return true;
901
		}
902
	}
903
904
	return false;
905
}
906
907
// Step 1: Do the maintenance and backup.
908
function UpgradeOptions()
909
{
910
	global $db_prefix, $command_line, $modSettings, $is_debug, $smcFunc, $packagesdir, $tasksdir, $language;
911
	global $boarddir, $boardurl, $sourcedir, $maintenance, $cachedir, $upcontext, $db_type, $db_server;
912
913
	$upcontext['sub_template'] = 'upgrade_options';
914
	$upcontext['page_title'] = 'Upgrade Options';
915
916
	db_extend('packages');
917
	$upcontext['karma_installed'] = array('good' => false, 'bad' => false);
918
	$member_columns = $smcFunc['db_list_columns']('{db_prefix}members');
919
920
	$upcontext['karma_installed']['good'] = in_array('karma_good', $member_columns);
921
	$upcontext['karma_installed']['bad'] = in_array('karma_bad', $member_columns);
922
923
	unset($member_columns);
924
925
	// If we've not submitted then we're done.
926
	if (empty($_POST['upcont']))
927
		return false;
928
929
	// Firstly, if they're enabling SM stat collection just do it.
930
	if (!empty($_POST['stats']) && substr($boardurl, 0, 16) != 'http://localhost' && empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']))
931
	{
932
		$upcontext['allow_sm_stats'] = true;
933
934
		// Don't register if we still have a key.
935
		if (empty($modSettings['sm_stats_key']))
936
		{
937
			// Attempt to register the site etc.
938
			$fp = @fsockopen('www.simplemachines.org', 80, $errno, $errstr);
939 View Code Duplication
			if ($fp)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
940
			{
941
				$out = 'GET /smf/stats/register_stats.php?site=' . base64_encode($boardurl) . ' HTTP/1.1' . "\r\n";
942
				$out .= 'Host: www.simplemachines.org' . "\r\n";
943
				$out .= 'Connection: Close' . "\r\n\r\n";
944
				fwrite($fp, $out);
945
946
				$return_data = '';
947
				while (!feof($fp))
948
					$return_data .= fgets($fp, 128);
949
950
				fclose($fp);
951
952
				// Get the unique site ID.
953
				preg_match('~SITE-ID:\s(\w{10})~', $return_data, $ID);
954
955
				if (!empty($ID[1]))
956
					$smcFunc['db_insert']('replace',
957
						$db_prefix . 'settings',
958
						array('variable' => 'string', 'value' => 'string'),
959
						array(
960
							array('sm_stats_key', $ID[1]),
961
							array('enable_sm_stats', 1),
962
						),
963
						array('variable')
964
					);
965
			}
966
		}
967
		else
968
		{
969
			$smcFunc['db_insert']('replace',
970
				$db_prefix . 'settings',
971
				array('variable' => 'string', 'value' => 'string'),
972
				array('enable_sm_stats', 1),
973
				array('variable')
974
			);
975
		}
976
	}
977
	// Don't remove stat collection unless we unchecked the box for real, not from the loop.
978 View Code Duplication
	elseif (empty($_POST['stats']) && empty($upcontext['allow_sm_stats']))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
979
		$smcFunc['db_query']('', '
980
			DELETE FROM {db_prefix}settings
981
			WHERE variable = {string:enable_sm_stats}',
982
			array(
983
				'enable_sm_stats' => 'enable_sm_stats',
984
				'db_error_skip' => true,
985
			)
986
		);
987
988
	// Deleting old karma stuff?
989
	if (!empty($_POST['delete_karma']))
990
	{
991
		// Delete old settings vars.
992
		$smcFunc['db_query']('', '
993
			DELETE FROM {db_prefix}settings
994
			WHERE variable IN ({array_string:karma_vars})',
995
			array(
996
				'karma_vars' => array('karmaMode', 'karmaTimeRestrictAdmins', 'karmaWaitTime', 'karmaMinPosts', 'karmaLabel', 'karmaSmiteLabel', 'karmaApplaudLabel'),
997
			)
998
		);
999
1000
		// Cleaning up old karma member settings.
1001
		if ($upcontext['karma_installed']['good'])
1002
			$smcFunc['db_query']('', '
1003
				ALTER TABLE {db_prefix}members
1004
				DROP karma_good',
1005
				array()
1006
			);
1007
1008
		// Does karma bad was enable?
1009
		if ($upcontext['karma_installed']['bad'])
1010
			$smcFunc['db_query']('', '
1011
				ALTER TABLE {db_prefix}members
1012
				DROP karma_bad',
1013
				array()
1014
			);
1015
1016
		// Cleaning up old karma permissions.
1017
		$smcFunc['db_query']('', '
1018
			DELETE FROM {db_prefix}permissions
1019
			WHERE permission = {string:karma_vars}',
1020
			array(
1021
				'karma_vars' => 'karma_edit',
1022
			)
1023
		);
1024
	}
1025
1026
	// Emptying the error log?
1027
	if (!empty($_POST['empty_error']))
1028
		$smcFunc['db_query']('truncate_table', '
1029
			TRUNCATE {db_prefix}log_errors',
1030
			array(
1031
			)
1032
		);
1033
1034
	$changes = array();
1035
1036
	// Add proxy settings.
1037
	if (!isset($GLOBALS['image_proxy_maxsize']))
1038
		$changes += array(
1039
			'image_proxy_secret' => '\'' . substr(sha1(mt_rand()), 0, 20) . '\'',
1040
			'image_proxy_maxsize' => 5190,
1041
			'image_proxy_enabled' => 0,
1042
		);
1043
1044
	// If we're overriding the language follow it through.
1045 View Code Duplication
	if (isset($_GET['lang']) && file_exists($modSettings['theme_dir'] . '/languages/index.' . $_GET['lang'] . '.php'))
1046
		$changes['language'] = '\'' . $_GET['lang'] . '\'';
1047
1048
	if (!empty($_POST['maint']))
1049
	{
1050
		$changes['maintenance'] = '2';
1051
		// Remember what it was...
1052
		$upcontext['user']['main'] = $maintenance;
1053
1054
		if (!empty($_POST['maintitle']))
1055
		{
1056
			$changes['mtitle'] = '\'' . addslashes($_POST['maintitle']) . '\'';
1057
			$changes['mmessage'] = '\'' . addslashes($_POST['mainmessage']) . '\'';
1058
		}
1059
		else
1060
		{
1061
			$changes['mtitle'] = '\'Upgrading the forum...\'';
1062
			$changes['mmessage'] = '\'Don\\\'t worry, we will be back shortly with an updated forum.  It will only be a minute ;).\'';
1063
		}
1064
	}
1065
1066
	if ($command_line)
1067
		echo ' * Updating Settings.php...';
1068
1069
	// Fix some old paths.
1070 View Code Duplication
	if (substr($boarddir, 0, 1) == '.')
1071
		$changes['boarddir'] = '\'' . fixRelativePath($boarddir) . '\'';
1072
1073 View Code Duplication
	if (substr($sourcedir, 0, 1) == '.')
1074
		$changes['sourcedir'] = '\'' . fixRelativePath($sourcedir) . '\'';
1075
1076
	if (empty($cachedir) || substr($cachedir, 0, 1) == '.')
1077
		$changes['cachedir'] = '\'' . fixRelativePath($boarddir) . '/cache\'';
1078
1079
	// Not had the database type added before?
1080
	if (empty($db_type))
1081
		$changes['db_type'] = 'mysql';
1082
1083
	// If they have a "host:port" setup for the host, split that into separate values
1084
	// You should never have a : in the hostname if you're not on MySQL, but better safe than sorry
1085
	if (strpos($db_server, ':') !== false && $db_type == 'mysql')
1086
	{
1087
		list ($db_server, $db_port) = explode(':', $db_server);
1088
1089
		$changes['db_server'] = '\'' . $db_server . '\'';
1090
1091
		// Only set this if we're not using the default port
1092
		if ($db_port != ini_get('mysqli.default_port'))
1093
			$changes['db_port'] = (int) $db_port;
1094
	}
1095
	elseif (!empty($db_port))
1096
	{
1097
		// If db_port is set and is the same as the default, set it to ''
1098
		if ($db_type == 'mysql')
1099
		{
1100
			if ($db_port == ini_get('mysqli.default_port'))
1101
				$changes['db_port'] = '\'\'';
1102
			elseif ($db_type == 'postgresql' && $db_port == 5432)
1103
				$changes['db_port'] = '\'\'';
1104
		}
1105
	}
1106
1107
	// Maybe we haven't had this option yet?
1108
	if (empty($packagesdir))
1109
		$changes['packagesdir'] = '\'' . fixRelativePath($boarddir) . '/Packages\'';
1110
1111
	// Add support for $tasksdir var.
1112
	if (empty($tasksdir))
1113
		$changes['tasksdir'] = '\'' . fixRelativePath($sourcedir) . '/tasks\'';
1114
1115
	// Make sure we fix the language as well.
1116
	if (stristr($language, '-utf8'))
1117
		$changes['language'] = '\'' . str_ireplace('-utf8', '', $language) . '\'';
1118
1119
	// @todo Maybe change the cookie name if going to 1.1, too?
1120
1121
	// Update Settings.php with the new settings.
1122
	require_once($sourcedir . '/Subs-Admin.php');
1123
	updateSettingsFile($changes);
1124
1125
	if ($command_line)
1126
		echo ' Successful.' . "\n";
1127
1128
	// Are we doing debug?
1129
	if (isset($_POST['debug']))
1130
	{
1131
		$upcontext['upgrade_status']['debug'] = true;
1132
		$is_debug = true;
1133
	}
1134
1135
	// If we're not backing up then jump one.
1136
	if (empty($_POST['backup']))
1137
		$upcontext['current_step']++;
1138
1139
	// If we've got here then let's proceed to the next step!
1140
	return true;
1141
}
1142
1143
// Backup the database - why not...
1144
function BackupDatabase()
1145
{
1146
	global $upcontext, $db_prefix, $command_line, $support_js, $file_steps, $smcFunc;
1147
1148
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'backup_xml' : 'backup_database';
1149
	$upcontext['page_title'] = 'Backup Database';
1150
1151
	// Done it already - js wise?
1152
	if (!empty($_POST['backup_done']))
1153
		return true;
1154
1155
	// Some useful stuff here.
1156
	db_extend();
1157
1158
	// Might need this as well
1159
	db_extend('packages');
1160
1161
	// Get all the table names.
1162
	$filter = str_replace('_', '\_', preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? $match[2] : $db_prefix) . '%';
1163
	$db = preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0 ? strtr($match[1], array('`' => '')) : false;
1164
	$tables = $smcFunc['db_list_tables']($db, $filter);
1165
1166
	$table_names = array();
1167
	foreach ($tables as $table)
1168
		if (substr($table, 0, 7) !== 'backup_')
1169
			$table_names[] = $table;
1170
1171
	$upcontext['table_count'] = count($table_names);
1172
	$upcontext['cur_table_num'] = $_GET['substep'];
1173
	$upcontext['cur_table_name'] = str_replace($db_prefix, '', isset($table_names[$_GET['substep']]) ? $table_names[$_GET['substep']] : $table_names[0]);
1174
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1175
	// For non-java auto submit...
1176
	$file_steps = $upcontext['table_count'];
1177
1178
	// What ones have we already done?
1179 View Code Duplication
	foreach ($table_names as $id => $table)
1180
		if ($id < $_GET['substep'])
1181
			$upcontext['previous_tables'][] = $table;
1182
1183
	if ($command_line)
1184
		echo 'Backing Up Tables.';
1185
1186
	// If we don't support javascript we backup here.
1187
	if (!$support_js || isset($_GET['xml']))
1188
	{
1189
		// Backup each table!
1190
		for ($substep = $_GET['substep'], $n = count($table_names); $substep < $n; $substep++)
1191
		{
1192
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($table_names[$substep + 1]) ? $table_names[$substep + 1] : $table_names[$substep]));
1193
			$upcontext['cur_table_num'] = $substep + 1;
1194
1195
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
1196
1197
			// Do we need to pause?
1198
			nextSubstep($substep);
1199
1200
			backupTable($table_names[$substep]);
1201
1202
			// If this is XML to keep it nice for the user do one table at a time anyway!
1203
			if (isset($_GET['xml']))
1204
				return upgradeExit();
1205
		}
1206
1207
		if ($command_line)
1208
		{
1209
			echo "\n" . ' Successful.\'' . "\n";
1210
			flush();
1211
		}
1212
		$upcontext['step_progress'] = 100;
1213
1214
		$_GET['substep'] = 0;
1215
		// Make sure we move on!
1216
		return true;
1217
	}
1218
1219
	// Either way next place to post will be database changes!
1220
	$_GET['substep'] = 0;
1221
	return false;
1222
}
1223
1224
// Backup one table...
1225
function backupTable($table)
1226
{
1227
	global $command_line, $db_prefix, $smcFunc;
1228
1229
	if ($command_line)
1230
	{
1231
		echo "\n" . ' +++ Backing up \"' . str_replace($db_prefix, '', $table) . '"...';
1232
		flush();
1233
	}
1234
1235
	$smcFunc['db_backup_table']($table, 'backup_' . $table);
1236
1237
	if ($command_line)
1238
		echo ' done.';
1239
}
1240
1241
// Step 2: Everything.
1242
function DatabaseChanges()
1243
{
1244
	global $db_prefix, $modSettings, $smcFunc;
1245
	global $upcontext, $support_js, $db_type;
1246
1247
	// Have we just completed this?
1248
	if (!empty($_POST['database_done']))
1249
		return true;
1250
1251
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'database_xml' : 'database_changes';
1252
	$upcontext['page_title'] = 'Database Changes';
1253
1254
	// All possible files.
1255
	// Name, < version, insert_on_complete
1256
	$files = array(
1257
		array('upgrade_1-0.sql', '1.1', '1.1 RC0'),
1258
		array('upgrade_1-1.sql', '2.0', '2.0 a'),
1259
		array('upgrade_2-0_' . $db_type . '.sql', '2.1', '2.1 dev0'),
1260
		array('upgrade_2-1_' . $db_type . '.sql', '3.0', SMF_VERSION),
1261
	);
1262
1263
	// How many files are there in total?
1264
	if (isset($_GET['filecount']))
1265
		$upcontext['file_count'] = (int) $_GET['filecount'];
1266
	else
1267
	{
1268
		$upcontext['file_count'] = 0;
1269
		foreach ($files as $file)
1270
		{
1271
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1272
				$upcontext['file_count']++;
1273
		}
1274
	}
1275
1276
	// Do each file!
1277
	$did_not_do = count($files) - $upcontext['file_count'];
1278
	$upcontext['step_progress'] = 0;
1279
	$upcontext['cur_file_num'] = 0;
1280
	foreach ($files as $file)
1281
	{
1282
		if ($did_not_do)
1283
			$did_not_do--;
1284
		else
1285
		{
1286
			$upcontext['cur_file_num']++;
1287
			$upcontext['cur_file_name'] = $file[0];
1288
			// Do we actually need to do this still?
1289
			if (!isset($modSettings['smfVersion']) || $modSettings['smfVersion'] < $file[1])
1290
			{
1291
				$nextFile = parse_sql(dirname(__FILE__) . '/' . $file[0]);
1292
				if ($nextFile)
1293
				{
1294
					// Only update the version of this if complete.
1295
					$smcFunc['db_insert']('replace',
1296
						$db_prefix . 'settings',
1297
						array('variable' => 'string', 'value' => 'string'),
1298
						array('smfVersion', $file[2]),
1299
						array('variable')
1300
					);
1301
1302
					$modSettings['smfVersion'] = $file[2];
1303
				}
1304
1305
				// If this is XML we only do this stuff once.
1306
				if (isset($_GET['xml']))
1307
				{
1308
					// Flag to move on to the next.
1309
					$upcontext['completed_step'] = true;
1310
					// Did we complete the whole file?
1311
					if ($nextFile)
1312
						$upcontext['current_debug_item_num'] = -1;
1313
					return upgradeExit();
1314
				}
1315
				elseif ($support_js)
1316
					break;
1317
			}
1318
			// Set the progress bar to be right as if we had - even if we hadn't...
1319
			$upcontext['step_progress'] = ($upcontext['cur_file_num'] / $upcontext['file_count']) * 100;
1320
		}
1321
	}
1322
1323
	$_GET['substep'] = 0;
1324
	// So the template knows we're done.
1325
	if (!$support_js)
1326
	{
1327
		$upcontext['changes_complete'] = true;
1328
1329
		return true;
1330
	}
1331
	return false;
1332
}
1333
1334
1335
// Delete the damn thing!
1336
function DeleteUpgrade()
1337
{
1338
	global $command_line, $language, $upcontext, $sourcedir, $forum_version, $user_info, $maintenance, $smcFunc, $db_type;
1339
1340
	// Now it's nice to have some of the basic SMF source files.
1341
	if (!isset($_GET['ssi']) && !$command_line)
1342
		redirectLocation('&ssi=1');
1343
1344
	$upcontext['sub_template'] = 'upgrade_complete';
1345
	$upcontext['page_title'] = 'Upgrade Complete';
1346
1347
	$endl = $command_line ? "\n" : '<br>' . "\n";
1348
1349
	$changes = array(
1350
		'language' => '\'' . (substr($language, -4) == '.lng' ? substr($language, 0, -4) : $language) . '\'',
1351
		'db_error_send' => '1',
1352
		'upgradeData' => '\'\'',
1353
	);
1354
1355
	// Are we in maintenance mode?
1356
	if (isset($upcontext['user']['main']))
1357
	{
1358
		if ($command_line)
1359
			echo ' * ';
1360
		$upcontext['removed_maintenance'] = true;
1361
		$changes['maintenance'] = $upcontext['user']['main'];
1362
	}
1363
	// Otherwise if somehow we are in 2 let's go to 1.
1364
	elseif (!empty($maintenance) && $maintenance == 2)
1365
		$changes['maintenance'] = 1;
1366
1367
	// Wipe this out...
1368
	$upcontext['user'] = array();
1369
1370
	require_once($sourcedir . '/Subs-Admin.php');
1371
	updateSettingsFile($changes);
1372
1373
	// Clean any old cache files away.
1374
	upgrade_clean_cache();
1375
1376
	// Can we delete the file?
1377
	$upcontext['can_delete_script'] = is_writable(dirname(__FILE__)) || is_writable(__FILE__);
1378
1379
	// Now is the perfect time to fetch the SM files.
1380
	if ($command_line)
1381
		cli_scheduled_fetchSMfiles();
1382
	else
1383
	{
1384
		require_once($sourcedir . '/ScheduledTasks.php');
1385
		$forum_version = SMF_VERSION; // The variable is usually defined in index.php so lets just use the constant to do it for us.
1386
		scheduled_fetchSMfiles(); // Now go get those files!
1387
	}
1388
1389
	// Log what we've done.
1390
	if (empty($user_info['id']))
1391
		$user_info['id'] = !empty($upcontext['user']['id']) ? $upcontext['user']['id'] : 0;
1392
1393
	// Log the action manually, so CLI still works.
1394
	$smcFunc['db_insert']('',
1395
		'{db_prefix}log_actions',
1396
		array(
1397
			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'inet', 'action' => 'string',
1398
			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
1399
		),
1400
		array(
1401
			time(), 3, $user_info['id'], $command_line ? '127.0.0.1' : $user_info['ip'], 'upgrade',
1402
			0, 0, 0, json_encode(array('version' => $forum_version, 'member' => $user_info['id'])),
1403
		),
1404
		array('id_action')
1405
	);
1406
	$user_info['id'] = 0;
1407
1408
	// Save the current database version.
1409
	$server_version = $smcFunc['db_server_info']();
1410 View Code Duplication
	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
1411
		updateSettings(array('db_mysql_group_by_fix' => '1'));
1412
1413
	if ($command_line)
1414
	{
1415
		echo $endl;
1416
		echo 'Upgrade Complete!', $endl;
1417
		echo 'Please delete this file as soon as possible for security reasons.', $endl;
1418
		exit;
1419
	}
1420
1421
	// Make sure it says we're done.
1422
	$upcontext['overall_percent'] = 100;
1423
	if (isset($upcontext['step_progress']))
1424
		unset($upcontext['step_progress']);
1425
1426
	$_GET['substep'] = 0;
1427
	return false;
1428
}
1429
1430
// Just like the built in one, but setup for CLI to not use themes.
1431
function cli_scheduled_fetchSMfiles()
1432
{
1433
	global $sourcedir, $language, $forum_version, $modSettings, $smcFunc;
1434
1435
	if (empty($modSettings['time_format']))
1436
		$modSettings['time_format'] = '%B %d, %Y, %I:%M:%S %p';
1437
1438
	// What files do we want to get
1439
	$request = $smcFunc['db_query']('', '
1440
		SELECT id_file, filename, path, parameters
1441
		FROM {db_prefix}admin_info_files',
1442
		array(
1443
		)
1444
	);
1445
1446
	$js_files = array();
1447 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
1448
	{
1449
		$js_files[$row['id_file']] = array(
1450
			'filename' => $row['filename'],
1451
			'path' => $row['path'],
1452
			'parameters' => sprintf($row['parameters'], $language, urlencode($modSettings['time_format']), urlencode($forum_version)),
1453
		);
1454
	}
1455
	$smcFunc['db_free_result']($request);
1456
1457
	// We're gonna need fetch_web_data() to pull this off.
1458
	require_once($sourcedir . '/Subs-Package.php');
1459
1460
	foreach ($js_files as $ID_FILE => $file)
1461
	{
1462
		// Create the url
1463
		$server = empty($file['path']) || substr($file['path'], 0, 7) != 'http://' ? 'https://www.simplemachines.org' : '';
1464
		$url = $server . (!empty($file['path']) ? $file['path'] : $file['path']) . $file['filename'] . (!empty($file['parameters']) ? '?' . $file['parameters'] : '');
1465
1466
		// Get the file
1467
		$file_data = fetch_web_data($url);
1468
1469
		// If we got an error - give up - the site might be down.
1470
		if ($file_data === false)
1471
			return throw_error(sprintf('Could not retrieve the file %1$s.', $url));
1472
1473
		// Save the file to the database.
1474
		$smcFunc['db_query']('substring', '
1475
			UPDATE {db_prefix}admin_info_files
1476
			SET data = SUBSTRING({string:file_data}, 1, 65534)
1477
			WHERE id_file = {int:id_file}',
1478
			array(
1479
				'id_file' => $ID_FILE,
1480
				'file_data' => $file_data,
1481
			)
1482
		);
1483
	}
1484
	return true;
1485
}
1486
1487
function convertSettingsToTheme()
1488
{
1489
	global $db_prefix, $modSettings, $smcFunc;
1490
1491
	$values = array(
1492
		'show_latest_member' => @$GLOBALS['showlatestmember'],
1493
		'show_bbc' => isset($GLOBALS['showyabbcbutt']) ? $GLOBALS['showyabbcbutt'] : @$GLOBALS['showbbcbutt'],
1494
		'show_modify' => @$GLOBALS['showmodify'],
1495
		'show_user_images' => @$GLOBALS['showuserpic'],
1496
		'show_blurb' => @$GLOBALS['showusertext'],
1497
		'show_gender' => @$GLOBALS['showgenderimage'],
1498
		'show_newsfader' => @$GLOBALS['shownewsfader'],
1499
		'display_recent_bar' => @$GLOBALS['Show_RecentBar'],
1500
		'show_member_bar' => @$GLOBALS['Show_MemberBar'],
1501
		'linktree_link' => @$GLOBALS['curposlinks'],
1502
		'show_profile_buttons' => @$GLOBALS['profilebutton'],
1503
		'show_mark_read' => @$GLOBALS['showmarkread'],
1504
		'newsfader_time' => @$GLOBALS['fadertime'],
1505
		'use_image_buttons' => empty($GLOBALS['MenuType']) ? 1 : 0,
1506
		'enable_news' => @$GLOBALS['enable_news'],
1507
		'return_to_post' => @$modSettings['returnToPost'],
1508
	);
1509
1510
	$themeData = array();
1511
	foreach ($values as $variable => $value)
1512
	{
1513
		if (!isset($value) || $value === null)
1514
			$value = 0;
1515
1516
		$themeData[] = array(0, 1, $variable, $value);
1517
	}
1518 View Code Duplication
	if (!empty($themeData))
1519
	{
1520
		$smcFunc['db_insert']('ignore',
1521
			$db_prefix . 'themes',
1522
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1523
			$themeData,
1524
			array('id_member', 'id_theme', 'variable')
1525
		);
1526
	}
1527
}
1528
1529
// This function only works with MySQL but that's fine as it is only used for v1.0.
1530
function convertSettingstoOptions()
1531
{
1532
	global $modSettings, $smcFunc;
1533
1534
	// Format: new_setting -> old_setting_name.
1535
	$values = array(
1536
		'calendar_start_day' => 'cal_startmonday',
1537
		'view_newest_first' => 'viewNewestFirst',
1538
		'view_newest_pm_first' => 'viewNewestFirst',
1539
	);
1540
1541
	foreach ($values as $variable => $value)
1542
	{
1543
		if (empty($modSettings[$value[0]]))
1544
			continue;
1545
1546
		$smcFunc['db_query']('', '
1547
			INSERT IGNORE INTO {db_prefix}themes
1548
				(id_member, id_theme, variable, value)
1549
			SELECT id_member, 1, {string:variable}, {string:value}
1550
			FROM {db_prefix}members',
1551
			array(
1552
				'variable' => $variable,
1553
				'value' => $modSettings[$value[0]],
1554
				'db_error_skip' => true,
1555
			)
1556
		);
1557
1558
		$smcFunc['db_query']('', '
1559
			INSERT IGNORE INTO {db_prefix}themes
1560
				(id_member, id_theme, variable, value)
1561
			VALUES (-1, 1, {string:variable}, {string:value})',
1562
			array(
1563
				'variable' => $variable,
1564
				'value' => $modSettings[$value[0]],
1565
				'db_error_skip' => true,
1566
			)
1567
		);
1568
	}
1569
}
1570
1571
function php_version_check()
1572
{
1573
	return version_compare(PHP_VERSION, $GLOBALS['required_php_version'], '>=');
1574
}
1575
1576
function db_version_check()
1577
{
1578
	global $db_type, $databases;
1579
1580
	$curver = eval($databases[$db_type]['version_check']);
1581
	$curver = preg_replace('~\-.+?$~', '', $curver);
1582
1583
	return version_compare($databases[$db_type]['version'], $curver, '<=');
1584
}
1585
1586
function fixRelativePath($path)
1587
{
1588
	global $install_path;
1589
1590
	// Fix the . at the start, clear any duplicate slashes, and fix any trailing slash...
1591
	return addslashes(preg_replace(array('~^\.([/\\\]|$)~', '~[/]+~', '~[\\\]+~', '~[/\\\]$~'), array($install_path . '$1', '/', '\\', ''), $path));
1592
}
1593
1594
function parse_sql($filename)
1595
{
1596
	global $db_prefix, $db_collation, $boarddir, $boardurl, $command_line, $file_steps, $step_progress, $custom_warning;
1597
	global $upcontext, $support_js, $is_debug, $db_type, $db_character_set;
1598
1599
/*
1600
	Failure allowed on:
1601
		- INSERT INTO but not INSERT IGNORE INTO.
1602
		- UPDATE IGNORE but not UPDATE.
1603
		- ALTER TABLE and ALTER IGNORE TABLE.
1604
		- DROP TABLE.
1605
	Yes, I realize that this is a bit confusing... maybe it should be done differently?
1606
1607
	If a comment...
1608
		- begins with --- it is to be output, with a break only in debug mode. (and say successful\n\n if there was one before.)
1609
		- begins with ---# it is a debugging statement, no break - only shown at all in debug.
1610
		- is only ---#, it is "done." and then a break - only shown in debug.
1611
		- begins with ---{ it is a code block terminating at ---}.
1612
1613
	Every block of between "--- ..."s is a step.  Every "---#" section represents a substep.
1614
1615
	Replaces the following variables:
1616
		- {$boarddir}
1617
		- {$boardurl}
1618
		- {$db_prefix}
1619
		- {$db_collation}
1620
*/
1621
1622
	// May want to use extended functionality.
1623
	db_extend();
1624
	db_extend('packages');
1625
1626
	// Our custom error handler - does nothing but does stop public errors from XML!
1627
	set_error_handler(
1628
		function ($errno, $errstr, $errfile, $errline) use ($support_js)
1629
		{
1630
			if ($support_js)
1631
				return true;
1632
			else
1633
				echo 'Error: ' . $errstr . ' File: ' . $errfile . ' Line: ' . $errline;
1634
		}
1635
	);
1636
1637
	// If we're on MySQL, set {db_collation}; this approach is used throughout upgrade_2-0_mysql.php to set new tables to utf8
1638
	// Note it is expected to be in the format: ENGINE=MyISAM{$db_collation};
1639
	if ($db_type == 'mysql')
1640
		$db_collation = ' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci';
1641
	else
1642
		$db_collation = '';
1643
1644
	$endl = $command_line ? "\n" : '<br>' . "\n";
1645
1646
	$lines = file($filename);
1647
1648
	$current_type = 'sql';
1649
	$current_data = '';
1650
	$substep = 0;
1651
	$last_step = '';
1652
1653
	// Make sure all newly created tables will have the proper characters set; this approach is used throughout upgrade_2-1_mysql.php
1654
	if (isset($db_character_set) && $db_character_set === 'utf8')
1655
		$lines = str_replace(') ENGINE=MyISAM;', ') ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;', $lines);
1656
1657
	// Count the total number of steps within this file - for progress.
1658
	$file_steps = substr_count(implode('', $lines), '---#');
1659
	$upcontext['total_items'] = substr_count(implode('', $lines), '--- ');
1660
	$upcontext['debug_items'] = $file_steps;
1661
	$upcontext['current_item_num'] = 0;
1662
	$upcontext['current_item_name'] = '';
1663
	$upcontext['current_debug_item_num'] = 0;
1664
	$upcontext['current_debug_item_name'] = '';
1665
	// This array keeps a record of what we've done in case java is dead...
1666
	$upcontext['actioned_items'] = array();
1667
1668
	$done_something = false;
1669
1670
	foreach ($lines as $line_number => $line)
1671
	{
1672
		$do_current = $substep >= $_GET['substep'];
1673
1674
		// Get rid of any comments in the beginning of the line...
1675
		if (substr(trim($line), 0, 2) === '/*')
1676
			$line = preg_replace('~/\*.+?\*/~', '', $line);
1677
1678
		// Always flush.  Flush, flush, flush.  Flush, flush, flush, flush!  FLUSH!
1679
		if ($is_debug && !$support_js && $command_line)
1680
			flush();
1681
1682
		if (trim($line) === '')
1683
			continue;
1684
1685
		if (trim(substr($line, 0, 3)) === '---')
1686
		{
1687
			$type = substr($line, 3, 1);
1688
1689
			// An error??
1690
			if (trim($current_data) != '' && $type !== '}')
1691
			{
1692
				$upcontext['error_message'] = 'Error in upgrade script - line ' . $line_number . '!' . $endl;
1693
				if ($command_line)
1694
					echo $upcontext['error_message'];
1695
			}
1696
1697
			if ($type == ' ')
1698
			{
1699
				if (!$support_js && $do_current && $_GET['substep'] != 0 && $command_line)
1700
				{
1701
					echo ' Successful.', $endl;
1702
					flush();
1703
				}
1704
1705
				$last_step = htmlspecialchars(rtrim(substr($line, 4)));
1706
				$upcontext['current_item_num']++;
1707
				$upcontext['current_item_name'] = $last_step;
1708
1709
				if ($do_current)
1710
				{
1711
					$upcontext['actioned_items'][] = $last_step;
1712
					if ($command_line)
1713
						echo ' * ';
1714
				}
1715
			}
1716
			elseif ($type == '#')
1717
			{
1718
				$upcontext['step_progress'] += (100 / $upcontext['file_count']) / $file_steps;
1719
1720
				$upcontext['current_debug_item_num']++;
1721
				if (trim($line) != '---#')
1722
					$upcontext['current_debug_item_name'] = htmlspecialchars(rtrim(substr($line, 4)));
1723
1724
				// Have we already done something?
1725
				if (isset($_GET['xml']) && $done_something)
1726
				{
1727
					restore_error_handler();
1728
					return $upcontext['current_debug_item_num'] >= $upcontext['debug_items'] ? true : false;
1729
				}
1730
1731
				if ($do_current)
1732
				{
1733
					if (trim($line) == '---#' && $command_line)
1734
						echo ' done.', $endl;
1735
					elseif ($command_line)
1736
						echo ' +++ ', rtrim(substr($line, 4));
1737
					elseif (trim($line) != '---#')
1738
					{
1739
						if ($is_debug)
1740
							$upcontext['actioned_items'][] = htmlspecialchars(rtrim(substr($line, 4)));
1741
					}
1742
				}
1743
1744
				if ($substep < $_GET['substep'] && $substep + 1 >= $_GET['substep'])
1745
				{
1746
					if ($command_line)
1747
						echo ' * ';
1748
					else
1749
						$upcontext['actioned_items'][] = $last_step;
1750
				}
1751
1752
				// Small step - only if we're actually doing stuff.
1753
				if ($do_current)
1754
					nextSubstep(++$substep);
1755
				else
1756
					$substep++;
1757
			}
1758
			elseif ($type == '{')
1759
				$current_type = 'code';
1760
			elseif ($type == '}')
1761
			{
1762
				$current_type = 'sql';
1763
1764
				if (!$do_current)
1765
				{
1766
					$current_data = '';
1767
					continue;
1768
				}
1769
1770
				if (eval('global $db_prefix, $modSettings, $smcFunc; ' . $current_data) === false)
1771
				{
1772
					$upcontext['error_message'] = 'Error in upgrade script ' . basename($filename) . ' on line ' . $line_number . '!' . $endl;
1773
					if ($command_line)
1774
						echo $upcontext['error_message'];
1775
				}
1776
1777
				// Done with code!
1778
				$current_data = '';
1779
				$done_something = true;
1780
			}
1781
1782
			continue;
1783
		}
1784
1785
		$current_data .= $line;
1786
		if (substr(rtrim($current_data), -1) === ';' && $current_type === 'sql')
1787
		{
1788
			if ((!$support_js || isset($_GET['xml'])))
1789
			{
1790
				if (!$do_current)
1791
				{
1792
					$current_data = '';
1793
					continue;
1794
				}
1795
1796
				$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));
1797
1798
				upgrade_query($current_data);
1799
1800
				// @todo This will be how it kinda does it once mysql all stripped out - needed for postgre (etc).
1801
				/*
1802
				$result = $smcFunc['db_query']('', $current_data, false, false);
1803
				// Went wrong?
1804
				if (!$result)
1805
				{
1806
					// Bit of a bodge - do we want the error?
1807
					if (!empty($upcontext['return_error']))
1808
					{
1809
						$upcontext['error_message'] = $smcFunc['db_error']($db_connection);
1810
						return false;
1811
					}
1812
				}*/
1813
				$done_something = true;
1814
			}
1815
			$current_data = '';
1816
		}
1817
		// If this is xml based and we're just getting the item name then that's grand.
1818
		elseif ($support_js && !isset($_GET['xml']) && $upcontext['current_debug_item_name'] != '' && $do_current)
1819
		{
1820
			restore_error_handler();
1821
			return false;
1822
		}
1823
1824
		// Clean up by cleaning any step info.
1825
		$step_progress = array();
1826
		$custom_warning = '';
1827
	}
1828
1829
	// Put back the error handler.
1830
	restore_error_handler();
1831
1832
	if ($command_line)
1833
	{
1834
		echo ' Successful.' . "\n";
1835
		flush();
1836
	}
1837
1838
	$_GET['substep'] = 0;
1839
	return true;
1840
}
1841
1842
function upgrade_query($string, $unbuffered = false)
1843
{
1844
	global $db_connection, $db_server, $db_user, $db_passwd, $db_type, $command_line, $upcontext, $upgradeurl, $modSettings;
1845
	global $db_name, $db_unbuffered, $smcFunc;
1846
1847
	// Get the query result - working around some SMF specific security - just this once!
1848
	$modSettings['disableQueryCheck'] = true;
1849
	$db_unbuffered = $unbuffered;
1850
	$result = $smcFunc['db_query']('', $string, array('security_override' => true, 'db_error_skip' => true));
1851
	$db_unbuffered = false;
1852
1853
	// Failure?!
1854
	if ($result !== false)
1855
		return $result;
1856
1857
	$db_error_message = $smcFunc['db_error']($db_connection);
1858
	// If MySQL we do something more clever.
1859
	if ($db_type == 'mysql')
1860
	{
1861
		$mysqli_errno = mysqli_errno($db_connection);
1862
		$error_query = in_array(substr(trim($string), 0, 11), array('INSERT INTO', 'UPDATE IGNO', 'ALTER TABLE', 'DROP TABLE ', 'ALTER IGNOR'));
1863
1864
		// Error numbers:
1865
		//    1016: Can't open file '....MYI'
1866
		//    1050: Table already exists.
1867
		//    1054: Unknown column name.
1868
		//    1060: Duplicate column name.
1869
		//    1061: Duplicate key name.
1870
		//    1062: Duplicate entry for unique key.
1871
		//    1068: Multiple primary keys.
1872
		//    1072: Key column '%s' doesn't exist in table.
1873
		//    1091: Can't drop key, doesn't exist.
1874
		//    1146: Table doesn't exist.
1875
		//    2013: Lost connection to server during query.
1876
1877
		if ($mysqli_errno == 1016)
1878
		{
1879
			if (preg_match('~\'([^\.\']+)~', $db_error_message, $match) != 0 && !empty($match[1]))
1880
			{
1881
				mysqli_query($db_connection, 'REPAIR TABLE `' . $match[1] . '`');
1882
				$result = mysqli_query($db_connection, $string);
1883
				if ($result !== false)
1884
					return $result;
1885
			}
1886
		}
1887
		elseif ($mysqli_errno == 2013)
1888
		{
1889
			$db_connection = mysqli_connect($db_server, $db_user, $db_passwd);
1890
			mysqli_select_db($db_connection, $db_name);
1891
			if ($db_connection)
1892
			{
1893
				$result = mysqli_query($db_connection, $string);
1894
				if ($result !== false)
1895
					return $result;
1896
			}
1897
		}
1898
		// Duplicate column name... should be okay ;).
1899 View Code Duplication
		elseif (in_array($mysqli_errno, array(1060, 1061, 1068, 1091)))
1900
			return false;
1901
		// Duplicate insert... make sure it's the proper type of query ;).
1902 View Code Duplication
		elseif (in_array($mysqli_errno, array(1054, 1062, 1146)) && $error_query)
1903
			return false;
1904
		// Creating an index on a non-existent column.
1905
		elseif ($mysqli_errno == 1072)
1906
			return false;
1907
		elseif ($mysqli_errno == 1050 && substr(trim($string), 0, 12) == 'RENAME TABLE')
1908
			return false;
1909
	}
1910
	// If a table already exists don't go potty.
1911
	else
1912
	{
1913
		if (in_array(substr(trim($string), 0, 8), array('CREATE T', 'CREATE S', 'DROP TABL', 'ALTER TA', 'CREATE I', 'CREATE U')))
1914
		{
1915
			if (strpos($db_error_message, 'exist') !== false)
1916
				return true;
1917
		}
1918
		elseif (strpos(trim($string), 'INSERT ') !== false)
1919
		{
1920
			if (strpos($db_error_message, 'duplicate') !== false)
1921
				return true;
1922
		}
1923
	}
1924
1925
	// Get the query string so we pass everything.
1926
	$query_string = '';
1927
	foreach ($_GET as $k => $v)
1928
		$query_string .= ';' . $k . '=' . $v;
1929
	if (strlen($query_string) != 0)
1930
		$query_string = '?' . substr($query_string, 1);
1931
1932
	if ($command_line)
1933
	{
1934
		echo 'Unsuccessful!  Database error message:', "\n", $db_error_message, "\n";
1935
		die;
1936
	}
1937
1938
	// Bit of a bodge - do we want the error?
1939
	if (!empty($upcontext['return_error']))
1940
	{
1941
		$upcontext['error_message'] = $db_error_message;
1942
		$upcontext['error_string'] = $string;
1943
		return false;
1944
	}
1945
1946
	// Otherwise we have to display this somewhere appropriate if possible.
1947
	$upcontext['forced_error_message'] = '
1948
			<strong>Unsuccessful!</strong><br>
1949
1950
			<div style="margin: 2ex;">
1951
				This query:
1952
				<blockquote><pre>' . nl2br(htmlspecialchars(trim($string))) . ';</pre></blockquote>
1953
1954
				Caused the error:
1955
				<blockquote>' . nl2br(htmlspecialchars($db_error_message)) . '</blockquote>
1956
			</div>
1957
1958
			<form action="' . $upgradeurl . $query_string . '" method="post">
1959
				<input type="submit" value="Try again" class="button_submit">
1960
			</form>
1961
		</div>';
1962
1963
	upgradeExit();
1964
}
1965
1966
// This performs a table alter, but does it unbuffered so the script can time out professionally.
1967
function protected_alter($change, $substep, $is_test = false)
1968
{
1969
	global $db_prefix, $smcFunc;
1970
1971
	db_extend('packages');
1972
1973
	// Firstly, check whether the current index/column exists.
1974
	$found = false;
1975
	if ($change['type'] === 'column')
1976
	{
1977
		$columns = $smcFunc['db_list_columns']('{db_prefix}' . $change['table'], true);
1978
		foreach ($columns as $column)
1979
		{
1980
			// Found it?
1981
			if ($column['name'] === $change['name'])
1982
			{
1983
				$found |= 1;
1984
				// Do some checks on the data if we have it set.
1985
				if (isset($change['col_type']))
1986
					$found &= $change['col_type'] === $column['type'];
1987
				if (isset($change['null_allowed']))
1988
					$found &= $column['null'] == $change['null_allowed'];
1989
				if (isset($change['default']))
1990
					$found &= $change['default'] === $column['default'];
1991
			}
1992
		}
1993
	}
1994
	elseif ($change['type'] === 'index')
1995
	{
1996
		$request = upgrade_query('
1997
			SHOW INDEX
1998
			FROM ' . $db_prefix . $change['table']);
1999
		if ($request !== false)
2000
		{
2001
			$cur_index = array();
2002
2003
			while ($row = $smcFunc['db_fetch_assoc']($request))
2004
				if ($row['Key_name'] === $change['name'])
2005
					$cur_index[(int) $row['Seq_in_index']] = $row['Column_name'];
2006
2007
			ksort($cur_index, SORT_NUMERIC);
2008
			$found = array_values($cur_index) === $change['target_columns'];
2009
2010
			$smcFunc['db_free_result']($request);
2011
		}
2012
	}
2013
2014
	// If we're trying to add and it's added, we're done.
2015
	if ($found && in_array($change['method'], array('add', 'change')))
2016
		return true;
2017
	// Otherwise if we're removing and it wasn't found we're also done.
2018
	elseif (!$found && in_array($change['method'], array('remove', 'change_remove')))
2019
		return true;
2020
	// Otherwise is it just a test?
2021
	elseif ($is_test)
2022
		return false;
2023
2024
	// Not found it yet? Bummer! How about we see if we're currently doing it?
2025
	$running = false;
2026
	$found = false;
2027
	while (1 == 1)
2028
	{
2029
		$request = upgrade_query('
2030
			SHOW FULL PROCESSLIST');
2031
		while ($row = $smcFunc['db_fetch_assoc']($request))
2032
		{
2033
			if (strpos($row['Info'], 'ALTER TABLE ' . $db_prefix . $change['table']) !== false && strpos($row['Info'], $change['text']) !== false)
2034
				$found = true;
2035
		}
2036
2037
		// Can't find it? Then we need to run it fools!
2038
		if (!$found && !$running)
2039
		{
2040
			$smcFunc['db_free_result']($request);
2041
2042
			$success = upgrade_query('
2043
				ALTER TABLE ' . $db_prefix . $change['table'] . '
2044
				' . $change['text'], true) !== false;
2045
2046
			if (!$success)
2047
				return false;
2048
2049
			// Return
2050
			$running = true;
2051
		}
2052
		// What if we've not found it, but we'd ran it already? Must of completed.
2053
		elseif (!$found)
2054
		{
2055
			$smcFunc['db_free_result']($request);
2056
			return true;
2057
		}
2058
2059
		// Pause execution for a sec or three.
2060
		sleep(3);
2061
2062
		// Can never be too well protected.
2063
		nextSubstep($substep);
2064
	}
2065
2066
	// Protect it.
2067
	nextSubstep($substep);
2068
}
2069
2070
/**
2071
 * Alter a text column definition preserving its character set.
2072
 *
2073
 * @param array $change
2074
 * @param int $substep
2075
 */
2076
function textfield_alter($change, $substep)
2077
{
2078
	global $db_prefix, $smcFunc;
2079
2080
	$request = $smcFunc['db_query']('', '
2081
		SHOW FULL COLUMNS
2082
		FROM {db_prefix}' . $change['table'] . '
2083
		LIKE {string:column}',
2084
		array(
2085
			'column' => $change['column'],
2086
			'db_error_skip' => true,
2087
		)
2088
	);
2089
	if ($smcFunc['db_num_rows']($request) === 0)
2090
		die('Unable to find column ' . $change['column'] . ' inside table ' . $db_prefix . $change['table']);
2091
	$table_row = $smcFunc['db_fetch_assoc']($request);
2092
	$smcFunc['db_free_result']($request);
2093
2094
	// If something of the current column definition is different, fix it.
2095
	$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']);
2096
2097
	// Columns that previously allowed null, need to be converted first.
2098
	$null_fix = strtolower($table_row['Null']) === 'yes' && !$change['null_allowed'];
2099
2100
	// Get the character set that goes with the collation of the column.
2101 View Code Duplication
	if ($column_fix && !empty($table_row['Collation']))
2102
	{
2103
		$request = $smcFunc['db_query']('', '
2104
			SHOW COLLATION
2105
			LIKE {string:collation}',
2106
			array(
2107
				'collation' => $table_row['Collation'],
2108
				'db_error_skip' => true,
2109
			)
2110
		);
2111
		// No results? Just forget it all together.
2112
		if ($smcFunc['db_num_rows']($request) === 0)
2113
			unset($table_row['Collation']);
2114
		else
2115
			$collation_info = $smcFunc['db_fetch_assoc']($request);
2116
		$smcFunc['db_free_result']($request);
2117
	}
2118
2119
	if ($column_fix)
2120
	{
2121
		// Make sure there are no NULL's left.
2122
		if ($null_fix)
2123
			$smcFunc['db_query']('', '
2124
				UPDATE {db_prefix}' . $change['table'] . '
2125
				SET ' . $change['column'] . ' = {string:default}
2126
				WHERE ' . $change['column'] . ' IS NULL',
2127
				array(
2128
					'default' => isset($change['default']) ? $change['default'] : '',
2129
					'db_error_skip' => true,
2130
				)
2131
			);
2132
2133
		// Do the actual alteration.
2134
		$smcFunc['db_query']('', '
2135
			ALTER TABLE {db_prefix}' . $change['table'] . '
2136
			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}' : ''),
2137
			array(
2138
				'default' => isset($change['default']) ? $change['default'] : '',
2139
				'db_error_skip' => true,
2140
			)
2141
		);
2142
	}
2143
	nextSubstep($substep);
2144
}
2145
2146
// Check if we need to alter this query.
2147
function checkChange(&$change)
2148
{
2149
	global $smcFunc, $db_type, $databases;
2150
	static $database_version, $where_field_support;
2151
2152
	// Attempt to find a database_version.
2153
	if (empty($database_version))
2154
	{
2155
		$database_version = $databases[$db_type]['version_check'];
2156
		$where_field_support = $db_type == 'mysql' && version_compare('5.0', $database_version, '<=');
2157
	}
2158
2159
	// Not a column we need to check on?
2160
	if (!in_array($change['name'], array('memberGroups', 'passwordSalt')))
2161
		return;
2162
2163
	// Break it up you (six|seven).
2164
	$temp = explode(' ', str_replace('NOT NULL', 'NOT_NULL', $change['text']));
2165
2166
	// Can we support a shortcut method?
2167
	if ($where_field_support)
2168
	{
2169
		// Get the details about this change.
2170
		$request = $smcFunc['db_query']('', '
2171
			SHOW FIELDS
2172
			FROM {db_prefix}{raw:table}
2173
			WHERE Field = {string:old_name} OR Field = {string:new_name}',
2174
			array(
2175
				'table' => $change['table'],
2176
				'old_name' => $temp[1],
2177
				'new_name' => $temp[2],
2178
		));
2179
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2180
		if ($smcFunc['db_num_rows'] != 1)
2181
			return;
2182
2183
		list (, $current_type) = $smcFunc['db_fetch_assoc']($request);
2184
		$smcFunc['db_free_result']($request);
2185
	}
2186
	else
2187
	{
2188
		// Do this the old fashion, sure method way.
2189
		$request = $smcFunc['db_query']('', '
2190
			SHOW FIELDS
2191
			FROM {db_prefix}{raw:table}',
2192
			array(
2193
				'table' => $change['table'],
2194
		));
2195
		// Mayday!
2196
		// !!! This doesn't technically work because we don't pass request into it, but it hasn't broke anything yet.
2197
		if ($smcFunc['db_num_rows'] == 0)
2198
			return;
2199
2200
		// Oh where, oh where has my little field gone. Oh where can it be...
2201
		while ($row = $smcFunc['db_query']($request))
2202
			if ($row['Field'] == $temp[1] || $row['Field'] == $temp[2])
2203
			{
2204
				$current_type = $row['Type'];
2205
				break;
2206
			}
2207
	}
2208
2209
	// If this doesn't match, the column may of been altered for a reason.
2210
	if (trim($current_type) != trim($temp[3]))
2211
		$temp[3] = $current_type;
2212
2213
	// Piece this back together.
2214
	$change['text'] = str_replace('NOT_NULL', 'NOT NULL', implode(' ', $temp));
2215
}
2216
2217
// The next substep.
2218
function nextSubstep($substep)
2219
{
2220
	global $start_time, $timeLimitThreshold, $command_line, $custom_warning;
2221
	global $step_progress, $is_debug, $upcontext;
2222
2223
	if ($_GET['substep'] < $substep)
2224
		$_GET['substep'] = $substep;
2225
2226
	if ($command_line)
2227
	{
2228
		if (time() - $start_time > 1 && empty($is_debug))
2229
		{
2230
			echo '.';
2231
			$start_time = time();
2232
		}
2233
		return;
2234
	}
2235
2236
	@set_time_limit(300);
2237
	if (function_exists('apache_reset_timeout'))
2238
		@apache_reset_timeout();
2239
2240
	if (time() - $start_time <= $timeLimitThreshold)
2241
		return;
2242
2243
	// Do we have some custom step progress stuff?
2244
	if (!empty($step_progress))
2245
	{
2246
		$upcontext['substep_progress'] = 0;
2247
		$upcontext['substep_progress_name'] = $step_progress['name'];
2248
		if ($step_progress['current'] > $step_progress['total'])
2249
			$upcontext['substep_progress'] = 99.9;
2250
		else
2251
			$upcontext['substep_progress'] = ($step_progress['current'] / $step_progress['total']) * 100;
2252
2253
		// Make it nicely rounded.
2254
		$upcontext['substep_progress'] = round($upcontext['substep_progress'], 1);
2255
	}
2256
2257
	// If this is XML we just exit right away!
2258
	if (isset($_GET['xml']))
2259
		return upgradeExit();
2260
2261
	// We're going to pause after this!
2262
	$upcontext['pause'] = true;
2263
2264
	$upcontext['query_string'] = '';
2265
	foreach ($_GET as $k => $v)
2266
	{
2267
		if ($k != 'data' && $k != 'substep' && $k != 'step')
2268
			$upcontext['query_string'] .= ';' . $k . '=' . $v;
2269
	}
2270
2271
	// Custom warning?
2272
	if (!empty($custom_warning))
2273
		$upcontext['custom_warning'] = $custom_warning;
2274
2275
	upgradeExit();
2276
}
2277
2278
function cmdStep0()
2279
{
2280
	global $boarddir, $sourcedir, $modSettings, $start_time, $cachedir, $databases, $db_type, $smcFunc, $upcontext;
2281
	global $is_debug;
2282
	$start_time = time();
2283
2284
	ob_end_clean();
2285
	ob_implicit_flush(true);
2286
	@set_time_limit(600);
2287
2288
	if (!isset($_SERVER['argv']))
2289
		$_SERVER['argv'] = array();
2290
	$_GET['maint'] = 1;
2291
2292
	foreach ($_SERVER['argv'] as $i => $arg)
2293
	{
2294
		if (preg_match('~^--language=(.+)$~', $arg, $match) != 0)
2295
			$_GET['lang'] = $match[1];
2296
		elseif (preg_match('~^--path=(.+)$~', $arg) != 0)
2297
			continue;
2298
		elseif ($arg == '--no-maintenance')
2299
			$_GET['maint'] = 0;
2300
		elseif ($arg == '--debug')
2301
			$is_debug = true;
2302
		elseif ($arg == '--backup')
2303
			$_POST['backup'] = 1;
2304
		elseif ($arg == '--template' && (file_exists($boarddir . '/template.php') || file_exists($boarddir . '/template.html') && !file_exists($modSettings['theme_dir'] . '/converted')))
2305
			$_GET['conv'] = 1;
2306
		elseif ($i != 0)
2307
		{
2308
			echo 'SMF Command-line Upgrader
2309
Usage: /path/to/php -f ' . basename(__FILE__) . ' -- [OPTION]...
2310
2311
    --language=LANG         Reset the forum\'s language to LANG.
2312
    --no-maintenance        Don\'t put the forum into maintenance mode.
2313
    --debug                 Output debugging information.
2314
    --backup                Create backups of tables with "backup_" prefix.';
2315
			echo "\n";
2316
			exit;
2317
		}
2318
	}
2319
2320
	if (!php_version_check())
2321
		print_error('Error: PHP ' . PHP_VERSION . ' does not match version requirements.', true);
2322
	if (!db_version_check())
2323
		print_error('Error: ' . $databases[$db_type]['name'] . ' ' . $databases[$db_type]['version'] . ' does not match minimum requirements.', true);
2324
2325
	// Do some checks to make sure they have proper privileges
2326
	db_extend('packages');
2327
2328
	// CREATE
2329
	$create = $smcFunc['db_create_table']('{db_prefix}priv_check', array(array('name' => 'id_test', 'type' => 'int', 'size' => 10, 'unsigned' => true, 'auto' => true)), array(array('columns' => array('id_test'), 'primary' => true)), array(), 'overwrite');
2330
2331
	// ALTER
2332
	$alter = $smcFunc['db_add_column']('{db_prefix}priv_check', array('name' => 'txt', 'type' => 'tinytext', 'null' => false, 'default' => ''));
2333
2334
	// DROP
2335
	$drop = $smcFunc['db_drop_table']('{db_prefix}priv_check');
2336
2337
	// Sorry... we need CREATE, ALTER and DROP
2338 View Code Duplication
	if (!$create || !$alter || !$drop)
2339
		print_error("The " . $databases[$db_type]['name'] . " user you have set in Settings.php does not have proper privileges.\n\nPlease ask your host to give this user the ALTER, CREATE, and DROP privileges.", true);
2340
2341
	$check = @file_exists($modSettings['theme_dir'] . '/index.template.php')
2342
		&& @file_exists($sourcedir . '/QueryString.php')
2343
		&& @file_exists($sourcedir . '/ManageBoards.php');
2344
	if (!$check && !isset($modSettings['smfVersion']))
2345
		print_error('Error: Some files are missing or out-of-date.', true);
2346
2347
	// Do a quick version spot check.
2348
	$temp = substr(@implode('', @file($boarddir . '/index.php')), 0, 4096);
2349
	preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $temp, $match);
2350
	if (empty($match[1]) || (trim($match[1]) != SMF_VERSION))
2351
		print_error('Error: Some files have not yet been updated properly.');
2352
2353
	// Make sure Settings.php is writable.
2354
		quickFileWritable($boarddir . '/Settings.php');
2355
	if (!is_writable($boarddir . '/Settings.php'))
2356
		print_error('Error: Unable to obtain write access to "Settings.php".', true);
2357
2358
	// Make sure Settings_bak.php is writable.
2359
		quickFileWritable($boarddir . '/Settings_bak.php');
2360
	if (!is_writable($boarddir . '/Settings_bak.php'))
2361
		print_error('Error: Unable to obtain write access to "Settings_bak.php".');
2362
2363 View Code Duplication
	if (isset($modSettings['agreement']) && (!is_writable($boarddir) || file_exists($boarddir . '/agreement.txt')) && !is_writable($boarddir . '/agreement.txt'))
2364
		print_error('Error: Unable to obtain write access to "agreement.txt".');
2365
	elseif (isset($modSettings['agreement']))
2366
	{
2367
		$fp = fopen($boarddir . '/agreement.txt', 'w');
2368
		fwrite($fp, $modSettings['agreement']);
2369
		fclose($fp);
2370
	}
2371
2372
	// Make sure Themes is writable.
2373
	quickFileWritable($modSettings['theme_dir']);
2374
2375
	if (!is_writable($modSettings['theme_dir']) && !isset($modSettings['smfVersion']))
2376
		print_error('Error: Unable to obtain write access to "Themes".');
2377
2378
	// Make sure cache directory exists and is writable!
2379
	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
2380
	if (!file_exists($cachedir_temp))
2381
		@mkdir($cachedir_temp);
2382
2383
	// Make sure the cache temp dir is writable.
2384
	quickFileWritable($cachedir_temp);
2385
2386
	if (!is_writable($cachedir_temp))
2387
		print_error('Error: Unable to obtain write access to "cache".', true);
2388
2389
	if (!file_exists($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php') && !isset($modSettings['smfVersion']) && !isset($_GET['lang']))
2390
		print_error('Error: Unable to find language files!', true);
2391
	else
2392
	{
2393
		$temp = substr(@implode('', @file($modSettings['theme_dir'] . '/languages/index.' . $upcontext['language'] . '.php')), 0, 4096);
2394
		preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*index(?:[\s]{2}|\*/)~i', $temp, $match);
2395
2396
		if (empty($match[1]) || $match[1] != SMF_LANG_VERSION)
2397
			print_error('Error: Language files out of date.', true);
2398
		if (!file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
2399
			print_error('Error: Install language is missing for selected language.', true);
2400
2401
		// Otherwise include it!
2402
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
2403
	}
2404
2405
	// Make sure we skip the HTML for login.
2406
	$_POST['upcont'] = true;
2407
	$upcontext['current_step'] = 1;
2408
}
2409
2410
/**
2411
 * Handles converting your database to UTF-8
2412
 */
2413
function ConvertUtf8()
2414
{
2415
	global $upcontext, $db_character_set, $sourcedir, $smcFunc, $modSettings, $language, $db_prefix, $db_type, $command_line, $support_js;
2416
2417
	// Done it already?
2418
	if (!empty($_POST['utf8_done']))
2419
		return true;
2420
2421
	// First make sure they aren't already on UTF-8 before we go anywhere...
2422
	if ($db_type == 'postgresql' || ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8'))
2423
	{
2424
		$smcFunc['db_insert']('replace',
2425
			'{db_prefix}settings',
2426
			array('variable' => 'string', 'value' => 'string'),
2427
			array(array('global_character_set', 'UTF-8')),
2428
			array('variable')
2429
		);
2430
2431
		return true;
2432
	}
2433
	else
2434
	{
2435
		$upcontext['page_title'] = 'Converting to UTF8';
2436
		$upcontext['sub_template'] = isset($_GET['xml']) ? 'convert_xml' : 'convert_utf8';
2437
2438
		// The character sets used in SMF's language files with their db equivalent.
2439
		$charsets = array(
2440
			// Armenian
2441
			'armscii8' => 'armscii8',
2442
			// Chinese-traditional.
2443
			'big5' => 'big5',
2444
			// Chinese-simplified.
2445
			'gbk' => 'gbk',
2446
			// West European.
2447
			'ISO-8859-1' => 'latin1',
2448
			// Romanian.
2449
			'ISO-8859-2' => 'latin2',
2450
			// Turkish.
2451
			'ISO-8859-9' => 'latin5',
2452
			// Latvian
2453
			'ISO-8859-13' => 'latin7',
2454
			// West European with Euro sign.
2455
			'ISO-8859-15' => 'latin9',
2456
			// Thai.
2457
			'tis-620' => 'tis620',
2458
			// Persian, Chinese, etc.
2459
			'UTF-8' => 'utf8',
2460
			// Russian.
2461
			'windows-1251' => 'cp1251',
2462
			// Greek.
2463
			'windows-1253' => 'utf8',
2464
			// Hebrew.
2465
			'windows-1255' => 'utf8',
2466
			// Arabic.
2467
			'windows-1256' => 'cp1256',
2468
		);
2469
2470
		// Get a list of character sets supported by your MySQL server.
2471
		$request = $smcFunc['db_query']('', '
2472
			SHOW CHARACTER SET',
2473
			array(
2474
			)
2475
		);
2476
		$db_charsets = array();
2477
		while ($row = $smcFunc['db_fetch_assoc']($request))
2478
			$db_charsets[] = $row['Charset'];
2479
2480
		$smcFunc['db_free_result']($request);
2481
2482
		// Character sets supported by both MySQL and SMF's language files.
2483
		$charsets = array_intersect($charsets, $db_charsets);
2484
2485
		// Use the messages.body column as indicator for the database charset.
2486
		$request = $smcFunc['db_query']('', '
2487
			SHOW FULL COLUMNS
2488
			FROM {db_prefix}messages
2489
			LIKE {string:body_like}',
2490
			array(
2491
				'body_like' => 'body',
2492
			)
2493
		);
2494
		$column_info = $smcFunc['db_fetch_assoc']($request);
2495
		$smcFunc['db_free_result']($request);
2496
2497
		// A collation looks like latin1_swedish. We only need the character set.
2498
		list($upcontext['database_charset']) = explode('_', $column_info['Collation']);
2499
		$upcontext['database_charset'] = in_array($upcontext['database_charset'], $charsets) ? array_search($upcontext['database_charset'], $charsets) : $upcontext['database_charset'];
2500
2501
		// Detect whether a fulltext index is set.
2502
		$request = $smcFunc['db_query']('', '
2503
 			SHOW INDEX
2504
	  	    FROM {db_prefix}messages',
2505
			array(
2506
			)
2507
		);
2508
2509
		$upcontext['dropping_index'] = false;
2510
2511
		// If there's a fulltext index, we need to drop it first...
2512 View Code Duplication
		if ($request !== false || $smcFunc['db_num_rows']($request) != 0)
2513
		{
2514
			while ($row = $smcFunc['db_fetch_assoc']($request))
2515
				if ($row['Column_name'] == 'body' && (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT' || isset($row['Comment']) && $row['Comment'] == 'FULLTEXT'))
2516
					$upcontext['fulltext_index'][] = $row['Key_name'];
2517
			$smcFunc['db_free_result']($request);
2518
2519
			if (isset($upcontext['fulltext_index']))
2520
				$upcontext['fulltext_index'] = array_unique($upcontext['fulltext_index']);
2521
		}
2522
2523
		// Drop it and make a note...
2524
		if (!empty($upcontext['fulltext_index']))
2525
		{
2526
			$upcontext['dropping_index'] = true;
2527
2528
			$smcFunc['db_query']('', '
2529
  			ALTER TABLE {db_prefix}messages
2530
	  		DROP INDEX ' . implode(',
2531
		  	DROP INDEX ', $upcontext['fulltext_index']),
2532
				array(
2533
					'db_error_skip' => true,
2534
				)
2535
			);
2536
2537
			// Update the settings table
2538
			$smcFunc['db_insert']('replace',
2539
				'{db_prefix}settings',
2540
				array('variable' => 'string', 'value' => 'string'),
2541
				array('db_search_index', ''),
2542
				array('variable')
2543
			);
2544
		}
2545
2546
		// Figure out what charset we should be converting from...
2547
		$lang_charsets = array(
2548
			'arabic' => 'windows-1256',
2549
			'armenian_east' => 'armscii-8',
2550
			'armenian_west' => 'armscii-8',
2551
			'azerbaijani_latin' => 'ISO-8859-9',
2552
			'bangla' => 'UTF-8',
2553
			'belarusian' => 'ISO-8859-5',
2554
			'bulgarian' => 'windows-1251',
2555
			'cambodian' => 'UTF-8',
2556
			'chinese_simplified' => 'gbk',
2557
			'chinese_traditional' => 'big5',
2558
			'croation' => 'ISO-8859-2',
2559
			'czech' => 'ISO-8859-2',
2560
			'czech_informal' => 'ISO-8859-2',
2561
			'english_pirate' => 'UTF-8',
2562
			'esperanto' => 'ISO-8859-3',
2563
			'estonian' => 'ISO-8859-15',
2564
			'filipino_tagalog' => 'UTF-8',
2565
			'filipino_vasayan' => 'UTF-8',
2566
			'georgian' => 'UTF-8',
2567
			'greek' => 'ISO-8859-3',
2568
			'hebrew' => 'windows-1255',
2569
			'hungarian' => 'ISO-8859-2',
2570
			'irish' => 'UTF-8',
2571
			'japanese' => 'UTF-8',
2572
			'khmer' => 'UTF-8',
2573
			'korean' => 'UTF-8',
2574
			'kurdish_kurmanji' => 'ISO-8859-9',
2575
			'kurdish_sorani' => 'windows-1256',
2576
			'lao' => 'tis-620',
2577
			'latvian' => 'ISO-8859-13',
2578
			'lithuanian' => 'ISO-8859-4',
2579
			'macedonian' => 'UTF-8',
2580
			'malayalam' => 'UTF-8',
2581
			'mongolian' => 'UTF-8',
2582
			'nepali' => 'UTF-8',
2583
			'persian' => 'UTF-8',
2584
			'polish' => 'ISO-8859-2',
2585
			'romanian' => 'ISO-8859-2',
2586
			'russian' => 'windows-1252',
2587
			'sakha' => 'UTF-8',
2588
			'serbian_cyrillic' => 'ISO-8859-5',
2589
			'serbian_latin' => 'ISO-8859-2',
2590
			'sinhala' => 'UTF-8',
2591
			'slovak' => 'ISO-8859-2',
2592
			'slovenian' => 'ISO-8859-2',
2593
			'telugu' => 'UTF-8',
2594
			'thai' => 'tis-620',
2595
			'turkish' => 'ISO-8859-9',
2596
			'turkmen' => 'ISO-8859-9',
2597
			'ukranian' => 'windows-1251',
2598
			'urdu' => 'UTF-8',
2599
			'uzbek_cyrillic' => 'ISO-8859-5',
2600
			'uzbek_latin' => 'ISO-8859-5',
2601
			'vietnamese' => 'UTF-8',
2602
			'yoruba' => 'UTF-8'
2603
		);
2604
2605
		// Default to ISO-8859-1 unless we detected another supported charset
2606
		$upcontext['charset_detected'] = (isset($lang_charsets[$language]) && isset($charsets[strtr(strtolower($upcontext['charset_detected']), array('utf' => 'UTF', 'iso' => 'ISO'))])) ? $lang_charsets[$language] : 'ISO-8859-1';
2607
2608
		$upcontext['charset_list'] = array_keys($charsets);
2609
2610
		// Translation table for the character sets not native for MySQL.
2611
		$translation_tables = array(
2612
			'windows-1255' => array(
2613
				'0x81' => '\'\'',		'0x8A' => '\'\'',		'0x8C' => '\'\'',
2614
				'0x8D' => '\'\'',		'0x8E' => '\'\'',		'0x8F' => '\'\'',
2615
				'0x90' => '\'\'',		'0x9A' => '\'\'',		'0x9C' => '\'\'',
2616
				'0x9D' => '\'\'',		'0x9E' => '\'\'',		'0x9F' => '\'\'',
2617
				'0xCA' => '\'\'',		'0xD9' => '\'\'',		'0xDA' => '\'\'',
2618
				'0xDB' => '\'\'',		'0xDC' => '\'\'',		'0xDD' => '\'\'',
2619
				'0xDE' => '\'\'',		'0xDF' => '\'\'',		'0xFB' => '0xD792',
2620
				'0xFC' => '0xE282AC',		'0xFF' => '0xD6B2',		'0xC2' => '0xFF',
2621
				'0x80' => '0xFC',		'0xE2' => '0xFB',		'0xA0' => '0xC2A0',
2622
				'0xA1' => '0xC2A1',		'0xA2' => '0xC2A2',		'0xA3' => '0xC2A3',
2623
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2624
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2625
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2626
				'0xAF' => '0xC2AF',		'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',
2627
				'0xB2' => '0xC2B2',		'0xB3' => '0xC2B3',		'0xB4' => '0xC2B4',
2628
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2629
				'0xB8' => '0xC2B8',		'0xB9' => '0xC2B9',		'0xBB' => '0xC2BB',
2630
				'0xBC' => '0xC2BC',		'0xBD' => '0xC2BD',		'0xBE' => '0xC2BE',
2631
				'0xBF' => '0xC2BF',		'0xD7' => '0xD7B3',		'0xD1' => '0xD781',
2632
				'0xD4' => '0xD7B0',		'0xD5' => '0xD7B1',		'0xD6' => '0xD7B2',
2633
				'0xE0' => '0xD790',		'0xEA' => '0xD79A',		'0xEC' => '0xD79C',
2634
				'0xED' => '0xD79D',		'0xEE' => '0xD79E',		'0xEF' => '0xD79F',
2635
				'0xF0' => '0xD7A0',		'0xF1' => '0xD7A1',		'0xF2' => '0xD7A2',
2636
				'0xF3' => '0xD7A3',		'0xF5' => '0xD7A5',		'0xF6' => '0xD7A6',
2637
				'0xF7' => '0xD7A7',		'0xF8' => '0xD7A8',		'0xF9' => '0xD7A9',
2638
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2639
				'0x86' => '0xE280A0',	'0x87' => '0xE280A1',	'0x89' => '0xE280B0',
2640
				'0x8B' => '0xE280B9',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2641
				'0x95' => '0xE280A2',	'0x97' => '0xE28094',	'0x99' => '0xE284A2',
2642
				'0xC0' => '0xD6B0',		'0xC1' => '0xD6B1',		'0xC3' => '0xD6B3',
2643
				'0xC4' => '0xD6B4',		'0xC5' => '0xD6B5',		'0xC6' => '0xD6B6',
2644
				'0xC7' => '0xD6B7',		'0xC8' => '0xD6B8',		'0xC9' => '0xD6B9',
2645
				'0xCB' => '0xD6BB',		'0xCC' => '0xD6BC',		'0xCD' => '0xD6BD',
2646
				'0xCE' => '0xD6BE',		'0xCF' => '0xD6BF',		'0xD0' => '0xD780',
2647
				'0xD2' => '0xD782',		'0xE3' => '0xD793',		'0xE4' => '0xD794',
2648
				'0xE5' => '0xD795',		'0xE7' => '0xD797',		'0xE9' => '0xD799',
2649
				'0xFD' => '0xE2808E',	'0xFE' => '0xE2808F',	'0x92' => '0xE28099',
2650
				'0x83' => '0xC692',		'0xD3' => '0xD783',		'0x88' => '0xCB86',
2651
				'0x98' => '0xCB9C',		'0x91' => '0xE28098',	'0x96' => '0xE28093',
2652
				'0xBA' => '0xC3B7',		'0x9B' => '0xE280BA',	'0xAA' => '0xC397',
2653
				'0xA4' => '0xE282AA',	'0xE1' => '0xD791',		'0xE6' => '0xD796',
2654
				'0xE8' => '0xD798',		'0xEB' => '0xD79B',		'0xF4' => '0xD7A4',
2655
				'0xFA' => '0xD7AA',
2656
			),
2657
			'windows-1253' => array(
2658
				'0x81' => '\'\'',			'0x88' => '\'\'',			'0x8A' => '\'\'',
2659
				'0x8C' => '\'\'',			'0x8D' => '\'\'',			'0x8E' => '\'\'',
2660
				'0x8F' => '\'\'',			'0x90' => '\'\'',			'0x98' => '\'\'',
2661
				'0x9A' => '\'\'',			'0x9C' => '\'\'',			'0x9D' => '\'\'',
2662
				'0x9E' => '\'\'',			'0x9F' => '\'\'',			'0xAA' => '\'\'',
2663
				'0xD2' => '0xE282AC',			'0xFF' => '0xCE92',			'0xCE' => '0xCE9E',
2664
				'0xB8' => '0xCE88',		'0xBA' => '0xCE8A',		'0xBC' => '0xCE8C',
2665
				'0xBE' => '0xCE8E',		'0xBF' => '0xCE8F',		'0xC0' => '0xCE90',
2666
				'0xC8' => '0xCE98',		'0xCA' => '0xCE9A',		'0xCC' => '0xCE9C',
2667
				'0xCD' => '0xCE9D',		'0xCF' => '0xCE9F',		'0xDA' => '0xCEAA',
2668
				'0xE8' => '0xCEB8',		'0xEA' => '0xCEBA',		'0xEC' => '0xCEBC',
2669
				'0xEE' => '0xCEBE',		'0xEF' => '0xCEBF',		'0xC2' => '0xFF',
2670
				'0xBD' => '0xC2BD',		'0xED' => '0xCEBD',		'0xB2' => '0xC2B2',
2671
				'0xA0' => '0xC2A0',		'0xA3' => '0xC2A3',		'0xA4' => '0xC2A4',
2672
				'0xA5' => '0xC2A5',		'0xA6' => '0xC2A6',		'0xA7' => '0xC2A7',
2673
				'0xA8' => '0xC2A8',		'0xA9' => '0xC2A9',		'0xAB' => '0xC2AB',
2674
				'0xAC' => '0xC2AC',		'0xAD' => '0xC2AD',		'0xAE' => '0xC2AE',
2675
				'0xB0' => '0xC2B0',		'0xB1' => '0xC2B1',		'0xB3' => '0xC2B3',
2676
				'0xB5' => '0xC2B5',		'0xB6' => '0xC2B6',		'0xB7' => '0xC2B7',
2677
				'0xBB' => '0xC2BB',		'0xE2' => '0xCEB2',		'0x80' => '0xD2',
2678
				'0x82' => '0xE2809A',	'0x84' => '0xE2809E',	'0x85' => '0xE280A6',
2679
				'0x86' => '0xE280A0',	'0xA1' => '0xCE85',		'0xA2' => '0xCE86',
2680
				'0x87' => '0xE280A1',	'0x89' => '0xE280B0',	'0xB9' => '0xCE89',
2681
				'0x8B' => '0xE280B9',	'0x91' => '0xE28098',	'0x99' => '0xE284A2',
2682
				'0x92' => '0xE28099',	'0x93' => '0xE2809C',	'0x94' => '0xE2809D',
2683
				'0x95' => '0xE280A2',	'0x96' => '0xE28093',	'0x97' => '0xE28094',
2684
				'0x9B' => '0xE280BA',	'0xAF' => '0xE28095',	'0xB4' => '0xCE84',
2685
				'0xC1' => '0xCE91',		'0xC3' => '0xCE93',		'0xC4' => '0xCE94',
2686
				'0xC5' => '0xCE95',		'0xC6' => '0xCE96',		'0x83' => '0xC692',
2687
				'0xC7' => '0xCE97',		'0xC9' => '0xCE99',		'0xCB' => '0xCE9B',
2688
				'0xD0' => '0xCEA0',		'0xD1' => '0xCEA1',		'0xD3' => '0xCEA3',
2689
				'0xD4' => '0xCEA4',		'0xD5' => '0xCEA5',		'0xD6' => '0xCEA6',
2690
				'0xD7' => '0xCEA7',		'0xD8' => '0xCEA8',		'0xD9' => '0xCEA9',
2691
				'0xDB' => '0xCEAB',		'0xDC' => '0xCEAC',		'0xDD' => '0xCEAD',
2692
				'0xDE' => '0xCEAE',		'0xDF' => '0xCEAF',		'0xE0' => '0xCEB0',
2693
				'0xE1' => '0xCEB1',		'0xE3' => '0xCEB3',		'0xE4' => '0xCEB4',
2694
				'0xE5' => '0xCEB5',		'0xE6' => '0xCEB6',		'0xE7' => '0xCEB7',
2695
				'0xE9' => '0xCEB9',		'0xEB' => '0xCEBB',		'0xF0' => '0xCF80',
2696
				'0xF1' => '0xCF81',		'0xF2' => '0xCF82',		'0xF3' => '0xCF83',
2697
				'0xF4' => '0xCF84',		'0xF5' => '0xCF85',		'0xF6' => '0xCF86',
2698
				'0xF7' => '0xCF87',		'0xF8' => '0xCF88',		'0xF9' => '0xCF89',
2699
				'0xFA' => '0xCF8A',		'0xFB' => '0xCF8B',		'0xFC' => '0xCF8C',
2700
				'0xFD' => '0xCF8D',		'0xFE' => '0xCF8E',
2701
			),
2702
		);
2703
2704
		// Make some preparations.
2705
		if (isset($translation_tables[$upcontext['charset_detected']]))
2706
		{
2707
			$replace = '%field%';
2708
2709
			// Build a huge REPLACE statement...
2710
			foreach ($translation_tables[$upcontext['charset_detected']] as $from => $to)
2711
				$replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')';
2712
		}
2713
2714
		// Get a list of table names ahead of time... This makes it easier to set our substep and such
2715
		db_extend();
2716
		$queryTables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
2717
2718
		$upcontext['table_count'] = count($queryTables);
2719
2720
		// What ones have we already done?
2721
		foreach ($queryTables as $id => $table)
2722
			if ($id < $_GET['substep'])
2723
				$upcontext['previous_tables'][] = $table;
2724
2725
		$upcontext['cur_table_num'] = $_GET['substep'];
2726
		$upcontext['cur_table_name'] = str_replace($db_prefix, '', $queryTables[$_GET['substep']]);
2727
		$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2728
2729
		// Make sure we're ready & have painted the template before proceeding
2730
		if ($support_js && !isset($_GET['xml'])) {
2731
			$_GET['substep'] = 0;
2732
			return false;
2733
		}
2734
2735
		// We want to start at the first table.
2736
		for ($substep = $_GET['substep'], $n = count($queryTables); $substep < $n; $substep++)
2737
		{
2738
			$table = $queryTables[$substep];
2739
2740
			$getTableStatus = $smcFunc['db_query']('', '
2741
				SHOW TABLE STATUS
2742
				LIKE {string:table_name}',
2743
				array(
2744
					'table_name' => str_replace('_', '\_', $table)
2745
				)
2746
			);
2747
2748
			// Only one row so we can just fetch_assoc and free the result...
2749
			$table_info = $smcFunc['db_fetch_assoc']($getTableStatus);
2750
			$smcFunc['db_free_result']($getTableStatus);
2751
2752
			$upcontext['cur_table_name'] = str_replace($db_prefix, '', (isset($queryTables[$substep + 1]) ? $queryTables[$substep + 1] : $queryTables[$substep]));
2753
			$upcontext['cur_table_num'] = $substep + 1;
2754
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2755
2756
			// Do we need to pause?
2757
			nextSubstep($substep);
2758
2759
			// Just to make sure it doesn't time out.
2760
			if (function_exists('apache_reset_timeout'))
2761
				@apache_reset_timeout();
2762
2763
			$table_charsets = array();
2764
2765
			// Loop through each column.
2766
			$queryColumns = $smcFunc['db_query']('', '
2767
				SHOW FULL COLUMNS
2768
				FROM ' . $table_info['Name'],
2769
				array(
2770
				)
2771
			);
2772
			while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns))
2773
			{
2774
				// Only text'ish columns have a character set and need converting.
2775
				if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false)
2776
				{
2777
					$collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation'];
2778
					if (!empty($collation) && $collation !== 'NULL')
2779
					{
2780
						list($charset) = explode('_', $collation);
2781
2782
						// Build structure of columns to operate on organized by charset; only operate on columns not yet utf8
2783
						if ($charset != 'utf8') {
2784
							if (!isset($table_charsets[$charset]))
2785
								$table_charsets[$charset] = array();
2786
2787
							$table_charsets[$charset][] = $column_info;
2788
						}
2789
					}
2790
				}
2791
			}
2792
			$smcFunc['db_free_result']($queryColumns);
2793
2794
			// Only change the non-utf8 columns identified above
2795
			if (count($table_charsets) > 0)
2796
			{
2797
				$updates_blob = '';
2798
				$updates_text = '';
2799
				foreach ($table_charsets as $charset => $columns)
2800
				{
2801
					if ($charset !== $charsets[$upcontext['charset_detected']])
2802
					{
2803
						foreach ($columns as $column)
2804
						{
2805
							$updates_blob .= '
2806
								CHANGE COLUMN `' . $column['Field'] . '` `' . $column['Field'] . '` ' . strtr($column['Type'], array('text' => 'blob', 'char' => 'binary')) . ($column['Null'] === 'YES' ? ' NULL' : ' NOT NULL') . (strpos($column['Type'], 'char') === false ? '' : ' default \'' . $column['Default'] . '\'') . ',';
2807
							$updates_text .= '
2808
								CHANGE COLUMN `' . $column['Field'] . '` `' . $column['Field'] . '` ' . $column['Type'] . ' CHARACTER SET ' . $charsets[$upcontext['charset_detected']] . ($column['Null'] === 'YES' ? '' : ' NOT NULL') . (strpos($column['Type'], 'char') === false ? '' : ' default \'' . $column['Default'] . '\'') . ',';
2809
						}
2810
					}
2811
				}
2812
2813
				// Change the columns to binary form.
2814
				$smcFunc['db_query']('', '
2815
					ALTER TABLE {raw:table_name}{raw:updates_blob}',
2816
					array(
2817
						'table_name' => $table_info['Name'],
2818
						'updates_blob' => substr($updates_blob, 0, -1),
2819
					)
2820
				);
2821
2822
				// Convert the character set if MySQL has no native support for it.
2823
				if (isset($translation_tables[$upcontext['charset_detected']]))
2824
				{
2825
					$update = '';
2826
					foreach ($table_charsets as $charset => $columns)
2827
						foreach ($columns as $column)
2828
							$update .= '
2829
								' . $column['Field'] . ' = ' . strtr($replace, array('%field%' => $column['Field'])) . ',';
2830
2831
					$smcFunc['db_query']('', '
2832
						UPDATE {raw:table_name}
2833
						SET {raw:updates}',
2834
						array(
2835
							'table_name' => $table_info['Name'],
2836
							'updates' => substr($update, 0, -1),
2837
						)
2838
					);
2839
				}
2840
2841
				// Change the columns back, but with the proper character set.
2842
				$smcFunc['db_query']('', '
2843
					ALTER TABLE {raw:table_name}{raw:updates_text}',
2844
					array(
2845
						'table_name' => $table_info['Name'],
2846
						'updates_text' => substr($updates_text, 0, -1),
2847
					)
2848
				);
2849
			}
2850
2851
			// Now do the actual conversion (if still needed).
2852
			if ($charsets[$upcontext['charset_detected']] !== 'utf8')
2853
			{
2854
				if ($command_line)
2855
					echo 'Converting table ' . $table_info['Name'] . ' to UTF-8...';
2856
2857
				$smcFunc['db_query']('', '
2858
					ALTER TABLE {raw:table_name}
2859
					CONVERT TO CHARACTER SET utf8',
2860
					array(
2861
						'table_name' => $table_info['Name'],
2862
					)
2863
				);
2864
2865
				if ($command_line)
2866
					echo " done.\n";
2867
			}
2868
			// If this is XML to keep it nice for the user do one table at a time anyway!
2869
			if (isset($_GET['xml']))
2870
				return upgradeExit();
2871
		}
2872
2873
		$prev_charset = empty($translation_tables[$upcontext['charset_detected']]) ? $charsets[$upcontext['charset_detected']] : $translation_tables[$upcontext['charset_detected']];
2874
2875
		$smcFunc['db_insert']('replace',
2876
			'{db_prefix}settings',
2877
			array('variable' => 'string', 'value' => 'string'),
2878
			array(array('global_character_set', 'UTF-8'), array('previousCharacterSet', $prev_charset)),
2879
			array('variable')
2880
		);
2881
2882
		// Store it in Settings.php too because it's needed before db connection.
2883
		// Hopefully this works...
2884
		require_once($sourcedir . '/Subs-Admin.php');
2885
		updateSettingsFile(array('db_character_set' => '\'utf8\''));
2886
2887
		// The conversion might have messed up some serialized strings. Fix them!
2888
		$request = $smcFunc['db_query']('', '
2889
			SELECT id_action, extra
2890
			FROM {db_prefix}log_actions
2891
			WHERE action IN ({string:remove}, {string:delete})',
2892
			array(
2893
				'remove' => 'remove',
2894
				'delete' => 'delete',
2895
			)
2896
		);
2897 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
2898
		{
2899
			if (@safe_unserialize($row['extra']) === false && preg_match('~^(a:3:{s:5:"topic";i:\d+;s:7:"subject";s:)(\d+):"(.+)"(;s:6:"member";s:5:"\d+";})$~', $row['extra'], $matches) === 1)
2900
				$smcFunc['db_query']('', '
2901
					UPDATE {db_prefix}log_actions
2902
					SET extra = {string:extra}
2903
					WHERE id_action = {int:current_action}',
2904
					array(
2905
						'current_action' => $row['id_action'],
2906
						'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
2907
					)
2908
				);
2909
		}
2910
		$smcFunc['db_free_result']($request);
2911
2912
		if ($upcontext['dropping_index'] && $command_line)
2913
		{
2914
			echo "\nYour fulltext search index was dropped to facilitate the conversion. You will need to recreate it.";
2915
			flush();
2916
		}
2917
	}
2918
	$_GET['substep'] = 0;
2919
	return false;
2920
}
2921
2922
function serialize_to_json()
2923
{
2924
	global $command_line, $smcFunc, $modSettings, $sourcedir, $upcontext, $support_js;
2925
2926
	$upcontext['sub_template'] = isset($_GET['xml']) ? 'serialize_json_xml' : 'serialize_json';
2927
	// First thing's first - did we already do this?
2928
	if (!empty($modSettings['json_done']))
2929
	{
2930
		if ($command_line)
2931
			return DeleteUpgrade();
2932
		else
2933
			return true;
2934
	}
2935
2936
	// Done it already - js wise?
2937
	if (!empty($_POST['json_done']))
2938
		return true;
2939
2940
	// List of tables affected by this function
2941
	// name => array('key', col1[,col2|true[,col3]])
2942
	// If 3rd item in array is true, it indicates that col1 could be empty...
2943
	$tables = array(
2944
		'background_tasks' => array('id_task', 'task_data'),
2945
		'log_actions' => array('id_action', 'extra'),
2946
		'log_online' => array('session', 'url'),
2947
		'log_packages' => array('id_install', 'db_changes', 'failed_steps', 'credits'),
2948
		'log_spider_hits' => array('id_hit', 'url'),
2949
		'log_subscribed' => array('id_sublog', 'pending_details'),
2950
		'pm_rules' => array('id_rule', 'criteria', 'actions'),
2951
		'qanda' => array('id_question', 'answers'),
2952
		'subscriptions' => array('id_subscribe', 'cost'),
2953
		'user_alerts' => array('id_alert', 'extra', true),
2954
		'user_drafts' => array('id_draft', 'to_list', true),
2955
		// These last two are a bit different - we'll handle those separately
2956
		'settings' => array(),
2957
		'themes' => array()
2958
	);
2959
2960
	// Set up some context stuff...
2961
	// Because we're not using numeric indices, we need this to figure out the current table name...
2962
	$keys = array_keys($tables);
2963
2964
	$upcontext['page_title'] = 'Converting to JSON';
2965
	$upcontext['table_count'] = count($keys);
2966
	$upcontext['cur_table_num'] = $_GET['substep'];
2967
	$upcontext['cur_table_name'] = isset($keys[$_GET['substep']]) ? $keys[$_GET['substep']] : $keys[0];
2968
	$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2969
2970 View Code Duplication
	foreach ($keys as $id => $table)
2971
		if ($id < $_GET['substep'])
2972
			$upcontext['previous_tables'][] = $table;
2973
2974
	if ($command_line)
2975
		echo 'Converting data from serialize() to json_encode().';
2976
2977
	if (!$support_js || isset($_GET['xml']))
2978
	{
2979
		// Fix the data in each table
2980
		for ($substep = $_GET['substep']; $substep < $upcontext['table_count']; $substep++)
2981
		{
2982
			$upcontext['cur_table_name'] = isset($keys[$substep + 1]) ? $keys[$substep + 1] : $keys[$substep];
2983
			$upcontext['cur_table_num'] = $substep + 1;
2984
2985
			$upcontext['step_progress'] = (int) (($upcontext['cur_table_num'] / $upcontext['table_count']) * 100);
2986
2987
			// Do we need to pause?
2988
			nextSubstep($substep);
2989
2990
			// Initialize a few things...
2991
			$where = '';
2992
			$vars = array();
2993
			$table = $keys[$substep];
2994
			$info = $tables[$table];
2995
2996
			// Now the fun - build our queries and all that fun stuff
2997
			if ($table == 'settings')
2998
			{
2999
				// Now a few settings...
3000
				$serialized_settings = array(
3001
					'attachment_basedirectories',
3002
					'attachmentUploadDir',
3003
					'cal_today_birthday',
3004
					'cal_today_event',
3005
					'cal_today_holiday',
3006
					'displayFields',
3007
					'last_attachments_directory',
3008
					'memberlist_cache',
3009
					'search_index_custom_config',
3010
					'spider_name_cache'
3011
				);
3012
3013
				// Loop through and fix these...
3014
				$new_settings = array();
3015
				if ($command_line)
3016
					echo "\n" . 'Fixing some settings...';
3017
3018
				foreach ($serialized_settings as $var)
3019
				{
3020
					if (isset($modSettings[$var]))
3021
					{
3022
						// Attempt to unserialize the setting
3023
						$temp = @safe_unserialize($modSettings[$var]);
3024
						if (!$temp && $command_line)
3025
							echo "\n - Failed to unserialize the '" . $var . "' setting. Skipping.";
3026
						elseif ($temp !== false)
3027
							$new_settings[$var] = json_encode($temp);
3028
					}
3029
				}
3030
3031
				// Update everything at once
3032
				if (!function_exists('cache_put_data'))
3033
					require_once($sourcedir . '/Load.php');
3034
				updateSettings($new_settings, true);
3035
3036
				if ($command_line)
3037
					echo ' done.';
3038
			}
3039
			elseif ($table == 'themes')
3040
			{
3041
				// Finally, fix the admin prefs. Unfortunately this is stored per theme, but hopefully they only have one theme installed at this point...
3042
				$query = $smcFunc['db_query']('', '
3043
					SELECT id_member, id_theme, value FROM {db_prefix}themes
3044
					WHERE variable = {string:admin_prefs}',
3045
						array(
3046
							'admin_prefs' => 'admin_preferences'
3047
						)
3048
				);
3049
3050
				if ($smcFunc['db_num_rows']($query) != 0)
3051
				{
3052
					while ($row = $smcFunc['db_fetch_assoc']($query))
3053
					{
3054
						$temp = @safe_unserialize($row['value']);
3055
3056
						if ($command_line)
3057
						{
3058
							if ($temp === false)
3059
								echo "\n" . 'Unserialize of admin_preferences for user ' . $row['id_member'] . ' failed. Skipping.';
3060
							else
3061
								echo "\n" . 'Fixing admin preferences...';
3062
						}
3063
3064
						if ($temp !== false)
3065
						{
3066
							$row['value'] = json_encode($temp);
3067
3068
							// Even though we have all values from the table, UPDATE is still faster than REPLACE
3069
							$smcFunc['db_query']('', '
3070
								UPDATE {db_prefix}themes
3071
								SET value = {string:prefs}
3072
								WHERE id_theme = {int:theme}
3073
									AND id_member = {int:member}
3074
									AND variable = {string:admin_prefs}',
3075
								array(
3076
									'prefs' => $row['value'],
3077
									'theme' => $row['id_theme'],
3078
									'member' => $row['id_member'],
3079
									'admin_prefs' => 'admin_preferences'
3080
								)
3081
							);
3082
3083
							if ($command_line)
3084
								echo ' done.';
3085
						}
3086
					}
3087
3088
					$smcFunc['db_free_result']($query);
3089
				}
3090
			}
3091
			else
3092
			{
3093
				// First item is always the key...
3094
				$key = $info[0];
3095
				unset($info[0]);
3096
3097
				// Now we know what columns we have and such...
3098
				if (count($info) == 2 && $info[2] === true)
3099
				{
3100
					$col_select = $info[1];
3101
					$where = ' WHERE ' . $info[1] . ' != {empty}';
3102
				}
3103
				else
3104
				{
3105
					$col_select = implode(', ', $info);
3106
				}
3107
3108
				$query = $smcFunc['db_query']('', '
3109
					SELECT ' . $key . ', ' . $col_select . '
3110
					FROM {db_prefix}' . $table . $where,
3111
					array()
3112
				);
3113
3114
				if ($smcFunc['db_num_rows']($query) != 0)
3115
				{
3116
					if ($command_line)
3117
					{
3118
						echo "\n" . ' +++ Fixing the "' . $table . '" table...';
3119
						flush();
3120
					}
3121
3122
					while ($row = $smcFunc['db_fetch_assoc']($query))
3123
					{
3124
						$update = '';
3125
3126
						// We already know what our key is...
3127
						foreach ($info as $col)
3128
						{
3129
							if ($col !== true && $row[$col] != '')
3130
							{
3131
								$temp = @safe_unserialize($row[$col]);
3132
3133
								if ($temp === false && $command_line)
3134
								{
3135
									echo "\nFailed to unserialize " . $row[$col] . "... Skipping\n";
3136
								}
3137
								else
3138
								{
3139
									$row[$col] = json_encode($temp);
3140
3141
									// Build our SET string and variables array
3142
									$update .= (empty($update) ? '' : ', ') . $col . ' = {string:' . $col . '}';
3143
									$vars[$col] = $row[$col];
3144
								}
3145
							}
3146
						}
3147
3148
						$vars[$key] = $row[$key];
3149
3150
						// In a few cases, we might have empty data, so don't try to update in those situations...
3151
						if (!empty($update))
3152
						{
3153
							$smcFunc['db_query']('', '
3154
								UPDATE {db_prefix}' . $table . '
3155
								SET ' . $update . '
3156
								WHERE ' . $key . ' = {' . ($key == 'session' ? 'string' : 'int') . ':' . $key . '}',
3157
								$vars
3158
							);
3159
						}
3160
					}
3161
3162
					if ($command_line)
3163
						echo ' done.';
3164
3165
					// Free up some memory...
3166
					$smcFunc['db_free_result']($query);
3167
				}
3168
			}
3169
			// If this is XML to keep it nice for the user do one table at a time anyway!
3170
			if (isset($_GET['xml']))
3171
				return upgradeExit();
3172
		}
3173
3174
		if ($command_line)
3175
		{
3176
			echo "\n" . 'Successful.' . "\n";
3177
			flush();
3178
		}
3179
		$upcontext['step_progress'] = 100;
3180
3181
		// Last but not least, insert a dummy setting so we don't have to do this again in the future...
3182
		updateSettings(array('json_done' => true));
3183
3184
		$_GET['substep'] = 0;
3185
		// Make sure we move on!
3186
		if ($command_line)
3187
			return DeleteUpgrade();
3188
3189
		return true;
3190
	}
3191
3192
	// If this fails we just move on to deleting the upgrade anyway...
3193
	$_GET['substep'] = 0;
3194
	return false;
3195
}
3196
3197
/******************************************************************************
3198
******************* Templates are below this point ****************************
3199
******************************************************************************/
3200
3201
// This is what is displayed if there's any chmod to be done. If not it returns nothing...
3202
function template_chmod()
3203
{
3204
	global $upcontext, $txt, $settings;
3205
3206
	// Don't call me twice!
3207
	if (!empty($upcontext['chmod_called']))
3208
		return;
3209
3210
	$upcontext['chmod_called'] = true;
3211
3212
	// Nothing?
3213
	if (empty($upcontext['chmod']['files']) && empty($upcontext['chmod']['ftp_error']))
3214
		return;
3215
3216
	// Was it a problem with Windows?
3217
	if (!empty($upcontext['chmod']['ftp_error']) && $upcontext['chmod']['ftp_error'] == 'total_mess')
3218
	{
3219
		echo '
3220
			<div class="error_message red">
3221
				The following files need to be writable to continue the upgrade. Please ensure the Windows permissions are correctly set to allow this:<br>
3222
				<ul style="margin: 2.5ex; font-family: monospace;">
3223
					<li>' . implode('</li>
3224
					<li>', $upcontext['chmod']['files']) . '</li>
3225
				</ul>
3226
			</div>';
3227
3228
		return false;
3229
	}
3230
3231
	echo '
3232
		<div class="panel">
3233
			<h2>Your FTP connection information</h2>
3234
			<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>
3235
			<script>
3236
				function warning_popup()
3237
				{
3238
					popup = window.open(\'\',\'popup\',\'height=150,width=400,scrollbars=yes\');
3239
					var content = popup.document;
3240
					content.write(\'<!DOCTYPE html>\n\');
3241
					content.write(\'<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>\n\t<head>\n\t\t<meta name="robots" content="noindex">\n\t\t\');
3242
					content.write(\'<title>Warning</title>\n\t\t<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css">\n\t</head>\n\t<body id="popup">\n\t\t\');
3243
					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\');
3244
					content.write(\'<p>', implode('<br>\n\t\t\t', $upcontext['chmod']['files']), '</p>\n\t\t\t\');';
3245
3246
	if (isset($upcontext['systemos']) && $upcontext['systemos'] == 'linux')
3247
		echo '
3248
					content.write(\'<hr>\n\t\t\t\');
3249
					content.write(\'<p>If you have a shell account, the convenient below command can automatically correct permissions on these files</p>\n\t\t\t\');
3250
					content.write(\'<tt># chmod a+w ', implode(' ', $upcontext['chmod']['files']), '</tt>\n\t\t\t\');';
3251
3252
	echo '
3253
					content.write(\'<a href="javascript:self.close();">close</a>\n\t\t</div>\n\t</body>\n</html>\');
3254
					content.close();
3255
				}
3256
			</script>';
3257
3258
	if (!empty($upcontext['chmod']['ftp_error']))
3259
		echo '
3260
			<div class="error_message red">
3261
				The following error was encountered when trying to connect:<br><br>
3262
				<code>', $upcontext['chmod']['ftp_error'], '</code>
3263
			</div>
3264
			<br>';
3265
3266
	if (empty($upcontext['chmod_in_form']))
3267
		echo '
3268
	<form action="', $upcontext['form_url'], '" method="post">';
3269
3270
	echo '
3271
		<table width="520" border="0" align="center" style="margin-bottom: 1ex;">
3272
			<tr>
3273
				<td width="26%" valign="top" class="textbox"><label for="ftp_server">', $txt['ftp_server'], ':</label></td>
3274
				<td>
3275
					<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>
3276
					<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">
3277
					<div class="smalltext block">', $txt['ftp_server_info'], '</div>
3278
				</td>
3279
			</tr><tr>
3280
				<td width="26%" valign="top" class="textbox"><label for="ftp_username">', $txt['ftp_username'], ':</label></td>
3281
				<td>
3282
					<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">
3283
					<div class="smalltext block">', $txt['ftp_username_info'], '</div>
3284
				</td>
3285
			</tr><tr>
3286
				<td width="26%" valign="top" class="textbox"><label for="ftp_password">', $txt['ftp_password'], ':</label></td>
3287
				<td>
3288
					<input type="password" size="50" name="ftp_password" id="ftp_password" style="width: 99%;" class="input_password">
3289
					<div class="smalltext block">', $txt['ftp_password_info'], '</div>
3290
				</td>
3291
			</tr><tr>
3292
				<td width="26%" valign="top" class="textbox"><label for="ftp_path">', $txt['ftp_path'], ':</label></td>
3293
				<td style="padding-bottom: 1ex;">
3294
					<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">
3295
					<div class="smalltext block">', !empty($upcontext['chmod']['path']) ? $txt['ftp_path_found_info'] : $txt['ftp_path_info'], '</div>
3296
				</td>
3297
			</tr>
3298
		</table>
3299
3300
		<div class="righttext" style="margin: 1ex;"><input type="submit" value="', $txt['ftp_connect'], '" class="button_submit"></div>
3301
	</div>';
3302
3303
	if (empty($upcontext['chmod_in_form']))
3304
		echo '
3305
	</form>';
3306
}
3307
3308
function template_upgrade_above()
3309
{
3310
	global $modSettings, $txt, $settings, $upcontext, $upgradeurl;
3311
3312
	echo '<!DOCTYPE html>
3313
<html', $txt['lang_rtl'] == true ? ' dir="rtl"' : '', '>
3314
	<head>
3315
		<meta charset="', isset($txt['lang_character_set']) ? $txt['lang_character_set'] : 'UTF-8', '">
3316
		<meta name="robots" content="noindex">
3317
		<title>', $txt['upgrade_upgrade_utility'], '</title>
3318
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/index.css?alp21">
3319
		<link rel="stylesheet" href="', $settings['default_theme_url'], '/css/install.css?alp21">
3320
		', $txt['lang_rtl'] == true ? '<link rel="stylesheet" href="' . $settings['default_theme_url'] . '/css/rtl.css?alp21">' : '', '
3321
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
3322
		<script src="', $settings['default_theme_url'], '/scripts/script.js"></script>
3323
		<script>
3324
			var smf_scripturl = \'', $upgradeurl, '\';
3325
			var smf_charset = \'', (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'UTF-8' : $txt['lang_character_set']) : $modSettings['global_character_set']), '\';
3326
			var startPercent = ', $upcontext['overall_percent'], ';
3327
3328
			// This function dynamically updates the step progress bar - and overall one as required.
3329
			function updateStepProgress(current, max, overall_weight)
3330
			{
3331
				// What out the actual percent.
3332
				var width = parseInt((current / max) * 100);
3333
				if (document.getElementById(\'step_progress\'))
3334
				{
3335
					document.getElementById(\'step_progress\').style.width = width + "%";
3336
					setInnerHTML(document.getElementById(\'step_text\'), width + "%");
3337
				}
3338
				if (overall_weight && document.getElementById(\'overall_progress\'))
3339
				{
3340
					overall_width = parseInt(startPercent + width * (overall_weight / 100));
3341
					document.getElementById(\'overall_progress\').style.width = overall_width + "%";
3342
					setInnerHTML(document.getElementById(\'overall_text\'), overall_width + "%");
3343
				}
3344
			}
3345
		</script>
3346
	</head>
3347
	<body>
3348
	<div id="footerfix">
3349
		<div id="header">
3350
			<h1 class="forumtitle">', $txt['upgrade_upgrade_utility'], '</h1>
3351
			<img id="smflogo" src="', $settings['default_theme_url'], '/images/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum">
3352
		</div>
3353
	<div id="wrapper">
3354
		<div id="upper_section">
3355
			<div id="inner_section">
3356
				<div id="inner_wrap">
3357
				</div>
3358
			</div>
3359
		</div>
3360
		<div id="content_section">
3361
		<div id="main_content_section">
3362
			<div id="main_steps">
3363
				<h2>', $txt['upgrade_progress'], '</h2>
3364
				<ul>';
3365
3366 View Code Duplication
	foreach ($upcontext['steps'] as $num => $step)
3367
		echo '
3368
						<li class="', $num < $upcontext['current_step'] ? 'stepdone' : ($num == $upcontext['current_step'] ? 'stepcurrent' : 'stepwaiting'), '">', $txt['upgrade_step'], ' ', $step[0], ': ', $step[1], '</li>';
3369
3370
	echo '
3371
					</ul>
3372
			</div>
3373
3374
			<div id="progress_bar">
3375
				<div id="overall_text">', $upcontext['overall_percent'], '%</div>
3376
				<div id="overall_progress" style="width: ', $upcontext['overall_percent'], '%;">
3377
					<span>', $txt['upgrade_overall_progress'], '</span>
3378
				</div>
3379
			</div>';
3380
3381
	if (isset($upcontext['step_progress']))
3382
		echo '
3383
				<br>
3384
				<br>
3385
				<div id="progress_bar_step">
3386
					<div id="step_text">', $upcontext['step_progress'], '%</div>
3387
					<div id="step_progress" style="width: ', $upcontext['step_progress'], '%;background-color: #ffd000;">
3388
						<span>', $txt['upgrade_step_progress'], '</span>
3389
					</div>
3390
				</div>';
3391
3392
	echo '
3393
				<div id="substep_bar_div" class="smalltext" style="float: left;width: 50%;margin-top: 0.6em;display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">', isset($upcontext['substep_progress_name']) ? trim(strtr($upcontext['substep_progress_name'], array('.' => ''))) : '', ':</div>
3394
				<div id="substep_bar_div2" style="float: left;font-size: 8pt; height: 12pt; border: 1px solid black; background-color: white; width: 33%; margin: 0.6em auto 0 6em; display: ', isset($upcontext['substep_progress']) ? '' : 'none', ';">
3395
					<div id="substep_text" style="color: #000; position: absolute; margin-left: -5em;">', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : '', '%</div>
3396
					<div id="substep_progress" style="width: ', isset($upcontext['substep_progress']) ? $upcontext['substep_progress'] : 0, '%; height: 12pt; z-index: 1; background-color: #eebaf4;">&nbsp;</div>
3397
				</div>';
3398
3399
	// How long have we been running this?
3400
	$elapsed = time() - $upcontext['started'];
3401
	$mins = (int) ($elapsed / 60);
3402
	$seconds = $elapsed - $mins * 60;
3403
	echo '
3404
								<br> <br> <br> <br> <br>
3405
								<div class="smalltext" style="padding: 5px; text-align: center;"><br>', $txt['upgrade_time_elapsed'], ':
3406
									<span id="mins_elapsed">', $mins, '</span> ', $txt['upgrade_time_mins'], ', <span id="secs_elapsed">', $seconds, '</span> ', $txt['upgrade_time_secs'], '.
3407
								</div>';
3408
	echo '
3409
			</div>
3410
			</div>
3411
			<div id="main_screen" class="clear">
3412
				<h2>', $upcontext['page_title'], '</h2>
3413
				<div class="panel">
3414
					<div style="max-height: 360px; overflow: auto;">';
3415
}
3416
3417
function template_upgrade_below()
3418
{
3419
	global $upcontext, $txt;
3420
3421
	if (!empty($upcontext['pause']))
3422
		echo '
3423
								<em>', $txt['upgrade_incomplete'], '.</em><br>
3424
3425
								<h2 style="margin-top: 2ex;">', $txt['upgrade_not_quite_done'], '</h2>
3426
								<h3>
3427
									', $txt['upgrade_paused_overload'], '
3428
								</h3>';
3429
3430
	if (!empty($upcontext['custom_warning']))
3431
		echo '
3432
								<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3433
									<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3434
									<strong style="text-decoration: underline;">', $txt['upgrade_note'], '</strong><br>
3435
									<div style="padding-left: 6ex;">', $upcontext['custom_warning'], '</div>
3436
								</div>';
3437
3438
	echo '
3439
								<div class="righttext" style="margin: 1ex;">';
3440
3441
	if (!empty($upcontext['continue']))
3442
		echo '
3443
									<input type="submit" id="contbutt" name="contbutt" value="', $txt['upgrade_continue'], '"', $upcontext['continue'] == 2 ? ' disabled' : '', ' class="button_submit">';
3444
	if (!empty($upcontext['skip']))
3445
		echo '
3446
									<input type="submit" id="skip" name="skip" value="', $txt['upgrade_skip'], '" onclick="dontSubmit = true; document.getElementById(\'contbutt\').disabled = \'disabled\'; return true;" class="button_submit">';
3447
3448
	echo '
3449
								</div>
3450
							</form>
3451
						</div>
3452
				</div>
3453
			</div>
3454
			</div>
3455
		</div>
3456
		<div id="footer">
3457
			<ul>
3458
				<li class="copyright"><a href="https://www.simplemachines.org/" title="Simple Machines Forum" target="_blank" class="new_win">SMF &copy; 2017, Simple Machines</a></li>
3459
			</ul>
3460
		</div>
3461
	</body>
3462
</html>';
3463
3464
	// Are we on a pause?
3465
	if (!empty($upcontext['pause']))
3466
	{
3467
		echo '
3468
		<script>
3469
			window.onload = doAutoSubmit;
3470
			var countdown = 3;
3471
			var dontSubmit = false;
3472
3473
			function doAutoSubmit()
3474
			{
3475
				if (countdown == 0 && !dontSubmit)
3476
					document.upform.submit();
3477
				else if (countdown == -1)
3478
					return;
3479
3480
				document.getElementById(\'contbutt\').value = "', $txt['upgrade_continue'], ' (" + countdown + ")";
3481
				countdown--;
3482
3483
				setTimeout("doAutoSubmit();", 1000);
3484
			}
3485
		</script>';
3486
	}
3487
}
3488
3489
function template_xml_above()
3490
{
3491
	global $upcontext;
3492
3493
	echo '<', '?xml version="1.0" encoding="UTF-8"?', '>
3494
	<smf>';
3495
3496
	if (!empty($upcontext['get_data']))
3497
		foreach ($upcontext['get_data'] as $k => $v)
3498
			echo '
3499
		<get key="', $k, '">', $v, '</get>';
3500
}
3501
3502
function template_xml_below()
3503
{
3504
	echo '
3505
		</smf>';
3506
}
3507
3508
function template_error_message()
3509
{
3510
	global $upcontext;
3511
3512
	echo '
3513
	<div class="error_message red">
3514
		', $upcontext['error_msg'], '
3515
		<br>
3516
		<a href="', $_SERVER['PHP_SELF'], '">Click here to try again.</a>
3517
	</div>';
3518
}
3519
3520
function template_welcome_message()
3521
{
3522
	global $upcontext, $disable_security, $settings, $txt;
3523
3524
	echo '
3525
		<script src="https://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
3526
			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
3527
	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">
3528
		<input type="hidden" name="', $upcontext['login_token_var'], '" value="', $upcontext['login_token'], '">
3529
		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
3530
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3531
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3532
			<div style="padding-left: 6ex;">
3533
				', sprintf($txt['upgrade_warning_out_of_date'], SMF_VERSION), '
3534
			</div>
3535
		</div>';
3536
3537
	$upcontext['chmod_in_form'] = true;
3538
	template_chmod();
3539
3540
	// For large, pre 1.1 RC2 forums give them a warning about the possible impact of this upgrade!
3541
	if ($upcontext['is_large_forum'])
3542
		echo '
3543
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3544
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3545
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3546
			<div style="padding-left: 6ex;">
3547
				', $txt['upgrade_warning_lots_data'], '
3548
			</div>
3549
		</div>';
3550
3551
	// A warning message?
3552
	if (!empty($upcontext['warning']))
3553
		echo '
3554
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3555
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3556
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3557
			<div style="padding-left: 6ex;">
3558
				', $upcontext['warning'], '
3559
			</div>
3560
		</div>';
3561
3562
	// Paths are incorrect?
3563
	echo '
3564
		<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">
3565
			<div style="float: left; width: 2ex; font-size: 2em; color: black;">!!</div>
3566
			<strong style="text-decoration: underline;">', $txt['upgrade_critical_error'], '</strong><br>
3567
			<div style="padding-left: 6ex;">
3568
				', $txt['upgrade_error_script_js'], '
3569
			</div>
3570
		</div>';
3571
3572
	// Is there someone already doing this?
3573
	if (!empty($upcontext['user']['id']) && (time() - $upcontext['started'] < 72600 || time() - $upcontext['updated'] < 3600))
3574
	{
3575
		$ago = time() - $upcontext['started'];
3576
		if ($ago < 60)
3577
			$ago = $ago . ' seconds';
3578
		elseif ($ago < 3600)
3579
			$ago = (int) ($ago / 60) . ' minutes';
3580
		else
3581
			$ago = (int) ($ago / 3600) . ' hours';
3582
3583
		$active = time() - $upcontext['updated'];
3584
		if ($active < 60)
3585
			$updated = $active . ' seconds';
3586
		elseif ($active < 3600)
3587
			$updated = (int) ($active / 60) . ' minutes';
3588
		else
3589
			$updated = (int) ($active / 3600) . ' hours';
3590
3591
		echo '
3592
		<div style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9;">
3593
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3594
			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br>
3595
			<div style="padding-left: 6ex;">
3596
				&quot;', $upcontext['user']['name'], '&quot; has been running the upgrade script for the last ', $ago, ' - and was last active ', $updated, ' ago.';
3597
3598
		if ($active < 600)
3599
			echo '
3600
				We recommend that you do not run this script unless you are sure that ', $upcontext['user']['name'], ' has completed their upgrade.';
3601
3602
		if ($active > $upcontext['inactive_timeout'])
3603
			echo '
3604
				<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.';
3605
		else
3606
			echo '
3607
				<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!');
3608
3609
		echo '
3610
			</div>
3611
		</div>';
3612
	}
3613
3614
	echo '
3615
			<strong>Admin Login: ', $disable_security ? '(DISABLED)' : '', '</strong>
3616
			<h3>For security purposes please login with your admin account to proceed with the upgrade.</h3>
3617
			<table>
3618
				<tr valign="top">
3619
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Username:</strong></td>
3620
					<td>
3621
						<input type="text" name="user" value="', !empty($upcontext['username']) ? $upcontext['username'] : '', '"', $disable_security ? ' disabled' : '', ' class="input_text">';
3622
3623
	if (!empty($upcontext['username_incorrect']))
3624
		echo '
3625
						<div class="smalltext" style="color: red;">Username Incorrect</div>';
3626
3627
	echo '
3628
					</td>
3629
				</tr>
3630
				<tr valign="top">
3631
					<td><strong ', $disable_security ? 'style="color: gray;"' : '', '>Password:</strong></td>
3632
					<td>
3633
						<input type="password" name="passwrd" value=""', $disable_security ? ' disabled' : '', ' class="input_password">
3634
						<input type="hidden" name="hash_passwrd" value="">';
3635
3636
	if (!empty($upcontext['password_failed']))
3637
		echo '
3638
						<div class="smalltext" style="color: red;">Password Incorrect</div>';
3639
3640
	echo '
3641
					</td>
3642
				</tr>';
3643
3644
	// Can they continue?
3645
	if (!empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] >= $upcontext['inactive_timeout'] && $upcontext['user']['step'] > 1)
3646
	{
3647
		echo '
3648
				<tr>
3649
					<td colspan="2">
3650
						<label for="cont"><input type="checkbox" id="cont" name="cont" checked class="input_check">Continue from step reached during last execution of upgrade script.</label>
3651
					</td>
3652
				</tr>';
3653
	}
3654
3655
	echo '
3656
			</table><br>
3657
			<span class="smalltext">
3658
				<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 = false;&quot; with &quot;$disable_security = true;&quot; and refresh this page.
3659
			</span>
3660
			<input type="hidden" name="login_attempt" id="login_attempt" value="1">
3661
			<input type="hidden" name="js_works" id="js_works" value="0">';
3662
3663
	// Say we want the continue button!
3664
	$upcontext['continue'] = !empty($upcontext['user']['id']) && time() - $upcontext['user']['updated'] < $upcontext['inactive_timeout'] ? 2 : 1;
3665
3666
	// This defines whether javascript is going to work elsewhere :D
3667
	echo '
3668
		<script>
3669
			if (\'XMLHttpRequest\' in window && document.getElementById(\'js_works\'))
3670
				document.getElementById(\'js_works\').value = 1;
3671
3672
			// Latest version?
3673
			function smfCurrentVersion()
3674
			{
3675
				var smfVer, yourVer;
3676
3677
				if (!(\'smfVersion\' in window))
3678
					return;
3679
3680
				window.smfVersion = window.smfVersion.replace(/SMF\s?/g, \'\');
3681
3682
				smfVer = document.getElementById(\'smfVersion\');
3683
				yourVer = document.getElementById(\'yourVersion\');
3684
3685
				setInnerHTML(smfVer, window.smfVersion);
3686
3687
				var currentVersion = getInnerHTML(yourVer);
3688
				if (currentVersion < window.smfVersion)
3689
					document.getElementById(\'version_warning\').style.display = \'\';
3690
			}
3691
			addLoadEvent(smfCurrentVersion);
3692
3693
			// This checks that the script file even exists!
3694
			if (typeof(smfSelectText) == \'undefined\')
3695
				document.getElementById(\'js_script_missing_error\').style.display = \'\';
3696
3697
		</script>';
3698
}
3699
3700
function template_upgrade_options()
3701
{
3702
	global $upcontext, $modSettings, $db_prefix, $mmessage, $mtitle;
3703
3704
	echo '
3705
			<h3>Before the upgrade gets underway please review the options below - and hit continue when you\'re ready to begin.</h3>
3706
			<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform">';
3707
3708
	// Warning message?
3709
	if (!empty($upcontext['upgrade_options_warning']))
3710
		echo '
3711
		<div style="margin: 1ex; padding: 1ex; border: 1px dashed #cc3344; color: black; background-color: #ffe4e9;">
3712
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3713
			<strong style="text-decoration: underline;">Warning!</strong><br>
3714
			<div style="padding-left: 4ex;">
3715
				', $upcontext['upgrade_options_warning'], '
3716
			</div>
3717
		</div>';
3718
3719
	echo '
3720
				<table>
3721
					<tr valign="top">
3722
						<td width="2%">
3723
							<input type="checkbox" name="backup" id="backup" value="1" class="input_check">
3724
						</td>
3725
						<td width="100%">
3726
							<label for="backup">Backup tables in your database with the prefix &quot;backup_' . $db_prefix . '&quot;.</label> (recommended!)
3727
						</td>
3728
					</tr>
3729
					<tr valign="top">
3730
						<td width="2%">
3731
							<input type="checkbox" name="maint" id="maint" value="1" checked class="input_check">
3732
						</td>
3733
						<td width="100%">
3734
							<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>
3735
							<div id="mainmess" style="display: none;">
3736
								<strong class="smalltext">Maintenance Title: </strong><br>
3737
								<input type="text" name="maintitle" size="30" value="', htmlspecialchars($mtitle), '" class="input_text"><br>
3738
								<strong class="smalltext">Maintenance Message: </strong><br>
3739
								<textarea name="mainmessage" rows="3" cols="50">', htmlspecialchars($mmessage), '</textarea>
3740
							</div>
3741
						</td>
3742
					</tr>
3743
					<tr valign="top">
3744
						<td width="2%">
3745
							<input type="checkbox" name="debug" id="debug" value="1" class="input_check">
3746
						</td>
3747
						<td width="100%">
3748
							<label for="debug">Output extra debugging information</label>
3749
						</td>
3750
					</tr>
3751
					<tr valign="top">
3752
						<td width="2%">
3753
							<input type="checkbox" name="empty_error" id="empty_error" value="1" class="input_check">
3754
						</td>
3755
						<td width="100%">
3756
							<label for="empty_error">Empty error log before upgrading</label>
3757
						</td>
3758
					</tr>';
3759
3760
	if (!empty($upcontext['karma_installed']['good']) || !empty($upcontext['karma_installed']['bad']))
3761
		echo '
3762
					<tr valign="top">
3763
						<td width="2%">
3764
							<input type="checkbox" name="delete_karma" id="delete_karma" value="1" class="input_check">
3765
						</td>
3766
						<td width="100%">
3767
							<label for="delete_karma">Delete all karma settings and info from the DB</label>
3768
						</td>
3769
					</tr>';
3770
3771
	echo '
3772
					<tr valign="top">
3773
						<td width="2%">
3774
							<input type="checkbox" name="stats" id="stats" value="1"', empty($modSettings['allow_sm_stats']) && empty($modSettings['enable_sm_stats']) ? '' : ' checked="checked"', ' class="input_check" />
3775
						</td>
3776
						<td width="100%">
3777
							<label for="stat">
3778
								Allow Simple Machines to Collect Basic Stats Monthly.<br>
3779
								<span class="smalltext">If enabled, this will allow Simple Machines to visit your site once a month to collect basic statistics. This will help us make decisions as to which configurations to optimise the software for. For more information please visit our <a href="https://www.simplemachines.org/about/stats.php" target="_blank">info page</a>.</span>
3780
							</label>
3781
						</td>
3782
					</tr>
3783
				</table>
3784
				<input type="hidden" name="upcont" value="1">';
3785
3786
	// We need a normal continue button here!
3787
	$upcontext['continue'] = 1;
3788
}
3789
3790
// Template for the database backup tool/
3791
function template_backup_database()
3792
{
3793
	global $upcontext, $support_js, $is_debug;
3794
3795
	echo '
3796
			<h3>Please wait while a backup is created. For large forums this may take some time!</h3>';
3797
3798
	echo '
3799
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
3800
			<input type="hidden" name="backup_done" id="backup_done" value="0">
3801
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
3802
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
3803
			<span id="debuginfo"></span>
3804
			</div>';
3805
3806
	// Dont any tables so far?
3807
	if (!empty($upcontext['previous_tables']))
3808
		foreach ($upcontext['previous_tables'] as $table)
3809
			echo '
3810
			<br>Completed Table: &quot;', $table, '&quot;.';
3811
3812
	echo '
3813
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
3814
			<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>';
3815
3816
	// Continue please!
3817
	$upcontext['continue'] = $support_js ? 2 : 1;
3818
3819
	// If javascript allows we want to do this using XML.
3820
	if ($support_js)
3821
	{
3822
		echo '
3823
		<script>
3824
			var lastTable = ', $upcontext['cur_table_num'], ';
3825
			function getNextTables()
3826
			{
3827
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
3828
			}
3829
3830
			// Got an update!
3831
			function onBackupUpdate(oXMLDoc)
3832
			{
3833
				var sCurrentTableName = "";
3834
				var iTableNum = 0;
3835
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
3836
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
3837
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
3838
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
3839
3840
				// Update the page.
3841
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
3842
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
3843
				lastTable = iTableNum;
3844
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
3845
3846
		// If debug flood the screen.
3847
		if ($is_debug)
3848
			echo '
3849
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
3850
3851
				if (document.getElementById(\'debug_section\').scrollHeight)
3852
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
3853
3854
		echo '
3855
				// Get the next update...
3856
				if (iTableNum == ', $upcontext['table_count'], ')
3857
				{
3858
					document.getElementById(\'commess\').style.display = "";
3859
					document.getElementById(\'current_tab_div\').style.display = "none";
3860
					document.getElementById(\'contbutt\').disabled = 0;
3861
					document.getElementById(\'backup_done\').value = 1;
3862
				}
3863
				else
3864
					getNextTables();
3865
			}
3866
			getNextTables();
3867
		//# sourceURL=dynamicScript-bkup.js
3868
		</script>';
3869
	}
3870
}
3871
3872
function template_backup_xml()
3873
{
3874
	global $upcontext;
3875
3876
	echo '
3877
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
3878
}
3879
3880
// Here is the actual "make the changes" template!
3881
function template_database_changes()
3882
{
3883
	global $upcontext, $support_js, $is_debug, $timeLimitThreshold;
3884
3885
	if (empty($is_debug) && !empty($upcontext['upgrade_status']['debug']))
3886
		$is_debug = true;
3887
3888
	echo '
3889
		<h3>Executing database changes</h3>
3890
		<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>';
3891
3892
	echo '
3893
		<form action="', $upcontext['form_url'], '&amp;filecount=', $upcontext['file_count'], '" name="upform" id="upform" method="post">
3894
		<input type="hidden" name="database_done" id="database_done" value="0">';
3895
3896
	// No javascript looks rubbish!
3897
	if (!$support_js)
3898
	{
3899
		foreach ($upcontext['actioned_items'] as $num => $item)
3900
		{
3901
			if ($num != 0)
3902
				echo ' Successful!';
3903
			echo '<br>' . $item;
3904
		}
3905 View Code Duplication
		if (!empty($upcontext['changes_complete']))
3906
		{
3907
			if ($is_debug)
3908
			{
3909
				$active = time() - $upcontext['started'];
3910
				$hours = floor($active / 3600);
3911
				$minutes = intval(($active / 60) % 60);
3912
				$seconds = intval($active % 60);
3913
3914
				$totalTime = '';
3915
				if ($hours > 0)
3916
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3917
				if ($minutes > 0)
3918
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3919
				if ($seconds > 0)
3920
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3921
			}
3922
3923
			if ($is_debug && !empty($totalTime))
3924
				echo ' Successful! Completed in ', $totalTime, '<br><br>';
3925
			else
3926
				echo ' Successful!<br><br>';
3927
3928
			echo '<span id="commess" style="font-weight: bold;">1 Database Updates Complete! Click Continue to Proceed.</span><br>';
3929
		}
3930
	}
3931
	else
3932
	{
3933
		// Tell them how many files we have in total.
3934
		if ($upcontext['file_count'] > 1)
3935
			echo '
3936
		<strong id="info1">Executing upgrade script <span id="file_done">', $upcontext['cur_file_num'], '</span> of ', $upcontext['file_count'], '.</strong>';
3937
3938
		echo '
3939
		<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>
3940
		<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>';
3941
3942 View Code Duplication
		if ($is_debug)
3943
		{
3944
			if ($upcontext['current_debug_item_num'] == $upcontext['debug_items'])
3945
			{
3946
				$active = time() - $upcontext['started'];
3947
				$hours = floor($active / 3600);
3948
				$minutes = intval(($active / 60) % 60);
3949
				$seconds = intval($active % 60);
3950
3951
				$totalTime = '';
3952
				if ($hours > 0)
3953
					$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
3954
				if ($minutes > 0)
3955
					$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
3956
				if ($seconds > 0)
3957
					$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
3958
			}
3959
3960
			echo '
3961
			<br><span id="upgradeCompleted">';
3962
3963
			if (!empty($totalTime))
3964
				echo 'Completed in ', $totalTime, '<br>';
3965
3966
			echo '</span>
3967
			<div id="debug_section" style="height: 59px; overflow: auto;">
3968
			<span id="debuginfo"></span>
3969
			</div>';
3970
		}
3971
	}
3972
3973
	// Place for the XML error message.
3974
	echo '
3975
		<div id="error_block" style="margin: 2ex; padding: 2ex; border: 2px dashed #cc3344; color: black; background-color: #ffe4e9; display: ', empty($upcontext['error_message']) ? 'none' : '', ';">
3976
			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
3977
			<strong style="text-decoration: underline;">Error!</strong><br>
3978
			<div style="padding-left: 6ex;" id="error_message">', isset($upcontext['error_message']) ? $upcontext['error_message'] : 'Unknown Error!', '</div>
3979
		</div>';
3980
3981
	// We want to continue at some point!
3982
	$upcontext['continue'] = $support_js ? 2 : 1;
3983
3984
	// If javascript allows we want to do this using XML.
3985
	if ($support_js)
3986
	{
3987
		echo '
3988
		<script>
3989
			var lastItem = ', $upcontext['current_debug_item_num'], ';
3990
			var sLastString = "', strtr($upcontext['current_debug_item_name'], array('"' => '&quot;')), '";
3991
			var iLastSubStepProgress = -1;
3992
			var curFile = ', $upcontext['cur_file_num'], ';
3993
			var totalItems = 0;
3994
			var prevFile = 0;
3995
			var retryCount = 0;
3996
			var testvar = 0;
3997
			var timeOutID = 0;
3998
			var getData = "";
3999
			var debugItems = ', $upcontext['debug_items'], ';';
4000
4001
		if ($is_debug)
4002
			echo '
4003
			var upgradeStartTime = ' . $upcontext['started'] . ';';
4004
4005
		echo '
4006
			function getNextItem()
4007
			{
4008
				// We want to track this...
4009
				if (timeOutID)
4010
					clearTimeout(timeOutID);
4011
				timeOutID = window.setTimeout("retTimeout()", ', $timeLimitThreshold, '000);
4012
4013
				getXMLDocument(\'', $upcontext['form_url'], '&xml&filecount=', $upcontext['file_count'], '&substep=\' + lastItem + getData, onItemUpdate);
4014
			}
4015
4016
			// Got an update!
4017
			function onItemUpdate(oXMLDoc)
4018
			{
4019
				var sItemName = "";
4020
				var sDebugName = "";
4021
				var iItemNum = 0;
4022
				var iSubStepProgress = -1;
4023
				var iDebugNum = 0;
4024
				var bIsComplete = 0;
4025
				getData = "";
4026
4027
				// We\'ve got something - so reset the timeout!
4028
				if (timeOutID)
4029
					clearTimeout(timeOutID);
4030
4031
				// Assume no error at this time...
4032
				document.getElementById("error_block").style.display = "none";
4033
4034
				// Are we getting some duff info?
4035
				if (!oXMLDoc.getElementsByTagName("item")[0])
4036
				{
4037
					// Too many errors?
4038
					if (retryCount > 15)
4039
					{
4040
						document.getElementById("error_block").style.display = "";
4041
						setInnerHTML(document.getElementById("error_message"), "Error retrieving information on step: " + (sDebugName == "" ? sLastString : sDebugName));';
4042
4043
	if ($is_debug)
4044
		echo '
4045
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4046
4047
	echo '
4048
					}
4049
					else
4050
					{
4051
						retryCount++;
4052
						getNextItem();
4053
					}
4054
					return false;
4055
				}
4056
4057
				// Never allow loops.
4058
				if (curFile == prevFile)
4059
				{
4060
					retryCount++;
4061
					if (retryCount > 10)
4062
					{
4063
						document.getElementById("error_block").style.display = "";
4064
						setInnerHTML(document.getElementById("error_message"), "Upgrade script appears to be going into a loop - step: " + sDebugName);';
4065
4066
	if ($is_debug)
4067
		echo '
4068
						setOuterHTML(document.getElementById(\'debuginfo\'), \'<span style="color: red;">failed<\' + \'/span><span id="debuginfo"><\' + \'/span>\');';
4069
4070
	echo '
4071
					}
4072
				}
4073
				retryCount = 0;
4074
4075
				for (var i = 0; i < oXMLDoc.getElementsByTagName("item")[0].childNodes.length; i++)
4076
					sItemName += oXMLDoc.getElementsByTagName("item")[0].childNodes[i].nodeValue;
4077
				for (var i = 0; i < oXMLDoc.getElementsByTagName("debug")[0].childNodes.length; i++)
4078
					sDebugName += oXMLDoc.getElementsByTagName("debug")[0].childNodes[i].nodeValue;
4079
				for (var i = 0; i < oXMLDoc.getElementsByTagName("get").length; i++)
4080
				{
4081
					getData += "&" + oXMLDoc.getElementsByTagName("get")[i].getAttribute("key") + "=";
4082
					for (var j = 0; j < oXMLDoc.getElementsByTagName("get")[i].childNodes.length; j++)
4083
					{
4084
						getData += oXMLDoc.getElementsByTagName("get")[i].childNodes[j].nodeValue;
4085
					}
4086
				}
4087
4088
				iItemNum = oXMLDoc.getElementsByTagName("item")[0].getAttribute("num");
4089
				iDebugNum = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("num"));
4090
				bIsComplete = parseInt(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("complete"));
4091
				iSubStepProgress = parseFloat(oXMLDoc.getElementsByTagName("debug")[0].getAttribute("percent"));
4092
				sLastString = sDebugName + " (Item: " + iDebugNum + ")";
4093
4094
				curFile = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("num"));
4095
				debugItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("debug_items"));
4096
				totalItems = parseInt(oXMLDoc.getElementsByTagName("file")[0].getAttribute("items"));
4097
4098
				// If we have an error we haven\'t completed!
4099
				if (oXMLDoc.getElementsByTagName("error")[0] && bIsComplete)
4100
					iDebugNum = lastItem;
4101
4102
				// Do we have the additional progress bar?
4103
				if (iSubStepProgress != -1)
4104
				{
4105
					document.getElementById("substep_bar_div").style.display = "";
4106
					document.getElementById("substep_bar_div2").style.display = "";
4107
					document.getElementById("substep_progress").style.width = iSubStepProgress + "%";
4108
					setInnerHTML(document.getElementById("substep_text"), iSubStepProgress + "%");
4109
					setInnerHTML(document.getElementById("substep_bar_div"), sDebugName.replace(/\./g, "") + ":");
4110
				}
4111
				else
4112
				{
4113
					document.getElementById("substep_bar_div").style.display = "none";
4114
					document.getElementById("substep_bar_div2").style.display = "none";
4115
				}
4116
4117
				// Move onto the next item?
4118
				if (bIsComplete)
4119
					lastItem = iDebugNum;
4120
				else
4121
					lastItem = iDebugNum - 1;
4122
4123
				// Are we finished?
4124
				if (bIsComplete && iDebugNum == -1 && curFile >= ', $upcontext['file_count'], ')
4125
				{';
4126
4127
		if ($is_debug)
4128
			echo '
4129
					document.getElementById(\'debug_section\').style.display = "none";
4130
4131
					var upgradeFinishedTime = parseInt(oXMLDoc.getElementsByTagName("curtime")[0].childNodes[0].nodeValue);
4132
					var diffTime = upgradeFinishedTime - upgradeStartTime;
4133
					var diffHours = Math.floor(diffTime / 3600);
4134
					var diffMinutes = parseInt((diffTime / 60) % 60);
4135
					var diffSeconds = parseInt(diffTime % 60);
4136
4137
					var totalTime = "";
4138
					if (diffHours > 0)
4139
						totalTime = totalTime + diffHours + " hour" + (diffHours > 1 ? "s" : "") + " ";
4140
					if (diffMinutes > 0)
4141
						totalTime = totalTime + diffMinutes + " minute" + (diffMinutes > 1 ? "s" : "") + " ";
4142
					if (diffSeconds > 0)
4143
						totalTime = totalTime + diffSeconds + " second" + (diffSeconds > 1 ? "s" : "");
4144
4145
					setInnerHTML(document.getElementById("upgradeCompleted"), "Completed in " + totalTime);';
4146
4147
		echo '
4148
4149
					document.getElementById(\'commess\').style.display = "";
4150
					document.getElementById(\'contbutt\').disabled = 0;
4151
					document.getElementById(\'database_done\').value = 1;';
4152
4153
		if ($upcontext['file_count'] > 1)
4154
			echo '
4155
					document.getElementById(\'info1\').style.display = "none";';
4156
4157
		echo '
4158
					document.getElementById(\'info2\').style.display = "none";
4159
					updateStepProgress(100, 100, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4160
					return true;
4161
				}
4162
				// Was it the last step in the file?
4163
				else if (bIsComplete && iDebugNum == -1)
4164
				{
4165
					lastItem = 0;
4166
					prevFile = curFile;';
4167
4168
		if ($is_debug)
4169
			echo '
4170
					setOuterHTML(document.getElementById(\'debuginfo\'), \'Moving to next script file...done<br><span id="debuginfo"><\' + \'/span>\');';
4171
4172
		echo '
4173
					getNextItem();
4174
					return true;
4175
				}';
4176
4177
		// If debug scroll the screen.
4178
		if ($is_debug)
4179
			echo '
4180
				if (iLastSubStepProgress == -1)
4181
				{
4182
					// Give it consistent dots.
4183
					dots = sDebugName.match(/\./g);
4184
					numDots = dots ? dots.length : 0;
4185
					for (var i = numDots; i < 3; i++)
4186
						sDebugName += ".";
4187
					setOuterHTML(document.getElementById(\'debuginfo\'), sDebugName + \'<span id="debuginfo"><\' + \'/span>\');
4188
				}
4189
				iLastSubStepProgress = iSubStepProgress;
4190
4191
				if (bIsComplete)
4192
					setOuterHTML(document.getElementById(\'debuginfo\'), \'done<br><span id="debuginfo"><\' + \'/span>\');
4193
				else
4194
					setOuterHTML(document.getElementById(\'debuginfo\'), \'...<span id="debuginfo"><\' + \'/span>\');
4195
4196
				if (document.getElementById(\'debug_section\').scrollHeight)
4197
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4198
4199
		echo '
4200
				// Update the page.
4201
				setInnerHTML(document.getElementById(\'item_num\'), iItemNum);
4202
				setInnerHTML(document.getElementById(\'cur_item_name\'), sItemName);';
4203
4204
		if ($upcontext['file_count'] > 1)
4205
		{
4206
			echo '
4207
				setInnerHTML(document.getElementById(\'file_done\'), curFile);
4208
				setInnerHTML(document.getElementById(\'item_count\'), totalItems);';
4209
		}
4210
4211
		echo '
4212
				// Is there an error?
4213
				if (oXMLDoc.getElementsByTagName("error")[0])
4214
				{
4215
					var sErrorMsg = "";
4216
					for (var i = 0; i < oXMLDoc.getElementsByTagName("error")[0].childNodes.length; i++)
4217
						sErrorMsg += oXMLDoc.getElementsByTagName("error")[0].childNodes[i].nodeValue;
4218
					document.getElementById("error_block").style.display = "";
4219
					setInnerHTML(document.getElementById("error_message"), sErrorMsg);
4220
					return false;
4221
				}
4222
4223
				// Get the progress bar right.
4224
				barTotal = debugItems * ', $upcontext['file_count'], ';
4225
				barDone = (debugItems * (curFile - 1)) + lastItem;
4226
4227
				updateStepProgress(barDone, barTotal, ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');
4228
4229
				// Finally - update the time here as it shows the server is responding!
4230
				curTime = new Date();
4231
				iElapsed = (curTime.getTime() / 1000 - ', $upcontext['started'], ');
4232
				mins = parseInt(iElapsed / 60);
4233
				secs = parseInt(iElapsed - mins * 60);
4234
				setInnerHTML(document.getElementById("mins_elapsed"), mins);
4235
				setInnerHTML(document.getElementById("secs_elapsed"), secs);
4236
4237
				getNextItem();
4238
				return true;
4239
			}
4240
4241
			// What if we timeout?!
4242
			function retTimeout(attemptAgain)
4243
			{
4244
				// Oh noes...
4245
				if (!attemptAgain)
4246
				{
4247
					document.getElementById("error_block").style.display = "";
4248
					setInnerHTML(document.getElementById("error_message"), "Server has not responded for ', $timeLimitThreshold, ' 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");
4249
				}
4250
				else
4251
				{
4252
					document.getElementById("error_block").style.display = "none";
4253
					getNextItem();
4254
				}
4255
			}';
4256
4257
		// Start things off assuming we've not errored.
4258
		if (empty($upcontext['error_message']))
4259
			echo '
4260
			getNextItem();';
4261
4262
		echo '
4263
		//# sourceURL=dynamicScript-dbch.js
4264
		</script>';
4265
	}
4266
	return;
4267
}
4268
4269
function template_database_xml()
4270
{
4271
	global $is_debug, $upcontext;
4272
4273
	echo '
4274
	<file num="', $upcontext['cur_file_num'], '" items="', $upcontext['total_items'], '" debug_items="', $upcontext['debug_items'], '">', $upcontext['cur_file_name'], '</file>
4275
	<item num="', $upcontext['current_item_num'], '">', $upcontext['current_item_name'], '</item>
4276
	<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>';
4277
4278
	if (!empty($upcontext['error_message']))
4279
		echo '
4280
	<error>', $upcontext['error_message'], '</error>';
4281
4282
	if (!empty($upcontext['error_string']))
4283
		echo '
4284
	<sql>', $upcontext['error_string'], '</sql>';
4285
4286
	if ($is_debug)
4287
		echo '
4288
	<curtime>', time(), '</curtime>';
4289
}
4290
4291
// Template for the UTF-8 conversion step. Basically a copy of the backup stuff with slight modifications....
4292
function template_convert_utf8()
4293
{
4294
	global $upcontext, $support_js, $is_debug;
4295
4296
	echo '
4297
			<h3>Please wait while your database is converted to UTF-8. For large forums this may take some time!</h3>';
4298
4299
	echo '
4300
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4301
			<input type="hidden" name="utf8_done" id="utf8_done" value="0">
4302
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4303
			<div id="debug_section" style="height: ', ($is_debug ? '97' : '12') , 'px; overflow: auto;">
4304
			<span id="debuginfo"></span>
4305
			</div>';
4306
4307
	// Done any tables so far?
4308
	if (!empty($upcontext['previous_tables']))
4309
		foreach ($upcontext['previous_tables'] as $table)
4310
			echo '
4311
			<br>Completed Table: &quot;', $table, '&quot;.';
4312
4313
	echo '
4314
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>';
4315
4316
	// If we dropped their index, let's let them know
4317
	if ($upcontext['dropping_index'])
4318
		echo '
4319
				<br><span id="indexmsg" style="font-weight: bold; font-style: italic; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Please note that your fulltext index was dropped to facilitate the conversion and will need to be recreated in the admin area after the upgrade is complete.</span>';
4320
4321
	// Completion notification
4322
	echo '
4323
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Conversion Complete! Click Continue to Proceed.</span>';
4324
4325
	// Continue please!
4326
	$upcontext['continue'] = $support_js ? 2 : 1;
4327
4328
	// If javascript allows we want to do this using XML.
4329
	if ($support_js)
4330
	{
4331
		echo '
4332
		<script>
4333
			var lastTable = ', $upcontext['cur_table_num'], ';
4334
			function getNextTables()
4335
			{
4336
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onConversionUpdate);
4337
			}
4338
4339
			// Got an update!
4340
			function onConversionUpdate(oXMLDoc)
4341
			{
4342
				var sCurrentTableName = "";
4343
				var iTableNum = 0;
4344
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4345
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4346
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4347
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4348
4349
				// Update the page.
4350
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4351
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4352
				lastTable = iTableNum;
4353
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4354
4355
		// If debug flood the screen.
4356
		if ($is_debug)
4357
			echo '
4358
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4359
4360
				if (document.getElementById(\'debug_section\').scrollHeight)
4361
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4362
4363
		echo '
4364
				// Get the next update...
4365
				if (iTableNum == ', $upcontext['table_count'], ')
4366
				{
4367
					document.getElementById(\'commess\').style.display = "";
4368
					if (document.getElementById(\'indexmsg\') != null) {
4369
						document.getElementById(\'indexmsg\').style.display = "";
4370
					}
4371
					document.getElementById(\'current_tab_div\').style.display = "none";
4372
					document.getElementById(\'contbutt\').disabled = 0;
4373
					document.getElementById(\'utf8_done\').value = 1;
4374
				}
4375
				else
4376
					getNextTables();
4377
			}
4378
			getNextTables();
4379
		//# sourceURL=dynamicScript-conv.js
4380
		</script>';
4381
	}
4382
}
4383
4384
function template_convert_xml()
4385
{
4386
	global $upcontext;
4387
4388
	echo '
4389
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4390
}
4391
4392
// Template for the database backup tool/
4393
function template_serialize_json()
4394
{
4395
	global $upcontext, $support_js, $is_debug;
4396
4397
	echo '
4398
			<h3>Converting data from serialize to JSON...</h3>';
4399
4400
	echo '
4401
			<form action="', $upcontext['form_url'], '" name="upform" id="upform" method="post">
4402
			<input type="hidden" name="json_done" id="json_done" value="0">
4403
			<strong>Completed <span id="tab_done">', $upcontext['cur_table_num'], '</span> out of ', $upcontext['table_count'], ' tables.</strong>
4404
			<div id="debug_section" style="height: ', ($is_debug ? '115' : '12') , 'px; overflow: auto;">
4405
			<span id="debuginfo"></span>
4406
			</div>';
4407
4408
	// Dont any tables so far?
4409
	if (!empty($upcontext['previous_tables']))
4410
		foreach ($upcontext['previous_tables'] as $table)
4411
			echo '
4412
			<br>Completed Table: &quot;', $table, '&quot;.';
4413
4414
	echo '
4415
			<h3 id="current_tab_div">Current Table: &quot;<span id="current_table">', $upcontext['cur_table_name'], '</span>&quot;</h3>
4416
			<br><span id="commess" style="font-weight: bold; display: ', $upcontext['cur_table_num'] == $upcontext['table_count'] ? 'inline' : 'none', ';">Convert to JSON Complete! Click Continue to Proceed.</span>';
4417
4418
	// Try to make sure substep was reset.
4419
	if ($upcontext['cur_table_num'] == $upcontext['table_count'])
4420
		echo '
4421
			<input type="hidden" name="substep" id="substep" value="0">';
4422
4423
	// Continue please!
4424
	$upcontext['continue'] = $support_js ? 2 : 1;
4425
4426
	// If javascript allows we want to do this using XML.
4427
	if ($support_js)
4428
	{
4429
		echo '
4430
		<script>
4431
			var lastTable = ', $upcontext['cur_table_num'], ';
4432
			function getNextTables()
4433
			{
4434
				getXMLDocument(\'', $upcontext['form_url'], '&xml&substep=\' + lastTable, onBackupUpdate);
4435
			}
4436
4437
			// Got an update!
4438
			function onBackupUpdate(oXMLDoc)
4439
			{
4440
				var sCurrentTableName = "";
4441
				var iTableNum = 0;
4442
				var sCompletedTableName = getInnerHTML(document.getElementById(\'current_table\'));
4443
				for (var i = 0; i < oXMLDoc.getElementsByTagName("table")[0].childNodes.length; i++)
4444
					sCurrentTableName += oXMLDoc.getElementsByTagName("table")[0].childNodes[i].nodeValue;
4445
				iTableNum = oXMLDoc.getElementsByTagName("table")[0].getAttribute("num");
4446
4447
				// Update the page.
4448
				setInnerHTML(document.getElementById(\'tab_done\'), iTableNum);
4449
				setInnerHTML(document.getElementById(\'current_table\'), sCurrentTableName);
4450
				lastTable = iTableNum;
4451
				updateStepProgress(iTableNum, ', $upcontext['table_count'], ', ', $upcontext['step_weight'] * ((100 - $upcontext['step_progress']) / 100), ');';
4452
4453
		// If debug flood the screen.
4454
		if ($is_debug)
4455
			echo '
4456
				setOuterHTML(document.getElementById(\'debuginfo\'), \'<br>Completed Table: &quot;\' + sCompletedTableName + \'&quot;.<span id="debuginfo"><\' + \'/span>\');
4457
4458
				if (document.getElementById(\'debug_section\').scrollHeight)
4459
					document.getElementById(\'debug_section\').scrollTop = document.getElementById(\'debug_section\').scrollHeight';
4460
4461
		echo '
4462
				// Get the next update...
4463
				if (iTableNum == ', $upcontext['table_count'], ')
4464
				{
4465
					document.getElementById(\'commess\').style.display = "";
4466
					document.getElementById(\'current_tab_div\').style.display = "none";
4467
					document.getElementById(\'contbutt\').disabled = 0;
4468
					document.getElementById(\'json_done\').value = 1;
4469
				}
4470
				else
4471
					getNextTables();
4472
			}
4473
			getNextTables();
4474
		//# sourceURL=dynamicScript-json.js
4475
		</script>';
4476
	}
4477
}
4478
4479
function template_serialize_json_xml()
4480
{
4481
	global $upcontext;
4482
4483
	echo '
4484
	<table num="', $upcontext['cur_table_num'], '">', $upcontext['cur_table_name'], '</table>';
4485
}
4486
4487
function template_upgrade_complete()
4488
{
4489
	global $upcontext, $upgradeurl, $settings, $boardurl, $is_debug;
4490
4491
	echo '
4492
	<h3>That wasn\'t so hard, was it?  Now you are ready to use <a href="', $boardurl, '/index.php">your installation of SMF</a>.  Hope you like it!</h3>
4493
	<form action="', $boardurl, '/index.php">';
4494
4495
	if (!empty($upcontext['can_delete_script']))
4496
		echo '
4497
			<label for="delete_self"><input type="checkbox" id="delete_self" onclick="doTheDelete(this);" class="input_check"> Delete upgrade.php and its data files now</label> <em>(doesn\'t work on all servers).</em>
4498
			<script>
4499
				function doTheDelete(theCheck)
4500
				{
4501
					var theImage = document.getElementById ? document.getElementById("delete_upgrader") : document.all.delete_upgrader;
4502
4503
					theImage.src = "', $upgradeurl, '?delete=1&ts_" + (new Date().getTime());
4504
					theCheck.disabled = true;
4505
				}
4506
			</script>
4507
			<img src="', $settings['default_theme_url'], '/images/blank.png" alt="" id="delete_upgrader"><br>';
4508
4509
	$active = time() - $upcontext['started'];
4510
	$hours = floor($active / 3600);
4511
	$minutes = intval(($active / 60) % 60);
4512
	$seconds = intval($active % 60);
4513
4514
	if ($is_debug)
4515
	{
4516
		$totalTime = '';
4517
		if ($hours > 0)
4518
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
4519
		if ($minutes > 0)
4520
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
4521
		if ($seconds > 0)
4522
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
4523
	}
4524
4525
	if ($is_debug && !empty($totalTime))
4526
		echo '<br> Upgrade completed in ', $totalTime, '<br><br>';
4527
4528
	echo '<br>
4529
			If you had any problems with this upgrade, or have any problems using SMF, please don\'t hesitate to <a href="https://www.simplemachines.org/community/index.php">look to us for assistance</a>.<br>
4530
			<br>
4531
			Best of luck,<br>
4532
			Simple Machines';
4533
}
4534
4535
/**
4536
 * Convert MySQL (var)char ip col to binary
4537
 *
4538
 * @param string $targetTable The table to perform the operation on
4539
 * @param string $oldCol The old column to gather data from
4540
 * @param string $newCol The new column to put data in
4541
 * @param int $limit The amount of entries to handle at once.
4542
 * @param int $setSize The amount of entries after which to update the database.
4543
 *
4544
 * newCol needs to be a varbinary(16) null able field
4545
 * @return bool
4546
 */
4547
function MySQLConvertOldIp($targetTable, $oldCol, $newCol, $limit = 50000, $setSize = 100)
4548
{
4549
	global $smcFunc, $step_progress;
4550
4551
	$current_substep = $_GET['substep'];
4552
4553
	if (empty($_GET['a']))
4554
		$_GET['a'] = 0;
4555
	$step_progress['name'] = 'Converting ips';
4556
	$step_progress['current'] = $_GET['a'];
4557
4558
	// Skip this if we don't have the column
4559
	$request = $smcFunc['db_query']('', '
4560
		SHOW FIELDS
4561
		FROM {db_prefix}{raw:table}
4562
		WHERE Field = {string:name}',
4563
		array(
4564
			'table' => $targetTable,
4565
			'name' => $oldCol,
4566
	));
4567
	if ($smcFunc['db_num_rows']($request) !== 1)
4568
	{
4569
		$smcFunc['db_free_result']($request);
4570
		return;
4571
	}
4572
	$smcFunc['db_free_result']($request);
4573
4574
	//mysql default max length is 1mb https://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html
4575
	$arIp = array();
4576
4577
	$is_done = false;
4578
	while (!$is_done)
4579
	{
4580
		// Keep looping at the current step.
4581
		nextSubStep($current_substep);
4582
4583
		$arIp = array();
4584
4585
		$request = $smcFunc['db_query']('', '
4586
			SELECT DISTINCT {raw:old_col}
4587
			FROM {db_prefix}{raw:table_name}
4588
			WHERE {raw:new_col} IS NULL
4589
			LIMIT {int:limit}',
4590
			array(
4591
				'old_col' => $oldCol,
4592
				'new_col' => $newCol,
4593
				'table_name' => $targetTable,
4594
				'empty' => '',
4595
				'limit' => $limit,
4596
		));
4597
		while ($row = $smcFunc['db_fetch_assoc']($request))
4598
			$arIp[] = $row[$oldCol];
4599
		$smcFunc['db_free_result']($request);
4600
4601
		// Special case, null ip could keep us in a loop.
4602
		if (is_null($arIp[0]))
4603
			unset($arIp[0]);
4604
4605
		if (empty($arIp))
4606
			$is_done = true;
4607
4608
		$updates = array();
4609
		$cases = array();
4610
		$count = count($arIp);
4611
		for ($i = 0; $i < $count; $i++)
4612
		{
4613
			$arIp[$i] = trim($arIp[$i]);
4614
4615
			if (empty($arIp[$i]))
4616
				continue;
4617
4618
			$updates['ip' . $i] = $arIp[$i];
4619
			$cases[$arIp[$i]] = 'WHEN ' . $oldCol . ' = {string:ip' . $i . '} THEN {inet:ip' . $i . '}';
4620
4621
			if ($setSize > 0 && $i % $setSize === 0)
4622
			{
4623
				if (count($updates) == 1)
4624
					continue;
4625
4626
				$updates['whereSet'] = array_values($updates);
4627
				$smcFunc['db_query']('', '
4628
					UPDATE {db_prefix}' . $targetTable . '
4629
					SET ' . $newCol . ' = CASE ' .
4630
					implode('
4631
						', $cases) . '
4632
						ELSE NULL
4633
					END
4634
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4635
					$updates
4636
				);
4637
4638
				$updates = array();
4639
				$cases = array();
4640
			}
4641
		}
4642
4643
		// Incase some extras made it through.
4644
		if (!empty($updates))
4645
		{
4646
			if (count($updates) == 1)
4647
			{
4648
				foreach ($updates as $key => $ip)
4649
				{
4650
					$smcFunc['db_query']('', '
4651
						UPDATE {db_prefix}' . $targetTable . '
4652
						SET ' . $newCol . ' = {inet:ip}
4653
						WHERE ' . $oldCol . ' = {string:ip}',
4654
						array(
4655
							'ip' => $ip
4656
					));
4657
				}
4658
			}
4659
			else
4660
			{
4661
				$updates['whereSet'] = array_values($updates);
4662
				$smcFunc['db_query']('', '
4663
					UPDATE {db_prefix}' . $targetTable . '
4664
					SET ' . $newCol . ' = CASE ' .
4665
					implode('
4666
						', $cases) . '
4667
						ELSE NULL
4668
					END
4669
					WHERE ' . $oldCol . ' IN ({array_string:whereSet})',
4670
					$updates
4671
				);
4672
			}
4673
		}
4674
		else
4675
			$is_done = true;
4676
4677
		$_GET['a'] += $limit;
4678
		$step_progress['current'] = $_GET['a'];
4679
	}
4680
4681
	unset($_GET['a']);
4682
}
4683
4684
/**
4685
 * Get the column info. This is basically the same as smf_db_list_columns but we get 1 column, force detail and other checks.
4686
 *
4687
 * @param string $targetTable The table to perform the operation on
4688
 * @param string $column The column we are looking for.
4689
 *
4690
 * @return array Info on the table.
4691
 */
4692
function upgradeGetColumnInfo($targetTable, $column)
4693
{
4694
 	global $smcFunc;
4695
4696
 	// This should already be here, but be safe.
4697
 	db_extend('packages');
4698
4699
 	$columns = $smcFunc['db_list_columns']($targetTable, true);
4700
4701
	if (isset($columns[$column]))
4702
		return $columns[$column];
4703
	else
4704
		return null;
4705
}
4706
4707
?>