Passed
Pull Request — development (#3540)
by Emanuele
07:01
created

bootstrap.php (2 issues)

1
<?php
2
3
/**
4
 * Initialize the ElkArte environment.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 */
15
16
use ElkArte\Debug;
17
use ElkArte\ext\Composer\Autoload\ClassLoader;
0 ignored issues
show
The type ElkArte\ext\Composer\Autoload\ClassLoader was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use ElkArte\Hooks;
19
use ElkArte\User;
20
use ElkArte\TokenHash;
21
use ElkArte\Errors\Errors;
22
use ElkArte\MembersList;
23
use ElkArte\Cache\Cache;
24
use ElkArte\Themes\ThemeLoader;
25
use ElkArte\Controller\Auth;
26
use ElkArte\EventManager;
27
use BBC\ParserWrapper;
28
29
/**
30
 * Class Bootstrap
31
 *
32
 * Takes care of the initial loading and feeding of Elkarte from
33
 * either SSI or Index
34
 */
35
class Bootstrap
36
{
37
	/**
38
	 * What is returned by the function getrusage.
39
	 *
40
	 * @var mixed[]
41
	 */
42
	protected $rusage_start = [];
43
44
	/**
45
	 * Bootstrap constructor.
46
	 *
47
	 * @param bool $standalone
48
	 *  - true to boot outside elkarte
49
	 *  - false to bootstrap the main elkarte site.
50
	 */
51
	public function __construct($standalone = true)
52
	{
53
		// Bootstrap only once.
54
		if (!defined('ELKBOOT'))
55
		{
56
			// We're going to set a few globals
57
			global $time_start, $ssi_error_reporting, $db_show_debug;
58
59
			// Your on the clock
60
			$time_start = microtime(true);
61
62
			// Unless settings.php tells us otherwise
63
			$db_show_debug = false;
64
65
			// Report errors but not depreciated ones
66
			$ssi_error_reporting = error_reporting(E_ALL & ~E_DEPRECATED);
67
68
			// Get the things needed for ALL modes
69
			$this->bringUpBasics();
70
71
			// Going to run from the side entrance and not directly from inside elkarte
72
			if ($standalone)
73
			{
74
				$this->ssi_main();
75
			}
76
		}
77
	}
78
79
	/**
80
	 * Calls the various initialization functions in the needed order
81
	 */
82
	public function bringUpBasics()
83
	{
84
		$this->setConstants();
85
		$this->setRusage();
86
		$this->clearGlobals();
87
		$this->loadSettingsFile();
88
		$this->validatePaths();
89
		$this->loadDependants();
90
		$this->loadAutoloader();
91
		$this->checkMaintance();
92
		$this->setDebug();
93
		$this->bringUp();
94
	}
95
96
	/**
97
	 * Set the core constants, you know the ones we often forget to
98
	 * update on new releases.
99
	 */
100
	private function setConstants()
101
	{
102
		// First things first, but not necessarily in that order.
103
		if (!defined('ELK'))
104
		{
105
			define('ELK', '1');
106
		}
107
		define('ELKBOOT', '1');
108
109
		// The software version
110
		define('FORUM_VERSION', 'ElkArte 2.0 dev');
111
112
		// Shortcut for the browser cache stale
113
		define('CACHE_STALE', '?20dev');
114
	}
115
116
	/**
117
	 * Get initial resource usage
118
	 */
119
	private function setRusage()
120
	{
121
		$this->rusage_start = getrusage();
122
	}
123
124
	/**
125
	 * If they glo, they need to be cleaned.
126
	 */
127
	private function clearGlobals()
128
	{
129
		// We don't need no globals. (a bug in "old" versions of PHP)
130
		foreach (array('db_character_set', 'cachedir') as $variable)
131
		{
132
			if (isset($GLOBALS[$variable]))
133
			{
134
				unset($GLOBALS[$variable], $GLOBALS[$variable]);
135
			}
136
		}
137
	}
138
139
	/**
140
	 * Loads the settings values into the global space
141
	 */
142
	private function loadSettingsFile()
143
	{
144
		// All those wonderful things found in settings
145
		global $maintenance, $mtitle, $msubject, $mmessage, $mbname, $language, $boardurl, $webmaster_email;
146
		global $cookiename, $db_type, $db_server, $db_port, $db_name, $db_user, $db_passwd;
147
		global $ssi_db_user, $ssi_db_passwd, $db_prefix, $db_persist, $db_error_send, $cache_accelerator;
148
		global $cache_uid, $cache_password, $cache_enable, $cache_memcached, $db_show_debug, $url_format;
149
		global $cachedir, $boarddir, $sourcedir, $extdir, $languagedir;
150
151
		// Where the Settings.php file is located
152
		$settings_loc = __DIR__ . '/Settings.php';
153
154
		// First thing: if the installation dir exists, just send anybody there
155
		// The IGNORE_INSTALL_DIR constant is for developers only. Do not add it on production sites
156
		if (file_exists('install') && (file_exists('install/install.php') || file_exists('install/upgrade.php')))
157
		{
158
			if (file_exists($settings_loc))
159
			{
160
				require_once($settings_loc);
161
			}
162
163
			if (!defined('IGNORE_INSTALL_DIR'))
164
			{
165
				if (file_exists($settings_loc) && empty($_SESSION['installing']))
166
				{
167
					$redirec_file = 'upgrade.php';
168
				}
169
				else
170
				{
171
					$redirec_file = 'install.php';
172
				}
173
174
				$version_running = str_replace('ElkArte ', '', FORUM_VERSION);
175
				$proto = 'http' . (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '');
176
				$port = empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] === '80' ? '' : ':' . $_SERVER['SERVER_PORT'];
177
				$host = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . $port : $_SERVER['HTTP_HOST'];
178
				$path = strtr(dirname($_SERVER['PHP_SELF']), '\\', '/') == '/' ? '' : strtr(dirname($_SERVER['PHP_SELF']), '\\', '/');
179
180
				// Too early to use Headers class etc.
181
				header('Location:' . $proto . '://' . $host . $path . '/install/' . $redirec_file . '?v=' . $version_running);
182
				die();
183
			}
184
		}
185
		else
186
		{
187
			require_once($settings_loc);
188
		}
189
	}
190
191
	/**
192
	 * Validate the paths set in Settings.php, correct as needed and move
193
	 * them to constants.
194
	 */
195
	private function validatePaths()
196
	{
197
		global $boarddir, $sourcedir, $cachedir, $extdir, $languagedir;
198
199
		// Make sure the paths are correct... at least try to fix them.
200
		if (!file_exists($boarddir) && file_exists(__DIR__ . '/agreement.txt'))
201
		{
202
			$boarddir = __DIR__;
203
		}
204
205
		if (!file_exists($sourcedir . '/SiteDispatcher.class.php') && file_exists($boarddir . '/sources'))
206
		{
207
			$sourcedir = $boarddir . '/sources';
208
		}
209
210
		// Check that directories which didn't exist in past releases are initialized.
211
		if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
212
		{
213
			$cachedir = $boarddir . '/cache';
214
		}
215
216
		if ((empty($extdir) || !file_exists($extdir)) && file_exists($sourcedir . '/ext'))
217
		{
218
			$extdir = $sourcedir . '/ext';
219
		}
220
221
		if ((empty($languagedir) || !file_exists($languagedir)) && file_exists($sourcedir . '/Languages/Index'))
222
		{
223
			$languagedir = $sourcedir . '/ElkArte/Languages';
224
		}
225
226
		// Time to forget about variables and go with constants!
227
		define('BOARDDIR', $boarddir);
228
		define('CACHEDIR', $cachedir);
229
		define('EXTDIR', $extdir);
230
		define('LANGUAGEDIR', $languagedir);
231
		define('SOURCEDIR', $sourcedir);
232
		define('ADMINDIR', $sourcedir . '/ElkArte/AdminController');
233
		define('CONTROLLERDIR', $sourcedir . '/ElkArte/Controller');
234
		define('SUBSDIR', $sourcedir . '/subs');
235
		define('ADDONSDIR', $boarddir . '/addons');
236
		unset($boarddir, $cachedir, $sourcedir, $languagedir, $extdir);
237
	}
238
239
	/**
240
	 * We require access to several important files, so load them upfront
241
	 */
242
	private function loadDependants()
243
	{
244
		// Files we cannot live without.
245
		require_once(SOURCEDIR . '/QueryString.php');
246
		require_once(SOURCEDIR . '/Session.php');
247
		require_once(SOURCEDIR . '/Subs.php');
248
		require_once(SOURCEDIR . '/Logging.php');
249
		require_once(SOURCEDIR . '/Load.php');
250
		require_once(SOURCEDIR . '/Security.php');
251
		require_once(SUBSDIR . '/Cache.subs.php');
252
	}
253
254
	/**
255
	 * The autoloader will take care most requests for files
256
	 */
257
	private function loadAutoloader()
258
	{
259
		require_once(EXTDIR . '/ClassLoader.php');
260
261
		$loader = new ClassLoader();
262
		$loader->setPsr4('ElkArte\\', SOURCEDIR . '/ElkArte');
263
		$loader->setPsr4('BBC\\', SOURCEDIR . '/ElkArte/BBC');
264
		$loader->register();
265
	}
266
267
	/**
268
	 * Check if we are in maintance mode, if so end here.
269
	 */
270
	private function checkMaintance()
271
	{
272
		global $maintenance, $ssi_maintenance_off;
273
274
		// Don't do john didley if the forum's been shut down completely.
275
		if (!empty($maintenance) && $maintenance == 2 && (!isset($ssi_maintenance_off) || $ssi_maintenance_off !== true))
276
		{
277
			Errors::instance()->display_maintenance_message();
278
		}
279
	}
280
281
	/**
282
	 * If you like lots of debug information in error messages and below the footer
283
	 * then set $db_show_debug to true in settings.  Don't do this on a production site.
284
	 */
285
	private function setDebug()
286
	{
287
		global $db_show_debug;
288
289
		// Show lots of debug information below the page, not for production sites
290
		if ($db_show_debug === true)
291
		{
292
			Debug::instance()->rusage('start', $this->rusage_start);
293
		}
294
	}
295
296
	/**
297
	 * Time to see what has been requested, by whom and dispatch it to the proper handler
298
	 */
299
	private function bringUp()
300
	{
301
		global $context;
302
303
		// Initiate the database connection and define some database functions to use.
304
		loadDatabase();
305
306
		// Let's set up our shiny new hooks handler.
307
		Hooks::init(database(), Debug::instance());
308
309
		// It's time for settings loaded from the database.
310
		reloadSettings();
311
312
		// Clean the request.
313
		cleanRequest();
314
315
		// Make sure we have the list of members for populating it
316
		MembersList::init(database(), Cache::instance(), ParserWrapper::instance());
317
318
		// Our good ole' contextual array, which will hold everything
319
		if (empty($context))
320
		{
321
			$context = array();
322
		}
323
	}
324
325
	/**
326
	 * If you are running SSI standalone, you need to call this function after bootstrap is
327
	 * initialized.
328
	 */
329
	public function ssi_main()
330
	{
331
		global $ssi_layers, $ssi_theme, $ssi_gzip, $ssi_ban, $ssi_guest_access;
332
		global $modSettings, $context, $board, $topic, $txt;
333
334
		// Check on any hacking attempts.
335
		$this->_validRequestCheck();
336
337
		// Gzip output? (because it must be boolean and true, this can't be hacked.)
338
		if (isset($ssi_gzip) && $ssi_gzip === true && detectServer()->outPutCompressionEnabled())
339
		{
340
			ob_start('ob_gzhandler');
341
		}
342
		else
343
		{
344
			$modSettings['enableCompressedOutput'] = '0';
345
		}
346
347
		// Primarily, this is to fix the URLs...
348
		ob_start('ob_sessrewrite');
349
350
		// Start the session... known to scramble SSI includes in cases...
351
		if (!headers_sent())
352
		{
353
			loadSession();
354
		}
355
		else
356
		{
357
			if (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))
358
			{
359
				// Make a stab at it, but ignore the E_WARNINGs generated because we can't send headers.
360
				$temp = error_reporting(error_reporting() & !E_WARNING);
361
				loadSession();
362
				error_reporting($temp);
363
			}
364
365
			if (!isset($_SESSION['session_value']))
366
			{
367
				$tokenizer = new TokenHash();
368
				$_SESSION['session_value'] = $tokenizer->generate_hash(32, session_id());
369
				$_SESSION['session_var'] = substr(preg_replace('~^\d+~', '', $tokenizer->generate_hash(16, session_id())), 0, rand(7, 12));
370
			}
371
372
			// This is here only to avoid session errors in PHP7
373
			// microtime effectively forces the replacing of the session in the db each
374
			// time the page is loaded
375
			$_SESSION['mictrotime'] = microtime();
376
		}
377
378
		// Get rid of $board and $topic... do stuff loadBoard would do.
379
		unset($board, $topic);
380
		$context['linktree'] = array();
381
382
		// Load the user and their cookie, as well as their settings.
383
		User::load(true);
384
		$context['user']['is_mod'] = User::$info->is_mod ?? false;
385
386
		// Load the current user's permissions....
387
		loadPermissions();
388
389
		// Load the current or SSI theme. (just use $ssi_theme = id_theme;)
390
		new ThemeLoader(isset($ssi_theme) ? (int) $ssi_theme : 0);
391
392
		// Load BadBehavior functions, but not when running from CLI
393
		if (!defined('STDIN'))
394
		{
395
			// Do our BadBehavior checking
396
			if (runBadBehavior())
397
			{
398
				// 403 and gone
399
				Errors::instance()->display_403_error(true);
400
			}
401
		}
402
403
		// Take care of any banning that needs to be done.
404
		if (isset($_REQUEST['ssi_ban']) || (isset($ssi_ban) && $ssi_ban === true))
405
		{
406
			is_not_banned();
407
		}
408
409
		// Do we allow guests in here?
410
		if (empty($ssi_guest_access) && empty($modSettings['allow_guestAccess']) && User::$info->is_guest && basename($_SERVER['PHP_SELF']) !== 'SSI.php')
411
		{
412
			$controller = new Auth(new EventManager());
413
			$controller->setUser(User::$info);
414
			$controller->action_kickguest();
415
			obExit(null, true);
416
		}
417
418
		if (!empty($modSettings['front_page']) && class_exists($modSettings['front_page'])
419
			&& in_array('frontPageHook', get_class_methods($modSettings['front_page'])))
420
		{
421
			$modSettings['default_forum_action'] = ['action' => 'forum'];
422
		}
423
		else
424
		{
425
			$modSettings['default_forum_action'] = [];
426
		}
427
428
		// Load the stuff like the menu bar, etc.
429
		if (isset($ssi_layers))
430
		{
431
			$template_layers = theme()->getLayers();
432
			$template_layers->removeAll();
433
			foreach ($ssi_layers as $layer)
434
			{
435
				$template_layers->addBegin($layer);
436
			}
437
			template_header();
438
		}
439
		else
440
		{
441
			setupThemeContext();
442
		}
443
444
		// We need to set up user agent, and make more checks on the request
445
		$req = request();
446
447
		// Make sure they didn't muss around with the settings... but only if it's not cli.
448
		if (isset($_SERVER['REMOTE_ADDR']) && session_id() === '')
449
		{
450
			trigger_error($txt['ssi_session_broken'], E_USER_NOTICE);
451
		}
452
453
		// Without visiting the forum this session variable might not be set on submit.
454
		if (!isset($_SESSION['USER_AGENT']) && (!isset($_GET['ssi_function']) || $_GET['ssi_function'] !== 'pollVote'))
455
		{
456
			$_SESSION['USER_AGENT'] = $req->user_agent();
457
		}
458
	}
459
460
	/**
461
	 * Used to ensure SSI requests are valid and not a probing attempt
462
	 */
463
	private function _validRequestCheck()
464
	{
465
		global $ssi_theme, $ssi_layers;
466
467
		// Check on any hacking attempts.
468
		if (
469
			isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS'])
470
			|| isset($_REQUEST['ssi_theme']) && (int) $_REQUEST['ssi_theme'] == (int) $ssi_theme
471
			|| isset($_COOKIE['ssi_theme']) && (int) $_COOKIE['ssi_theme'] == (int) $ssi_theme
472
			|| isset($_REQUEST['ssi_layers'], $ssi_layers) && $_REQUEST['ssi_layers'] == $ssi_layers
473
			|| isset($_REQUEST['context']))
474
		{
475
			die('No access...');
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
476
		}
477
	}
478
}
479