Completed
Push — development ( 5236fa...488938 )
by Stephen
20s
created

Bootstrap::clearGlobals()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 0
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
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.1
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
			// We're going to set a few globals
40
			global $time_start, $ssi_error_reporting, $db_show_debug;
41
42
			// Your on the clock
43
			$time_start = microtime(true);
44
45
			// Unless settings.php tells us otherwise
46
			$db_show_debug = false;
47
48
			// Report errors but not depreciated ones
49
			$ssi_error_reporting = error_reporting(E_ALL & ~E_DEPRECATED);
50
51
			// Get the things needed for ALL modes
52
			$this->bringUpBasics();
53
54
			// Going to run from the side entrance and not directly from inside elkarte
55
			if ($standalone)
56
			{
57
				$this->ssi_main();
58
			}
59
		}
60
	}
61
62
	/**
63
	 * Calls the various initialization functions in the needed order
64
	 */
65
	public function bringUpBasics()
66
	{
67
		$this->setConstants();
68
		$this->setRusage();
69
		$this->clearGlobals();
70
		$this->loadSettingsFile();
71
		$this->validatePaths();
72
		$this->loadDependants();
73
		$this->loadAutoloader();
74
		$this->checkMaintance();
75
		$this->setDebug();
76
		$this->bringUp();
77
	}
78
79
	/**
80
	 * Set the core constants, you know the ones we often forget to
81
	 * update on new releases.
82
	 */
83
	private function setConstants()
84
	{
85
		// First things first, but not necessarily in that order.
86
		if (!defined('ELK'))
87
		{
88
			define('ELK', '1');
89
		}
90
		define('ELKBOOT', '1');
91
92
		// The software version
93
		define('FORUM_VERSION', 'ElkArte 1.1.1');
94
95
		// Shortcut for the browser cache stale
96
		define('CACHE_STALE', '?R111');
97
	}
98
99
	/**
100
	 * Get initial resource usage
101
	 */
102
	private function setRusage()
103
	{
104
		global $rusage_start;
105
106
		// Directional only script time usage for display
107
		// getrusage is missing in php < 7 on Windows
108
		if (function_exists('getrusage'))
109
		{
110
			$rusage_start = getrusage();
111
		}
112
		else
113
		{
114
			$rusage_start = array();
115
		}
116
	}
117
118
	/**
119
	 * If they glo, they need to be cleaned.
120
	 */
121
	private function clearGlobals()
122
	{
123
		// We don't need no globals. (a bug in "old" versions of PHP)
124
		foreach (array('db_character_set', 'cachedir') as $variable)
125
		{
126
			if (isset($GLOBALS[$variable]))
127
			{
128
				unset($GLOBALS[$variable], $GLOBALS[$variable]);
129
			}
130
		}
131
	}
132
133
	/**
134
	 * Loads the settings values into the global space
135
	 */
136
	private function loadSettingsFile()
137
	{
138
		// All those wonderful things found in settings
139
		global $maintenance, $mtitle, $msubject, $mmessage, $mbname, $language, $boardurl, $webmaster_email;
140
		global $cookiename, $db_type, $db_server, $db_port, $db_name, $db_user, $db_passwd;
141
		global $ssi_db_user, $ssi_db_passwd, $db_prefix, $db_persist, $db_error_send, $cache_accelerator;
142
		global $cache_uid, $cache_password, $cache_enable, $cache_memcached, $db_show_debug;
143
		global $cachedir, $boarddir, $sourcedir, $extdir, $languagedir, $ignore_install_dir;
144
145
		// Where the Settings.php file is located
146
		$settings_loc = __DIR__ . '/Settings.php';
147
148
		// First thing: if the install dir exists, just send anybody there
149
		// The ignore_install_dir var is for developers only. Do not add it on production sites
150
		if (file_exists('install'))
151
		{
152
			if (file_exists($settings_loc))
153
			{
154
				require_once($settings_loc);
155
			}
156
157
			if (empty($ignore_install_dir))
158
			{
159
				if (file_exists($settings_loc) && empty($_SESSION['installing']))
160
				{
161
					$redirec_file = 'upgrade.php';
162
				}
163
				else
164
				{
165
					$redirec_file = 'install.php';
166
				}
167
168
				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 168

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...
169
				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...
170
			}
171
		}
172
		else
173
		{
174
			require_once($settings_loc);
175
		}
176
	}
177
178
	/**
179
	 * Validate the paths set in Settings.php, correct as needed and move
180
	 * them to constants.
181
	 */
182
	private function validatePaths()
