Issues (1686)

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