Completed
Push — release-2.1 ( 0c190a...4abb70 )
by Jeremy
26:57 queued 19:17
created

other/upgrade.php (2 issues)

Upgrade to new PHP Analysis Engine

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

Code
1
<?php
2
3
/**
4
 * Simple Machines Forum (SMF)
5
 *
6
 * @package SMF
7
 * @author Simple Machines http://www.simplemachines.org
8
 * @copyright 2018 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.22',
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.22',
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.4',
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;
84
85
// Initialize everything and load the language files.
86
initialize_inputs();
87
load_lang_file();
88
89
90
// All the steps in detail.
91
// Number,Name,Function,Progress Weight.
92
$upcontext['steps'] = array(
93
	0 => array(1, $txt['upgrade_step_login'], 'WelcomeLogin', 2),
94
	1 => array(2, $txt['upgrade_step_options'], 'UpgradeOptions', 2),
95
	2 => array(3, $txt['upgrade_step_backup'], 'BackupDatabase', 10),
96
	3 => array(4, $txt['upgrade_step_database'], 'DatabaseChanges', 50),
97
	4 => array(5, $txt['upgrade_step_convertutf'], 'ConvertUtf8', 20),
98
	5 => array(6, $txt['upgrade_step_convertjson'], 'serialize_to_json', 10),
99
	6 => array(7, $txt['upgrade_step_delete'], 'DeleteUpgrade', 1),
100
);
101
// Just to remember which one has files in it.
102
$upcontext['database_step'] = 3;
103
@set_time_limit(600);
104
if (!ini_get('safe_mode'))
105
{
106
	ini_set('mysql.connect_timeout', -1);
107
	ini_set('default_socket_timeout', 900);
108
}
109
// Clean the upgrade path if this is from the client.
110
if (!empty($_SERVER['argv']) && php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
111
	for ($i = 1; $i < $_SERVER['argc']; $i++)
112
	{
113
		if (preg_match('~^--path=(.+)$~', $_SERVER['argv'][$i], $match) != 0)
114
			$upgrade_path = substr($match[1], -1) == '/' ? substr($match[1], 0, -1) : $match[1];
115
	}
116
117
// Are we from the client?
118
if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']))
119
{
120
	$command_line = true;
121
	$disable_security = true;
122
}
123
else
124
	$command_line = false;
125
126
// Load this now just because we can.
127
require_once($upgrade_path . '/Settings.php');
128
129
// We don't use "-utf8" anymore...  Tweak the entry that may have been loaded by Settings.php
130
if (isset($language))
131
	$language = str_ireplace('-utf8', '', $language);
132
133
// Are we logged in?
134
if (isset($upgradeData))
135
{
136
	$upcontext['user'] = json_decode(base64_decode($upgradeData), true);
137
138
	// Check for sensible values.
139 View Code Duplication
	if (empty($upcontext['user']['started']) || $upcontext['user']['started'] < time() - 86400)
140
		$upcontext['user']['started'] = time();
141 View Code Duplication
	if (empty($upcontext['user']['updated']) || $upcontext['user']['updated'] < time() - 86400)
142
		$upcontext['user']['updated'] = 0;
143
144
	$upcontext['started'] = $upcontext['user']['started'];
145
	$upcontext['updated'] = $upcontext['user']['updated'];
146
147
	$is_debug = !empty($upcontext['user']['debug']) ? true : false;
148
}
149
150
// Nothing sensible?
151
if (empty($upcontext['updated']))
152
{
153
	$upcontext['started'] = time();
154
	$upcontext['updated'] = 0;
155
	$upcontext['user'] = array(
156
		'id' => 0,
157
		'name' => 'Guest',
158
		'pass' => 0,
159
		'started' => $upcontext['started'],
160
		'updated' => $upcontext['updated'],
161
	);
162
}
163
164
// Load up some essential data...
165
loadEssentialData();
166
167
// Are we going to be mimic'ing SSI at this point?
168
if (isset($_GET['ssi']))
169
{
170
	require_once($sourcedir . '/Errors.php');
171
	require_once($sourcedir . '/Logging.php');
172
	require_once($sourcedir . '/Load.php');
173
	require_once($sourcedir . '/Security.php');
174
	require_once($sourcedir . '/Subs-Package.php');
175
176
	// SMF isn't started up properly, but loadUserSettings calls our cookies.
177 View Code Duplication
	if (!isset($smcFunc['json_encode']))
178
	{
179
		$smcFunc['json_encode'] = 'json_encode';
180
		$smcFunc['json_decode'] = 'smf_json_decode';
181
	}
182
183
	loadUserSettings();
184
	loadPermissions();
185
}
186
187
// Include our helper functions.
188
require_once($sourcedir . '/Subs.php');
189
require_once($sourcedir . '/LogInOut.php');
190
191
// This only exists if we're on SMF ;)
192
if (isset($modSettings['smfVersion']))
193
{
194
	$request = $smcFunc['db_query']('', '
195
		SELECT variable, value
196
		FROM {db_prefix}themes
197
		WHERE id_theme = {int:id_theme}
198
			AND variable IN ({string:theme_url}, {string:theme_dir}, {string:images_url})',
199
		array(
200
			'id_theme' => 1,
201
			'theme_url' => 'theme_url',
202
			'theme_dir' => 'theme_dir',
203
			'images_url' => 'images_url',
204
			'db_error_skip' => true,
205
		)
206
	);
207 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
208
		$modSettings[$row['variable']] = $row['value'];
209
	$smcFunc['db_free_result']($request);
210
}
211
212
if (!isset($modSettings['theme_url']))
213
{
214
	$modSettings['theme_dir'] = $boarddir . '/Themes/default';
215
	$modSettings['theme_url'] = 'Themes/default';
216
	$modSettings['images_url'] = 'Themes/default/images';
217
}
218
if (!isset($settings['default_theme_url']))
219
	$settings['default_theme_url'] = $modSettings['theme_url'];
220
if (!isset($settings['default_theme_dir']))
221
	$settings['default_theme_dir'] = $modSettings['theme_dir'];
222
223
// This is needed in case someone invokes the upgrader using https when upgrading an http forum
224 View Code Duplication
if (httpsOn())
225
	$settings['default_theme_url'] = strtr($settings['default_theme_url'], array('http://' => 'https://'));
226
227
$upcontext['is_large_forum'] = (empty($modSettings['smfVersion']) || $modSettings['smfVersion'] <= '1.1 RC1') && !empty($modSettings['totalMessages']) && $modSettings['totalMessages'] > 75000;
228
// Default title...
229
$upcontext['page_title'] = $txt['updating_smf_installation'];
230
231
// Have we got tracking data - if so use it (It will be clean!)
232
if (isset($_GET['data']))
233
{
234
	global $is_debug;
235
236
	$upcontext['upgrade_status'] = json_decode(base64_decode($_GET['data']), true);
237
	$upcontext['current_step'] = $upcontext['upgrade_status']['curstep'];
238
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
239
	$upcontext['rid'] = $upcontext['upgrade_status']['rid'];
240
	$support_js = $upcontext['upgrade_status']['js'];
241
242
	// Only set this if the upgrader status says so.
243
	if (empty($is_debug))
244
		$is_debug = $upcontext['upgrade_status']['debug'];
245
246
	// Load the language.
247
	if (file_exists($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php'))
248
		require_once($modSettings['theme_dir'] . '/languages/Install.' . $upcontext['language'] . '.php');
249
}
250
// Set the defaults.
251
else
252
{
253
	$upcontext['current_step'] = 0;
254
	$upcontext['rid'] = mt_rand(0, 5000);
255
	$upcontext['upgrade_status'] = array(
256
		'curstep' => 0,
257
		'lang' => isset($_GET['lang']) ? $_GET['lang'] : basename($language, '.lng'),
258
		'rid' => $upcontext['rid'],
259
		'pass' => 0,
260
		'debug' => 0,
261
		'js' => 0,
262
	);
263
	$upcontext['language'] = $upcontext['upgrade_status']['lang'];
264
}
265
266
// If this isn't the first stage see whether they are logging in and resuming.
267
if ($upcontext['current_step'] != 0 || !empty($upcontext['user']['step']))
268
	checkLogin();
269
270
if ($command_line)
271
	cmdStep0();
272
273
// Don't error if we're using xml.
274
if (isset($_GET['xml']))
275
	$upcontext['return_error'] = true;
276
277
// Loop through all the steps doing each one as required.
278
$upcontext['overall_percent'] = 0;
279
foreach ($upcontext['steps'] as $num => $step)
280
{
281
	if ($num >= $upcontext['current_step'])
282
	{
283
		// The current weight of this step in terms of overall progress.
284
		$upcontext['step_weight'] = $step[3];
285
		// Make sure we reset the skip button.
286
		$upcontext['skip'] = false;
287
288
		// We cannot proceed if we're not logged in.
289
		if ($num != 0 && !$disable_security && $upcontext['user']['pass'] != $upcontext['upgrade_status']['pass'])
290
		{
291
			$upcontext['steps'][0][2]();
292
			break;
293
		}
294
295
		// Call the step and if it returns false that means pause!
296
		if (function_exists($step[2]) && $step[2]() === false)
297
			break;
298
		elseif (function_exists($step[2])) {
299
			//Start each new step with this unset, so the 'normal' template is called first
300
			unset($_GET['xml']);
301
			//Clear out warnings at the start of each step
302
			unset($upcontext['custom_warning']);
303
			$_GET['substep'] = 0;
304
			$upcontext['current_step']++;
305
		}
306
	}
307
	$upcontext['overall_percent'] += $step[3];
308
}
309
310
upgradeExit();
311
312
// Exit the upgrade script.
313
function upgradeExit($fallThrough = false)
314
{
315
	global $upcontext, $upgradeurl, $sourcedir, $command_line, $is_debug, $txt;
316
317
	// Save where we are...
318
	if (!empty($upcontext['current_step']) && !empty($upcontext['user']['id']))
319
	{
320
		$upcontext['user']['step'] = $upcontext['current_step'];
321
		$upcontext['user']['substep'] = $_GET['substep'];
322
		$upcontext['user']['updated'] = time();
323
		$upcontext['debug'] = $is_debug;
324
		$upgradeData = base64_encode(json_encode($upcontext['user']));
325
		require_once($sourcedir . '/Subs-Admin.php');
326
		updateSettingsFile(array('upgradeData' => '"' . $upgradeData . '"'));
327
		updateDbLastError(0);
328
	}
329
330
	// Handle the progress of the step, if any.
331
	if (!empty($upcontext['step_progress']) && isset($upcontext['steps'][$upcontext['current_step']]))
332
	{
333
		$upcontext['step_progress'] = round($upcontext['step_progress'], 1);
334
		$upcontext['overall_percent'] += $upcontext['step_progress'] * ($upcontext['steps'][$upcontext['current_step']][3] / 100);
335
	}
336
	$upcontext['overall_percent'] = (int) $upcontext['overall_percent'];
337
338
	// We usually dump our templates out.
339
	if (!$fallThrough)
340
	{
341
		// This should not happen my dear... HELP ME DEVELOPERS!!
342
		if (!empty($command_line))
343
		{
344
			if (function_exists('debug_print_backtrace'))
345
				debug_print_backtrace();
346
347
			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.';
348
			flush();
349
			die();
350
		}
351
352
		if (!isset($_GET['xml']))
353
			template_upgrade_above();
354
		else
355
		{
356
			header('content-type: text/xml; charset=UTF-8');
357
			// Sadly we need to retain the $_GET data thanks to the old upgrade scripts.
358
			$upcontext['get_data'] = array();
359
			foreach ($_GET as $k => $v)
360
			{
361
				if (substr($k, 0, 3) != 'amp' && !in_array($k, array('xml', 'substep', 'lang', 'data', 'step', 'filecount')))
362
				{
363
					$upcontext['get_data'][$k] = $v;
364
				}
365
			}
366
			template_xml_above();
367
		}
368
369
		// Call the template.
370
		if (isset($upcontext['sub_template']))
371
		{
372
			$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
373
			$upcontext['form_url'] = $upgradeurl . '?step=' . $upcontext['current_step'] . '&amp;substep=' . $_GET['substep'] . '&amp;data=' . base64_encode(json_encode($upcontext['upgrade_status']));
374
375
			// Custom stuff to pass back?
376
			if (!empty($upcontext['query_string']))
377
				$upcontext['form_url'] .= $upcontext['query_string'];
378
379
			// Call the appropriate subtemplate
380
			if (is_callable('template_' . $upcontext['sub_template']))
381
				call_user_func('template_' . $upcontext['sub_template']);
382
			else
383
				die('Upgrade aborted!  Invalid template: template_' . $upcontext['sub_template']);
384
		}
385
386
		// Was there an error?
387
		if (!empty($upcontext['forced_error_message']))
388
			echo $upcontext['forced_error_message'];
389
390
		// Show the footer.
391
		if (!isset($_GET['xml']))
392
			template_upgrade_below();
393
		else
394
			template_xml_below();
395
	}
396
397
398
	if (!empty($command_line) && $is_debug)
399
	{
400
		$active = time() - $upcontext['started'];
401
		$hours = floor($active / 3600);
402
		$minutes = intval(($active / 60) % 60);
403
		$seconds = intval($active % 60);
404
405
		$totalTime = '';
406 View Code Duplication
		if ($hours > 0)
407
			$totalTime .= $hours . ' hour' . ($hours > 1 ? 's' : '') . ' ';
408 View Code Duplication
		if ($minutes > 0)
409
			$totalTime .= $minutes . ' minute' . ($minutes > 1 ? 's' : '') . ' ';
410 View Code Duplication
		if ($seconds > 0)
411
			$totalTime .= $seconds . ' second' . ($seconds > 1 ? 's' : '') . ' ';
412
413
		if (!empty($totalTime))
414
			echo "\n" . '', $txt['upgrade_completed_time'], ' ' . $totalTime . "\n";
415
	}
416
417
	// Bang - gone!
418
	die();
419
}
420
421
// Load the list of language files, and the current language file.
422
function load_lang_file()
423
{
424
	global $txt, $incontext, $user_info;
425
426
	$incontext['detected_languages'] = array();
427
428
	// Make sure the languages directory actually exists.
429 View Code Duplication
	if (file_exists(dirname(__FILE__) . '/Themes/default/languages'))
430
	{
431
		// Find all the "Install" language files in the directory.
432
		$dir = dir(dirname(__FILE__) . '/Themes/default/languages');
433
		while ($entry = $dir->read())
434
		{
435
			if (substr($entry, 0, 8) == 'Install.' && substr($entry, -4) == '.php')
436
				$incontext['detected_languages'][$entry] = ucfirst(substr($entry, 8, strlen($entry) - 12));
437
		}
438
		$dir->close();
439
	}
440
441
	// Didn't find any, show an error message!
442 View Code Duplication
	if (empty($incontext['detected_languages']))
443
	{
444
		// Let's not cache this message, eh?
445
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
446
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
447
		header('Cache-Control: no-cache');
448
449
		echo '<!DOCTYPE html>
450
			<html>
451
				<head>
452
					<title>SMF Upgrader: Error!</title>
453
						<style>
454
							body {
455
								font-family: sans-serif;
456
								max-width: 700px; }
457
458
								h1 {
459
									font-size: 14pt; }
460
461
								.directory {
462
									margin: 0.3em;
463
									font-family: monospace;
464
									font-weight: bold; }
465
						</style>
466
				</head>
467
				<body>
468
					<h1>A critical error has occurred.</h1>
469
						<p>This upgrader was unable to find the upgrader\'s language file or files.  They should be found under:</p>
470
						<div class="directory">', dirname($_SERVER['PHP_SELF']) != '/' ? dirname($_SERVER['PHP_SELF']) : '', '/Themes/default/languages</div>
471
						<p>In some cases, FTP clients do not properly upload files with this many folders. Please double check to make sure you <strong>have uploaded all the files in the distribution</strong>.</p>
472
						<p>If that doesn\'t help, please make sure this install.php file is in the same place as the Themes folder.</p>
473
						<p>If you continue to get this error message, feel free to <a href="https://support.simplemachines.org/">look to us for support</a>.</p>
474
				</body>
475
			</html>';
476
		die;
477
	}
478
479
	// Override the language file?
480 View Code Duplication
	if (isset($_GET['lang_file']))
481
		$_SESSION['installer_temp_lang'] = $_GET['lang_file'];
482
	elseif (isset($GLOBALS['HTTP_GET_VARS']['lang_file']))
483
		$_SESSION['installer_temp_lang'] = $GLOBALS['HTTP_GET_VARS']['lang_file'];
484
485
	// Make sure it exists, if it doesn't reset it.
486
	if (!isset($_SESSION['installer_temp_lang']) || preg_match('~[^\\w_\\-.]~', $_SESSION['installer_temp_lang']) === 1 || !file_exists(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']))
487
	{
488
		// Use the first one...
489
		list ($_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
490
491
		// If we have english and some other language, use the other language.  We Americans hate english :P.
492 View Code Duplication
		if ($_SESSION['installer_temp_lang'] == 'Install.english.php' && count($incontext['detected_languages']) > 1)
493
			list (, $_SESSION['installer_temp_lang']) = array_keys($incontext['detected_languages']);
494
495
		// For backup we load the english at first -> second language overwrite the english one
496
		if (count($incontext['detected_languages']) > 1)
497
			require_once(dirname(__FILE__) . '/Themes/default/languages/Install.english.php');
498
	}
499
500
	// And now include the actual language file itself.
501
	require_once(dirname(__FILE__) . '/Themes/default/languages/' . $_SESSION['installer_temp_lang']);
502
503
	// Which language did we load? Assume that he likes his language.
504
	preg_match('~^Install\.(.+[^-utf8])\.php$~', $_SESSION['installer_temp_lang'], $matches);
505
	if (empty($matches[1]))
506
		$matches = [
507
			0 => 'nothing',
508
			1 => 'english',
509
		];
510
	$user_info['language'] = $matches[1];
511
}
512
513
// Used to direct the user to another location.
514
function redirectLocation($location, $addForm = true)
515
{
516
	global $upgradeurl, $upcontext, $command_line;
517
518
	// Command line users can't be redirected.
519
	if ($command_line)
520
		upgradeExit(true);
521
522
	// Are we providing the core info?
523
	if ($addForm)
524
	{
525
		$upcontext['upgrade_status']['curstep'] = $upcontext['current_step'];
526
		$location = $upgradeurl . '?step=' . $upcontext['current_step'] . '&substep=' . $_GET['substep'] . '&data=' . base64_encode(json_encode($upcontext['upgrade_status'])) . $location;
527
	}
528
529
	while (@ob_end_clean());
530
	header('location: ' . strtr($location, array('&amp;' => '&')));
531
532
	// Exit - saving status as we go.
533
	upgradeExit(true);
534
}
535
536
// Load all essential data and connect to the DB as this is pre SSI.php
537
function loadEssentialData()
538
{
539
	global $db_server, $db_user, $db_passwd, $db_name, $db_connection, $db_prefix, $db_character_set, $db_type, $db_port;
540
	global $db_mb4, $modSettings, $sourcedir, $smcFunc;
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...
541
542
	error_reporting(E_ALL);
543
	define('SMF', 1);
544
545
	// Start the session.
546
	if (@ini_get('session.save_handler') == 'user')
547
		@ini_set('session.save_handler', 'files');
548
	@session_start();
549
550
	if (empty($smcFunc))
551
		$smcFunc = array();
552
553
	// We need this for authentication and some upgrade code
554
	require_once($sourcedir . '/Subs-Auth.php');
555
	require_once($sourcedir . '/Class-Package.php');
556
557
	$smcFunc['strtolower'] = 'smf_strtolower';
558
559
	// Initialize everything...
560
	initialize_inputs();
561
562
	// Get the database going!
563
	if (empty($db_type) || $db_type == 'mysqli')
564
	{
565
		$db_type = 'mysql';
566
		// If overriding $db_type, need to set its settings.php entry too
567
		$changes = array();
568
		$changes['db_type'] = '\'mysql\'';
569
		require_once($sourcedir . '/Subs-Admin.php');
570
		updateSettingsFile($changes);
571
	}
572
573
	if (file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
574
	{
575
		require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
576
577
		// Make the connection...
578 View Code Duplication
		if (empty($db_connection))
579
		{
580
			$options = array('non_fatal' => true);
581
			// Add in the port if needed
582
			if (!empty($db_port))
583
				$options['port'] = $db_port;
584
			
585
			if (!empty($db_mb4))
586
				$options['db_mb4'] = $db_mb4;
587
			
588
			$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
589
		}
590
		else
591
			// If we've returned here, ping/reconnect to be safe
592
			$smcFunc['db_ping']($db_connection);
593
594
		// Oh dear god!!
595
		if ($db_connection === null)
596
			die('Unable to connect to database - please check username and password are correct in Settings.php');
597
598
		if ($db_type == 'mysql' && isset($db_character_set) && preg_match('~^\w+$~', $db_character_set) === 1)
599
			$smcFunc['db_query']('', '
600
			SET NAMES {string:db_character_set}',
601
			array(
602
				'db_error_skip' => true,
603
				'db_character_set' => $db_character_set,
604
			)
605
		);
606
607
		// Load the modSettings data...
608
		$request = $smcFunc['db_query']('', '
609
			SELECT variable, value
610
			FROM {db_prefix}settings',
611
			array(
612
				'db_error_skip' => true,
613
			)
614
		);
615
		$modSettings = array();
616 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
617
			$modSettings[$row['variable']] = $row['value'];
618
		$smcFunc['db_free_result']($request);
619
	}
620
	else
621
	{
622
		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.');
623
	}
624
625
	require_once($sourcedir . '/Subs.php');
626
627
	// If they don't have the file, they're going to get a warning anyway so we won't need to clean request vars.
628
	if (file_exists($sourcedir . '/QueryString.php') && php_version_check())
629
	{
630
		require_once($sourcedir . '/QueryString.php');
631
		cleanRequest();
632
	}
633
634
	if (!isset($_GET['substep']))
635
		$_GET['substep'] = 0;
636
}
637
638
function initialize_inputs()
639
{
640
	global $start_time, $db_type;
641
642
	$start_time = time();
643
644
	umask(0);
645
646
	ob_start();
647
648
	// Better to upgrade cleanly and fall apart than to screw everything up if things take too long.
649
	ignore_user_abort(true);
650
651
	// This is really quite simple; if ?delete is on the URL, delete the upgrader...
652
	if (isset($_GET['delete']))
653
	{
654
		@unlink(__FILE__);
655
656
		// And the extra little files ;).
657
		@unlink(dirname(__FILE__) . '/upgrade_1-0.sql');
658
		@unlink(dirname(__FILE__) . '/upgrade_1-1.sql');
659
		@unlink(dirname(__FILE__) . '/upgrade_2-0_' . $db_type . '.sql');
660
		@unlink(dirname(__FILE__) . '/upgrade_2-1_' . $db_type . '.sql');
661
		@unlink(dirname(__FILE__) . '/upgrade-helper.php');
662
663
		$dh = opendir(dirname(__FILE__));
664
		while ($file = readdir($dh))
665
		{
666
			if (preg_match('~upgrade_\d-\d_([A-Za-z])+\.sql~i', $file, $matches) && isset($matches[1]))
667
				@unlink(dirname(__FILE__) . '/' . $file);
668
		}
669
		closedir($dh);
670
671
		// Legacy files while we're at it. NOTE: We only touch files we KNOW shouldn't be there.
672
		// 1.1 Sources files not in 2.0+
673
		@unlink(dirname(__FILE__) . '/Sources/ModSettings.php');
674
		// 1.1 Templates that don't exist any more (e.g. renamed)
675
		@unlink(dirname(__FILE__) . '/Themes/default/Combat.template.php');
676
		@unlink(dirname(__FILE__) . '/Themes/default/Modlog.template.php');
677
		// 1.1 JS files were stored in the main theme folder, but in 2.0+ are in the scripts/ folder
678
		@unlink(dirname(__FILE__) . '/Themes/default/fader.js');
679
		@unlink(dirname(__FILE__) . '/Themes/default/script.js');
680
		@unlink(dirname(__FILE__) . '/Themes/default/spellcheck.js');
681
		@unlink(dirname(__FILE__) . '/Themes/default/xml_board.js');
682
		@unlink(dirname(__FILE__) . '/Themes/default/xml_topic.js');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

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