183
	{
184
		global $boarddir, $sourcedir, $cachedir, $extdir, $languagedir;
185
186
		// Make sure the paths are correct... at least try to fix them.
187
		if (!file_exists($boarddir) && file_exists(__DIR__ . '/agreement.txt'))
188
		{
189
			$boarddir = __DIR__;
190
		}
191
192
		if (!file_exists($sourcedir . '/SiteDispatcher.class.php') && file_exists($boarddir . '/sources'))
193
		{
194
			$sourcedir = $boarddir . '/sources';
195
		}
196
197
		// Check that directories which didn't exist in past releases are initialized.
198
		if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
199
		{
200
			$cachedir = $boarddir . '/cache';
201
		}
202
203
		if ((empty($extdir) || !file_exists($extdir)) && file_exists($sourcedir . '/ext'))
204
		{
205
			$extdir = $sourcedir . '/ext';
206
		}
207
208
		if ((empty($languagedir) || !file_exists($languagedir)) && file_exists($boarddir . '/themes/default/languages'))
209
		{
210
			$languagedir = $boarddir . '/themes/default/languages';
211
		}
212
213
		// Time to forget about variables and go with constants!
214
		define('BOARDDIR', $boarddir);
215
		define('CACHEDIR', $cachedir);
216
		define('EXTDIR', $extdir);
217
		define('LANGUAGEDIR', $languagedir);
218
		define('SOURCEDIR', $sourcedir);
219
		define('ADMINDIR', $sourcedir . '/admin');
220
		define('CONTROLLERDIR', $sourcedir . '/controllers');
221
		define('SUBSDIR', $sourcedir . '/subs');
222
		define('ADDONSDIR', $boarddir . '/addons');
223
		unset($boarddir, $cachedir, $sourcedir, $languagedir, $extdir);
224
	}
225
226
	/**
227
	 * We require access to several important files, so load them upfront
228
	 */
229
	private function loadDependants()
230
	{
231
		// Files we cannot live without.
232
		require_once(SOURCEDIR . '/QueryString.php');
233
		require_once(SOURCEDIR . '/Session.php');
234
		require_once(SOURCEDIR . '/Subs.php');
235
		require_once(SOURCEDIR . '/Logging.php');
236
		require_once(SOURCEDIR . '/Load.php');
237
		require_once(SOURCEDIR . '/Security.php');
238
		require_once(SUBSDIR . '/Cache.subs.php');
239
	}
240
241
	/**
242
	 * The autoloader will take care most requests for files
243
	 */
244
	private function loadAutoloader()
245
	{
246
		// Initialize the class Autoloader
247
		require_once(SOURCEDIR . '/Autoloader.class.php');
248
		$autoloader = Elk_Autoloader::instance();
249
		$autoloader->setupAutoloader(array(SOURCEDIR, SUBSDIR, CONTROLLERDIR, ADMINDIR, ADDONSDIR));
250
		$autoloader->register(SOURCEDIR, '\\ElkArte');
251
		$autoloader->register(SOURCEDIR . '/subs/BBC', '\\BBC');
252
	}
253
254
	/**
255
	 * Check if we are in maintance mode, if so end here.
256
	 */
257
	private function checkMaintance()
258
	{
259
		global $maintenance, $ssi_maintenance_off;
260
261
		// Don't do john didley if the forum's been shut down completely.
262
		if (!empty($maintenance) && $maintenance == 2 && (!isset($ssi_maintenance_off) || $ssi_maintenance_off !== true))
263
		{
264
			Errors::instance()->display_maintenance_message();
265
		}
266
	}
267
268
	/**
269
	 * If you like lots of debug information in error messages and below the footer
270
	 * then set $db_show_debug to true in settings.  Don't do this on a production site.
271
	 */
272
	private function setDebug()
273
	{
274
		global $db_show_debug, $rusage_start;
275
276
		// Show lots of debug information below the page, not for production sites
277
		if ($db_show_debug === true)
278
		{
279
			Debug::instance()->rusage('start', $rusage_start);
280
		}
281
	}
282
283
	/**
284
	 * Time to see what has been requested, by whom and dispatch it to the proper handler
285
	 */
286
	private function bringUp()
287
	{
288
		global $context;
289
290
		// Clean the request.
291
		cleanRequest();
292
293
		// Initiate the database connection and define some database functions to use.
294
		loadDatabase();
295
296
		// Let's set up our shiny new hooks handler.
297
		Hooks::init(database(), Debug::instance());
298
299
		// It's time for settings loaded from the database.
300
		reloadSettings();
301
302
		// Our good ole' contextual array, which will hold everything
303
		if (empty($context))
304
		{
305
			$context = array();
306
		}
307
	}
308
309
	/**
310
	 * If you are running SSI standalone, you need to call this function after bootstrap is
311
	 * initialized.
312
	 *
313
	 * @throws \Elk_Exception
314
	 */
315
	public function ssi_main()
