Completed
Pull Request — release-2.1 (#4470)
by Fran
21:16
created

other/upgrade.php (1 issue)

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 4
12
 */
13
14
// Version information...
15
define('SMF_VERSION', '2.1 Beta 4');
16
define('SMF_LANG_VERSION', '2.1 Beta 4');
17
18
/**
19
 * The minimum required PHP version.
20
 * @var string
21
 */
22
$GLOBALS['required_php_version'] = '5.4.0';
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.2',
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 = 3;
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
global $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

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