Completed
Pull Request — patch_1-1-4 (#3210)
by Emanuele
12:56
created

bootstrap.php (8 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

1
<?php
2
3
/**
4
 * Initialize the ElkArte environment.
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1.4
15
 *
16
 */
17
18
/**
19
 * Class Bootstrap
20
 *
21
 * This takes care of the initial loading and feeding of Elkarte from
22
 * either SSI or Index
23
 */
24
class Bootstrap
25
{
26
	/**
27
	 * Bootstrap constructor.
28
	 *
29
	 * @param bool $standalone
30
	 *  - true to boot outside of elkarte
31
	 *  - false to bootstrap the main elkarte site.
32
	 * @throws \Elk_Exception
33
	 */
34
	public function __construct($standalone = true)
35
	{
36
		// Bootstrap only once.
37
		if (defined('ELKBOOT'))
38
		{
39
			return true;
0 ignored issues
show
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
40
		}
41
42
		// We're going to set a few globals
43
		global $time_start, $ssi_error_reporting, $db_show_debug;
44
45
		// Your on the clock
46
		$time_start = microtime(true);
47
48
		// Unless settings.php tells us otherwise
49
		$db_show_debug = false;
50
51
		// Report errors but not depreciated ones
52
		$ssi_error_reporting = error_reporting(E_ALL | E_STRICT & ~8192);
53
54
		// Get the things needed for ALL modes
55
		$this->bringUpBasics();
56
57
		// Going to run from the side entrance and not directly from inside elkarte
58
		if ($standalone)
59
		{
60
			$this->ssi_main();
61
		}
62
	}
63
64
	/**
65
	 * Calls the various initialization functions in the needed order
66
	 */
67
	public function bringUpBasics()
68
	{
69
		$this->setConstants();
70
		$this->setRusage();
71
		$this->clearGloballs();
72
		$this->loadSettingsFile();
73
		$this->validatePaths();
74
		$this->loadDependants();
75
		$this->loadAutoloader();
76
		$this->checkMaintance();
77
		$this->setDebug();
78
		$this->bringUp();
79
	}
80
81
	/**
82
	 * Set the core constants, you know the ones we often forget to
83
	 * update on new releases.
84
	 */
85
	private function setConstants()
86
	{
87
		// First things first, but not necessarily in that order.
88
		if (!defined('ELK'))
89
		{
90
			define('ELK', '1');
91
		}
92
		define('ELKBOOT', '1');
93
94
		// The software version
95
		define('FORUM_VERSION', 'ElkArte 1.1.4');
96
97
		// Shortcut for the browser cache stale
98
		define('CACHE_STALE', '?R114');
99
	}
100
101
	/**
102
	 * Get initial resource usage
103
	 */
104
	private function setRusage()
105
	{
106
		global $rusage_start;
107
108
		// Directional only script time usage for display
109
		// getrusage is missing in php < 7 on Windows
110
		if (function_exists('getrusage'))
111
		{
112
			$rusage_start = getrusage();
113
		}
114
		else
115
		{
116
			$rusage_start = array();
117
		}
118
	}
119
120
	/**
121
	 * If they glo, they need to be cleaned.
122
	 */
123
	private function clearGloballs()
124
	{
125
		// We don't need no globals. (a bug in "old" versions of PHP)
126
		foreach (array('db_character_set', 'cachedir') as $variable)
127
		{
128
			if (isset($GLOBALS[$variable]))
129
			{
130
				unset($GLOBALS[$variable], $GLOBALS[$variable]);
131
			}
132
		}
133
	}
134
135
	/**
136
	 * Loads the settings values into the global space
137
	 */
138
	private function loadSettingsFile()
139
	{
140
		// All those wonderful things found in settings
141
		global $maintenance, $mtitle, $msubject, $mmessage, $mbname, $language, $boardurl, $webmaster_email;
142
		global $cookiename, $db_type, $db_server, $db_port, $db_name, $db_user, $db_passwd;
143
		global $ssi_db_user, $ssi_db_passwd, $db_prefix, $db_persist, $db_error_send, $cache_accelerator;
144
		global $cache_uid, $cache_password, $cache_enable, $cache_memcached, $db_show_debug;
145
		global $cachedir, $boarddir, $sourcedir, $extdir, $languagedir, $ignore_install_dir;
146
147
		// Where the Settings.php file is located
148
		$settings_loc = __DIR__ . '/Settings.php';
149
150
		// First thing: if the install dir exists, just send anybody there
151
		// The ignore_install_dir var is for developers only. Do not add it on production sites
152
		if (file_exists('install'))
153
		{
154
			if (file_exists($settings_loc))
155
			{
156
				require_once($settings_loc);
157
			}
158
159
			if (empty($ignore_install_dir))
160
			{
161
				if (file_exists($settings_loc) && empty($_SESSION['installing']))
162
				{
163
					$redirec_file = 'upgrade.php';
164
				}
165
				else
166
				{
167
					$redirec_file = 'install.php';
168
				}
169
170
				header('Location: http' . (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . (empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] === '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST']) . (strtr(dirname($_SERVER['PHP_SELF']), '\\', '/') == '/' ? '' : strtr(dirname($_SERVER['PHP_SELF']), '\\', '/')) . '/install/' . $redirec_file);
0 ignored issues
show
Security Response Splitting introduced by
'Location: http' . (!emp...stall/' . $redirec_file can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key HTTP_HOST from $_SERVER
    in bootstrap.php on line 170

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
171
				die();
0 ignored issues
show
Coding Style Compatibility introduced by
The method loadSettingsFile() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
172
			}
173
		}
174
		else
175
		{
176
			require_once($settings_loc);
177
		}
178
	}
179
180
	/**
181
	 * Validate the paths set in Settings.php, correct as needed and move
182
	 * them to constants.
183
	 */
184
	private function validatePaths()
185
	{
186
		global $boarddir, $sourcedir, $cachedir, $extdir, $languagedir;
187
188
		// Make sure the paths are correct... at least try to fix them.
189
		if (!file_exists($boarddir) && file_exists(__DIR__ . '/agreement.txt'))
190
		{
191
			$boarddir = __DIR__;
192
		}
193
194
		if (!file_exists($sourcedir . '/SiteDispatcher.class.php') && file_exists($boarddir . '/sources'))
195
		{
196
			$sourcedir = $boarddir . '/sources';
197
		}
198
199
		// Check that directories which didn't exist in past releases are initialized.
200
		if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
201
		{
202
			$cachedir = $boarddir . '/cache';
203
		}
204
205
		if ((empty($extdir) || !file_exists($extdir)) && file_exists($sourcedir . '/ext'))
206
		{
207
			$extdir = $sourcedir . '/ext';
208
		}
209
210
		if ((empty($languagedir) || !file_exists($languagedir)) && file_exists($boarddir . '/themes/default/languages'))
211
		{
212
			$languagedir = $boarddir . '/themes/default/languages';
213
		}
214
215
		// Time to forget about variables and go with constants!
216
		define('BOARDDIR', $boarddir);
217
		define('CACHEDIR', $cachedir);
218
		define('EXTDIR', $extdir);
219
		define('LANGUAGEDIR', $languagedir);
220
		define('SOURCEDIR', $sourcedir);
221
		define('ADMINDIR', $sourcedir . '/admin');
222
		define('CONTROLLERDIR', $sourcedir . '/controllers');
223
		define('SUBSDIR', $sourcedir . '/subs');
224
		define('ADDONSDIR', $boarddir . '/addons');
225
		unset($boarddir, $cachedir, $sourcedir, $languagedir, $extdir);
226
	}
227
228
	/**
229
	 * We require access to several important files, so load them upfront
230
	 */
231
	private function loadDependants()
232
	{
233
		// Files we cannot live without.
234
		require_once(SOURCEDIR . '/QueryString.php');
235
		require_once(SOURCEDIR . '/Session.php');
236
		require_once(SOURCEDIR . '/Subs.php');
237
		require_once(SOURCEDIR . '/Logging.php');
238
		require_once(SOURCEDIR . '/Load.php');
239
		require_once(SOURCEDIR . '/Security.php');
240
		require_once(SUBSDIR . '/Cache.subs.php');
241
	}
242
243
	/**
244
	 * The autoloader will take care most requests for files
245
	 */
246
	private function loadAutoloader()
247
	{
248
		// Initialize the class Autoloader
249
		require_once(SOURCEDIR . '/Autoloader.class.php');
250
		$autoloader = Elk_Autoloader::instance();
251
		$autoloader->setupAutoloader(array(SOURCEDIR, SUBSDIR, CONTROLLERDIR, ADMINDIR, ADDONSDIR));
252
		$autoloader->register(SOURCEDIR, '\\ElkArte');
253
		$autoloader->register(SOURCEDIR . '/subs/BBC', '\\BBC');
254
	}
255
256
	/**
257
	 * Check if we are in maintance mode, if so end here.
258
	 */
259
	private function checkMaintance()
260
	{
261
		global $maintenance, $ssi_maintenance_off;
262
263
		// Don't do john didley if the forum's been shut down completely.
264
		if (!empty($maintenance) && $maintenance == 2 && (!isset($ssi_maintenance_off) || $ssi_maintenance_off !== true))
265
		{
266
			Errors::instance()->display_maintenance_message();
267
		}
268
	}
269
270
	/**
271
	 * If you like lots of debug information in error messages and below the footer
272
	 * then set $db_show_debug to true in settings.  Don't do this on a production site.
273
	 */
274
	private function setDebug()
275
	{
276
		global $db_show_debug, $rusage_start;
277
278
		// Show lots of debug information below the page, not for production sites
279
		if ($db_show_debug === true)
280
		{
281
			Debug::instance()->rusage('start', $rusage_start);
282
		}
283
	}
284
285
	/**
286
	 * Time to see what has been requested, by whom and dispatch it to the proper handler
287
	 */
288
	private function bringUp()
289
	{
290
		global $context;
291
292
		// Clean the request.
293
		cleanRequest();
294
295
		// Initiate the database connection and define some database functions to use.
296
		loadDatabase();
297
298
		// Let's set up our shiny new hooks handler.
299
		Hooks::init(database(), Debug::instance());
300
301
		// It's time for settings loaded from the database.
302
		reloadSettings();
303
304
		// Our good ole' contextual array, which will hold everything
305
		if (empty($context))
306
		{
307
			$context = array();
308
		}
309
310
		// Seed the random generator.
311
		elk_seed_generator();
0 ignored issues
show
Deprecated Code introduced by
The function elk_seed_generator() has been deprecated.

This function has been deprecated.

Loading history...
312
	}
313
314
	/**
315
	 * If you are running SSI standalone, you need to call this function after bootstrap is
316
	 * initialized.
317
	 *
318
	 * @throws \Elk_Exception
319
	 */
320
	public function ssi_main()
321
	{
322
		global $ssi_layers, $ssi_theme, $ssi_gzip, $ssi_ban, $ssi_guest_access;
323
		global $modSettings, $context, $sc, $board, $topic, $user_info, $txt;
324
325
		// Check on any hacking attempts.
326
		$this->_validRequestCheck();
327
328
		// Gzip output? (because it must be boolean and true, this can't be hacked.)
329
		if (isset($ssi_gzip) && $ssi_gzip === true && detectServer()->outPutCompressionEnabled())
330
		{
331
			ob_start('ob_gzhandler');
332
		}
333
		else
334
		{
335
			$modSettings['enableCompressedOutput'] = '0';
336
		}
337
338
		// Primarily, this is to fix the URLs...
339
		ob_start('ob_sessrewrite');
340
341
		// Start the session... known to scramble SSI includes in cases...
342
		if (!headers_sent())
343
		{
344
			loadSession();
345
		}
346
		else
347
		{
348
			if (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))
349
			{
350
				// Make a stab at it, but ignore the E_WARNINGs generated because we can't send headers.
351
				$temp = error_reporting(error_reporting() & !E_WARNING);
352
				loadSession();
353
				error_reporting($temp);
354
			}
355
356 View Code Duplication
			if (!isset($_SESSION['session_value']))
357
			{
358
				$tokenizer = new Token_Hash();
359
				$_SESSION['session_value'] = $tokenizer->generate_hash(32, session_id());
360
				$_SESSION['session_var'] = substr(preg_replace('~^\d+~', '', $tokenizer->generate_hash(16, session_id())), 0, rand(7, 12));
361
			}
362
363
			$sc = $_SESSION['session_value'];
364
			// This is here only to avoid session errors in PHP7
365
			// microtime effectively forces the replacing of the session in the db each
366
			// time the page is loaded
367
			$_SESSION['mictrotime'] = microtime();
368
		}
369
370
		// Get rid of $board and $topic... do stuff loadBoard would do.
371
		unset($board, $topic);
372
		$user_info['is_mod'] = false;
373
		$context['user']['is_mod'] = &$user_info['is_mod'];
374
		$context['linktree'] = array();
375
376
		// Load the user and their cookie, as well as their settings.
377
		loadUserSettings();
378
379
		// Load the current user's permissions....
380
		loadPermissions();
381
382
		// Load the current or SSI theme. (just use $ssi_theme = id_theme;)
383
		loadTheme(isset($ssi_theme) ? (int) $ssi_theme : 0);
384
385
		// Load BadBehavior functions
386
		loadBadBehavior();
387
388
		// @todo: probably not the best place, but somewhere it should be set...
389
		if (!headers_sent())
390
		{
391
			header('Content-Type: text/html; charset=UTF-8');
392
		}
393
394
		// Take care of any banning that needs to be done.
395
		if (isset($_REQUEST['ssi_ban']) || (isset($ssi_ban) && $ssi_ban === true))
396
		{
397
			is_not_banned();
398
		}
399
400
		// Do we allow guests in here?
401
		if (empty($ssi_guest_access) && empty($modSettings['allow_guestAccess']) && $user_info['is_guest'] && basename($_SERVER['PHP_SELF']) !== 'SSI.php')
402
		{
403
			$controller = new Auth_Controller();
404
			$controller->action_kickguest();
405
			obExit(null, true);
406
		}
407
408
		if (!empty($modSettings['front_page']) && is_callable(array($modSettings['front_page'], 'frontPageHook')))
409
		{
410
			$modSettings['default_forum_action'] = '?action=forum;';
411
		}
412
		else
413
		{
414
			$modSettings['default_forum_action'] = '';
415
		}
416
417
		// Load the stuff like the menu bar, etc.
418
		if (isset($ssi_layers))
419
		{
420
			$template_layers = Template_Layers::instance();
421
			$template_layers->removeAll();
422
			foreach ($ssi_layers as $layer)
423
			{
424
				$template_layers->addBegin($layer);
425
			}
426
			template_header();
427
		}
428
		else
429
		{
430
			setupThemeContext();
431
		}
432
433
		// We need to set up user agent, and make more checks on the request
434
		$req = request();
435
436
		// Make sure they didn't muss around with the settings... but only if it's not cli.
437
		if (isset($_SERVER['REMOTE_ADDR']) && session_id() === '')
438
		{
439
			trigger_error($txt['ssi_session_broken'], E_USER_NOTICE);
440
		}
441
442
		// Without visiting the forum this session variable might not be set on submit.
443
		if (!isset($_SESSION['USER_AGENT']) && (!isset($_GET['ssi_function']) || $_GET['ssi_function'] !== 'pollVote'))
444
		{
445
			$_SESSION['USER_AGENT'] = $req->user_agent();
446
		}
447
	}
448
449
	/**
450
	 * Used to ensure SSI requests are valid and not a probing attempt
451
	 */
452
	private function _validRequestCheck()
453
	{
454
		global $ssi_theme, $ssi_layers;
455
456
		if (isset($_REQUEST['ssi_theme']) && (int) $_REQUEST['ssi_theme'] === (int) $ssi_theme)
457
		{
458
			die('No access...');
0 ignored issues
show
Coding Style Compatibility introduced by
The method _validRequestCheck() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
459
		}
460
		elseif (isset($_COOKIE['ssi_theme']) && (int) $_COOKIE['ssi_theme'] === (int) $ssi_theme)
461
		{
462
			die('No access...');
0 ignored issues
show
Coding Style Compatibility introduced by
The method _validRequestCheck() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
463
		}
464
		elseif (isset($_REQUEST['ssi_layers'], $ssi_layers) && (@get_magic_quotes_gpc() ? stripslashes($_REQUEST['ssi_layers']) : $_REQUEST['ssi_layers']) == $ssi_layers)
465
		{
466
			die('No access...');
0 ignored issues
show
Coding Style Compatibility introduced by
The method _validRequestCheck() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
467
		}
468
469
		// Yeah right
470
		if (isset($_REQUEST['context']))
471
		{
472
			die('No access...');
0 ignored issues
show
Coding Style Compatibility introduced by
The method _validRequestCheck() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
473
		}
474
	}
475
}
476