316
	{
317
		global $ssi_layers, $ssi_theme, $ssi_gzip, $ssi_ban, $ssi_guest_access;
318
		global $modSettings, $context, $sc, $board, $topic, $user_info, $txt;
319
320
		// Check on any hacking attempts.
321
		$this->_validRequestCheck();
322
323
		// Gzip output? (because it must be boolean and true, this can't be hacked.)
324
		if (isset($ssi_gzip) && $ssi_gzip === true && detectServer()->outPutCompressionEnabled())
325
		{
326
			ob_start('ob_gzhandler');
327
		}
328
		else
329
		{
330
			$modSettings['enableCompressedOutput'] = '0';
331
		}
332
333
		// Primarily, this is to fix the URLs...
334
		ob_start('ob_sessrewrite');
335
336
		// Start the session... known to scramble SSI includes in cases...
337
		if (!headers_sent())
338
		{
339
			loadSession();
340
		}
341
		else
342
		{
343
			if (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))
344
			{
345
				// Make a stab at it, but ignore the E_WARNINGs generated because we can't send headers.
346
				$temp = error_reporting(error_reporting() & !E_WARNING);
347
				loadSession();
348
				error_reporting($temp);
349
			}
350
351 View Code Duplication
			if (!isset($_SESSION['session_value']))
352
			{
353
				$tokenizer = new Token_Hash();
354
				$_SESSION['session_value'] = $tokenizer->generate_hash(32, session_id());
355
				$_SESSION['session_var'] = substr(preg_replace('~^\d+~', '', $tokenizer->generate_hash(16, session_id())), 0, rand(7, 12));
356
			}
357
358
			$sc = $_SESSION['session_value'];
359
			// This is here only to avoid session errors in PHP7
360
			// microtime effectively forces the replacing of the session in the db each
361
			// time the page is loaded
362
			$_SESSION['mictrotime'] = microtime();
363
		}
364
365
		// Get rid of $board and $topic... do stuff loadBoard would do.
366
		unset($board, $topic);
367
		$user_info['is_mod'] = false;
368
		$context['user']['is_mod'] = &$user_info['is_mod'];
369
		$context['linktree'] = array();
370
371
		// Load the user and their cookie, as well as their settings.
372
		loadUserSettings();
373
374
		// Load the current user's permissions....
375
		loadPermissions();
376
377
		// Load the current or SSI theme. (just use $ssi_theme = id_theme;)
378
		new ElkArte\Themes\ThemeLoader(isset($ssi_theme) ? (int) $ssi_theme : 0);
379
380
		// Load BadBehavior functions
381
		loadBadBehavior();
382
383
		// @todo: probably not the best place, but somewhere it should be set...
384
		if (!headers_sent())
385
		{
386
			header('Content-Type: text/html; charset=UTF-8');
387
		}
388
389
		// Take care of any banning that needs to be done.
390
		if (isset($_REQUEST['ssi_ban']) || (isset($ssi_ban) && $ssi_ban === true))
391
		{
392
			is_not_banned();
393
		}
394
395
		// Do we allow guests in here?
396
		if (empty($ssi_guest_access) && empty($modSettings['allow_guestAccess']) && $user_info['is_guest'] && basename($_SERVER['PHP_SELF']) !== 'SSI.php')
397
		{
398
			$controller = new Auth_Controller(new Event_manager());
399
			$controller->action_kickguest();
400
			obExit(null, true);
401
		}
402
403
		if (!empty($modSettings['front_page']) && is_callable(array($modSettings['front_page'], 'frontPageHook')))
404
		{
405
			$modSettings['default_forum_action'] = '?action=forum;';
406
		}
407
		else
408
		{
409
			$modSettings['default_forum_action'] = '';
410
		}
411
412
		// Load the stuff like the menu bar, etc.
413
		if (isset($ssi_layers))
414
		{
415
			$template_layers = Template_Layers::instance();
416
			$template_layers->removeAll();
417
			foreach ($ssi_layers as $layer)
418
			{
419
				$template_layers->addBegin($layer);
420
			}
421
			template_header();
422
		}
423
		else
424
		{
425
			setupThemeContext();
426
		}
427
428
		// We need to set up user agent, and make more checks on the request
429
		$req = request();
430
431
		// Make sure they didn't muss around with the settings... but only if it's not cli.
432
		if (isset($_SERVER['REMOTE_ADDR']) && session_id() === '')
433
		{
434
			trigger_error($txt['ssi_session_broken'], E_USER_NOTICE);
435
		}
436
437
		// Without visiting the forum this session variable might not be set on submit.
438
		if (!isset($_SESSION['USER_AGENT']) && (!isset($_GET['ssi_function']) || $_GET['ssi_function'] !== 'pollVote'))
439
		{
440
			$_SESSION['USER_AGENT'] = $req->user_agent();
441
		}
442
	}
443
444
	/**
445
	 * Used to ensure SSI requests are valid and not a probing attempt
446
	 */
447
	private function _validRequestCheck()
448
	{
449
		global $ssi_theme, $ssi_layers;
450
451
		// Check on any hacking attempts.
452
		if (
453
			isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS'])
454
			|| isset($_REQUEST['ssi_theme']) && (int) $_REQUEST['ssi_theme'] == (int) $ssi_theme
455
			|| isset($_COOKIE['ssi_theme']) && (int) $_COOKIE['ssi_theme'] == (int) $ssi_theme
456
			|| isset($_REQUEST['ssi_layers'], $ssi_layers) && $_REQUEST['ssi_layers'] == $ssi_layers
457
			|| isset($_REQUEST['context']))
458
		{
459
			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...
460
		}
461
	}
462
}
463