Passed
Push — development ( b773b4...471a27 )
by Spuds
01:20 queued 21s
created

Bootstrap::handleNoLockInstallState()   C

Complexity

Conditions 13
Paths 88

Size

Total Lines 39
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 20
c 1
b 0
f 0
nc 88
nop 1
dl 0
loc 39
rs 6.6166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
Bug introduced by
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\Themes\ThemeLoader;
28
use ElkArte\User;
29
30
/**
31
 * Class Bootstrap
32
 *
33
 * Takes care of the initial loading and feeding of Elkarte from
34
 * either SSI or Index
35
 */
36
class Bootstrap
37
{
38
	/** @var array What is returned by the function getrusage. */
39
	protected $rusage_start = [];
40
41
	/**
42
	 * Bootstrap constructor.
43
	 *
44
	 * @param bool $standalone
45
	 *  - True to boot outside elkarte
46
	 *  - False to bootstrap the main elkarte site.
47
	 */
48
	public function __construct($standalone = true)
49
	{
50
		// Bootstrap only once.
51
		if (!defined('ELKBOOT'))
52
		{
53
			// We're going to set a few globals
54
			global $time_start, $ssi_error_reporting, $db_show_debug;
55
56
			// Your on the clock
57
			$time_start = microtime(true);
58
59
			// Unless settings.php tells us otherwise
60
			$db_show_debug = false;
61
62
			// Report errors but not depreciated ones
63
			$ssi_error_reporting = error_reporting(E_ALL & ~E_DEPRECATED);
64
65
			// Get the things needed for ALL modes
66
			$this->bringUpBasics();
67
68
			// Going to run from the side entrance and not directly from inside elkarte
69
			if ($standalone)
70
			{
71
				$this->ssi_main();
72
			}
73
		}
74
	}
75
76
	/**
77
	 * Calls various initialization functions in the necessary order
78
	 */
79
	public function bringUpBasics()
80
	{
81
		$this->setConstants();
82
		$this->setRusage();
83
		$this->clearGlobals();
84
		$this->loadSettingsFile();
85
		$this->validatePaths();
86
		$this->loadDependants();
87
		$this->loadAutoloader();
88
		$this->checkMaintance();
89
		$this->setDebug();
90
		$this->bringUp();
91
	}
92
93
	/**
94
	 * Set the core constants, you know the ones we often forget to
95
	 * update on new releases.
96
	 */
97
	private function setConstants()
98
	{
99
		// First things first, but not necessarily in that order.
100
		if (!defined('ELK'))
101
		{
102
			define('ELK', '1');
103
		}
104
105
		define('ELKBOOT', '1');
106
107
		// The software version
108
		define('FORUM_VERSION', 'ElkArte 2.0 dev');
109
110
		// Shortcut for the browser cache stale
111
		define('CACHE_STALE', '?20dev');
112
	}
113
114
	/**
115
	 * Get initial resource usage
116
	 */
117
	private function setRusage()
118
	{
119
		$this->rusage_start = getrusage();
120
	}
121
122
	/**
123
	 * If they glo, they need to be cleaned.
124
	 */
125
	private function clearGlobals()
126
	{
127
		// We don't need no globals. (a bug in "old" versions of PHP)
128
		foreach (['db_character_set', 'cachedir'] as $variable)
129
		{
130
			if (isset($GLOBALS[$variable]))
131
			{
132
				unset($GLOBALS[$variable], $GLOBALS[$variable]);
133
			}
134
		}
135
	}
136
137
	/**
138
	 * Loads the Settings.php values into the global space
139
	 */
140
	private function loadSettingsFile()
141
	{
142
		// All those wonderful things found in settings
143
		global $maintenance, $mtitle, $msubject, $mmessage, $mbname, $language, $boardurl, $webmaster_email;
144
		global $cookiename, $db_type, $db_server, $db_port, $db_name, $db_user, $db_passwd;
145
		global $ssi_db_user, $ssi_db_passwd, $db_prefix, $db_persist, $db_error_send;
146
		global $cache_uid, $cache_password, $cache_enable, $cache_servers, $cache_accelerator;
147
		global $db_show_debug, $url_format, $cachedir, $boarddir, $sourcedir, $extdir, $languagedir;
148
149
		// Where the Settings.php file is located
150
		$settings_location = __DIR__ . '/Settings.php';
151
152
		// First thing: prefer the presence of a lock file to short-circuit any installer logic
153
		// The IGNORE_INSTALL_DIR constant is intended for development only.
154
		if (file_exists(__DIR__ . '/installed.lock'))
155
		{
156
			require_once($settings_location);
157
			return;
158
		}
159
160
		// No lock present: decide if we should redirect to install/upgrade or continue
161
		$this->handleNoLockInstallState($settings_location);
162
	}
163
164
	/**
165
	 * Decides installation vs upgrade vs a normal load when no lock file is present.
166
	 *
167
	 * Rules:
168
	 * - If Settings.php exists and $install_time is set (not 0/empty) and installer exists → redirect to upgrade.php
169
	 * - If Settings.php exists and $install_time is 0/empty and installer exists → redirect to install.php
170
	 * - If Settings.php does not exist and installer exists → redirect to install.php
171
	 * - Otherwise, just continue (and Settings.php will be loaded by caller if present)
172
	 *
173
	 * @param string $settings_location Absolute path to Settings.php
174
	 */
175
	private function handleNoLockInstallState($settings_location)
176
	{
177
		// Values defined in Settings
178
		global $maintenance, $mtitle, $msubject, $mmessage, $mbname, $language, $boardurl, $webmaster_email;
179
		global $cookiename, $db_type, $db_server, $db_port, $db_name, $db_user, $db_passwd;
180
		global $ssi_db_user, $ssi_db_passwd, $db_prefix, $db_persist, $db_error_send;
181
		global $cache_uid, $cache_password, $cache_enable, $cache_servers, $cache_accelerator;
182
		global $db_show_debug, $url_format, $cachedir, $boarddir, $sourcedir, $extdir, $languagedir;
183
184
		$hasInstallDir = is_dir(__DIR__ . '/install');
185
		$hasInstall = $hasInstallDir && file_exists(__DIR__ . '/install/install.php');
186
		$hasUpgrade = $hasInstallDir && file_exists(__DIR__ . '/install/upgrade.php');
187
		$hasSettings = file_exists($settings_location);
188
189
		if ($hasSettings)
190
		{
191
			// Load to get $install_time and set the globals
192
			require_once($settings_location);
193
		}
194
195
		// If we have an installer directory available, decide the proper entry
196
		if (!defined('IGNORE_INSTALL_DIR') && ($hasInstall || $hasUpgrade))
197
		{
198
			$redirect_file = 'install.php';
199
200
			if ($hasSettings)
201
			{
202
				// If install_time is non-empty and non-zero, prefer upgrade flow when available
203
				$isInstalled = !empty($install_time) && $install_time !== '0';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $install_time seems to never exist and therefore empty should always be true.
Loading history...
204
				if ($isInstalled && $hasUpgrade && empty($_SESSION['installing']))
205
				{
206
					$redirect_file = 'upgrade.php';
207
				}
208
			}
209
210
			// Build a safe, relative redirect to avoid host header issues
211
			$version_running = defined('FORUM_VERSION') ? str_replace('ElkArte ', '', FORUM_VERSION) : '';
212
			header('Location: install/' . $redirect_file . '?v=' . $version_running);
213
			die();
214
		}
215
216
		// No installer available, nothing to redirect to; simply return
217
	}
218
219
	/**
220
	 * Validate the paths set in Settings.php, correct as needed and move them to constants.
221
	 */
222
	private function validatePaths()
223
	{
224
		global $boarddir, $sourcedir, $cachedir, $extdir, $languagedir;
225
226
		// Make sure the paths are correct... at least try to fix them.
227
		if (!file_exists($boarddir) && file_exists(__DIR__ . '/bootstrap.php'))
228
		{
229
			$boarddir = __DIR__;
230
		}
231
232
		if (!file_exists($sourcedir . '/SiteDispatcher.class.php') && file_exists($boarddir . '/sources'))
233
		{
234
			$sourcedir = $boarddir . '/sources';
235
		}
236
237
		// Check that directories which didn't exist in past releases are initialized.
238
		if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
239
		{
240
			$cachedir = $boarddir . '/cache';
241
		}
242
243
		if ((empty($extdir) || !file_exists($extdir)) && file_exists($sourcedir . '/ext'))
244
		{
245
			$extdir = $sourcedir . '/ext';
246
		}
247
248
		if ((empty($languagedir) || !file_exists($languagedir)) && file_exists($sourcedir . '/Languages/Index'))
249
		{
250
			$languagedir = $sourcedir . '/ElkArte/Languages';
251
		}
252
253
		// Time to forget about variables and go with constants!
254
		define('BOARDDIR', $boarddir);
255
		define('CACHEDIR', $cachedir);
256
		define('EXTDIR', $extdir);
257
		define('LANGUAGEDIR', $languagedir);
258
		define('SOURCEDIR', $sourcedir);
259
		define('ADMINDIR', $sourcedir . '/ElkArte/AdminController');
260
		define('CONTROLLERDIR', $sourcedir . '/ElkArte/Controller');
261
		define('SUBSDIR', $sourcedir . '/subs');
262
		define('ADDONSDIR', $boarddir . '/Addons');
263
		define('ELKARTEDIR', $sourcedir . '/ElkArte');
264
		unset($boarddir, $cachedir, $sourcedir, $languagedir, $extdir);
265
	}
266
267
	/**
268
	 * We require access to several important files, so load them upfront
269
	 */
270
	private function loadDependants()
271
	{
272
		// Files we cannot live without.
273
		require_once(SOURCEDIR . '/QueryString.php');
274
		require_once(SOURCEDIR . '/Session.php');
275
		require_once(SOURCEDIR . '/Subs.php');
276
		require_once(SOURCEDIR . '/Logging.php');
277
		require_once(SOURCEDIR . '/Load.php');
278
		require_once(SOURCEDIR . '/Security.php');
279
		require_once(SUBSDIR . '/Cache.subs.php');
280
	}
281
282
	/**
283
	 * The autoloader will take care of most requests for files
284
	 */
285
	private function loadAutoloader()
286
	{
287
		require_once(EXTDIR . '/ClassLoader.php');
288
289
		$loader = new ClassLoader();
290
		$loader->setPsr4('ElkArte\\', SOURCEDIR . '/ElkArte');
0 ignored issues
show
Bug introduced by
SOURCEDIR . '/ElkArte' of type string is incompatible with the type ElkArte\ext\Composer\Autoload\list expected by parameter $paths of ElkArte\ext\Composer\Aut...\ClassLoader::setPsr4(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

290
		$loader->setPsr4('ElkArte\\', /** @scrutinizer ignore-type */ SOURCEDIR . '/ElkArte');
Loading history...
291
		$loader->setPsr4('BBC\\', SOURCEDIR . '/ElkArte/BBC');
292
		$loader->setPsr4('Addons\\', BOARDDIR . '/Addons');
293
		$loader->register();
294
	}
295
296
	/**
297
	 * Check if we are in maintenance mode, if so end here.
298
	 */
299
	private function checkMaintance()
300
	{
301
		global $maintenance, $ssi_maintenance_off;
302
303
		// Don't do john didley if the forum's been shut down completely.
304
		if (empty($maintenance))
305
		{
306
			return;
307
		}
308
309
		if ((int) $maintenance !== 2)
310
		{
311
			return;
312
		}
313
314
		if (isset($ssi_maintenance_off) && $ssi_maintenance_off === true)
315
		{
316
			return;
317
		}
318
319
		Errors::instance()->display_maintenance_message();
320
	}
321
322
	/**
323
	 * If you like lots of debug information in error messages and below the footer
324
	 * then set $db_show_debug to true in settings.  Don't do this on a production site.
325
	 */
326
	private function setDebug()
327
	{
328
		global $db_show_debug, $ssi_error_reporting;
329
330
		// Show lots of debug information below the page, not for production sites
331
		if ($db_show_debug === true)
332
		{
333
			Debug::instance()->rusage('start', $this->rusage_start);
334
			$ssi_error_reporting = error_reporting(E_ALL | E_STRICT & ~8192);
335
		}
336
	}
337
338
	/**
339
	 * Time to see what has been requested, by whom and dispatch it to the proper handler
340
	 */
341
	private function bringUp()
342
	{
343
		global $context;
344
345
		// Initiate the database connection and define some database functions to use.
346
		loadDatabase();
347
348
		// Let's set up our shiny new hooks handler.
349
		Hooks::init(database(), Debug::instance());
350
351
		// It's time for settings loaded from the database.
352
		reloadSettings();
353
354
		// Clean the request.
355
		cleanRequest();
356
357
		// Make sure we have the list of members for populating it
358
		MembersList::init(database(), Cache::instance(), ParserWrapper::instance());
359
360
		// Our good ole' contextual array, which will hold everything
361
		if (empty($context))
362
		{
363
			$context = [];
364
		}
365
	}
366
367
	/**
368
	 * If you are running SSI standalone, you need to call this function after bootstrap is
369
	 * initialized.
370
	 */
371
	public function ssi_main()
372
	{
373
		global $ssi_layers, $ssi_theme, $ssi_gzip, $ssi_ban, $ssi_guest_access;
374
		global $modSettings, $context, $board, $topic, $txt;
375
376
		// Check on any hacking attempts.
377
		$this->_validRequestCheck();
378
379
		// Gzip output? (because it must be boolean and true, this can't be hacked.)
380
		if (isset($ssi_gzip) && $ssi_gzip === true && detectServer()->outPutCompressionEnabled())
381
		{
382
			ob_start('ob_gzhandler');
383
		}
384
		else
385
		{
386
			$modSettings['enableCompressedOutput'] = '0';
387
		}
388
389
		// Primarily, this is to fix the URLs...
390
		ob_start('ob_sessrewrite');
391
392
		// Start the session... known to scramble SSI includes in cases...
393
		if (!headers_sent())
394
		{
395
			loadSession();
396
		}
397
		else
398
		{
399
			if (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))
400
			{
401
				// Make a stab at it, but ignore the E_WARNINGs generated because we can't send headers.
402
				$temp = error_reporting(error_reporting() & !E_WARNING);
403
				loadSession();
404
				error_reporting($temp);
405
			}
406
407
			if (!isset($_SESSION['session_value']))
408
			{
409
				$tokenizer = new TokenHash();
410
				$_SESSION['session_value'] = $tokenizer->generate_hash(32, session_id());
411
				$_SESSION['session_var'] = substr(preg_replace('~^\d+~', '', $tokenizer->generate_hash(16, session_id())), 0, rand(7, 12));
412
			}
413
414
			// This is here only to avoid session errors in PHP7
415
			// microtime effectively forces the replacing of the session in the db each
416
			// time the page is loaded
417
			$_SESSION['mictrotime'] = microtime();
418
		}
419
420
		// Get rid of $board and $topic... do stuff loadBoard would do.
421
		unset($board, $topic);
422
		$context['breadcrumbs'] = [];
423
424
		// Load the user and their cookie, as well as their settings.
425
		User::load(true);
426
		$context['user']['is_mod'] = User::$info->is_mod ?? false;
427
428
		// Load the current user's permissions....
429
		loadPermissions();
430
431
		// Load the current or SSI theme. (just use $ssi_theme = id_theme;)
432
		new ThemeLoader(isset($ssi_theme) ? (int) $ssi_theme : 0);
433
434
		// Load BadBehavior functions, but not when running from CLI
435
		if (!defined('STDIN') && runBadBehavior())
436
		{
437
			// 403 and gone
438
			Errors::instance()->display_403_error(true);
439
		}
440
441
		// Take care of any banning that needs to be done.
442
		if (isset($_REQUEST['ssi_ban']) || (isset($ssi_ban) && $ssi_ban === true))
443
		{
444
			is_not_banned();
445
		}
446
447
		// Do we allow guests in here?
448
		if (empty($ssi_guest_access) && empty($modSettings['allow_guestAccess']) && User::$info->is_guest && basename($_SERVER['PHP_SELF']) !== 'SSI.php')
449
		{
450
			$controller = new Auth(new EventManager());
451
			$controller->setUser(User::$info);
452
			$controller->action_kickguest();
453
			obExit(null, true);
454
		}
455
456
		if (!empty($modSettings['front_page']) && class_exists($modSettings['front_page'])
457
			&& in_array('frontPageHook', get_class_methods($modSettings['front_page'])))
458
		{
459
			$modSettings['default_forum_action'] = ['action' => 'forum'];
460
		}
461
		else
462
		{
463
			$modSettings['default_forum_action'] = [];
464
		}
465
466
		// Load the stuff like the menu bar, etc.
467
		if (isset($ssi_layers))
468
		{
469
			$template_layers = theme()->getLayers();
470
			$template_layers->removeAll();
471
			foreach ($ssi_layers as $layer)
472
			{
473
				$template_layers->addBegin($layer);
474
			}
475
476
			template_header();
477
		}
478
		else
479
		{
480
			setupThemeContext();
481
		}
482
483
		// We need to set up user agent, and make more checks on the request
484
		$req = Request::instance();
485
486
		// Make sure they didn't muss around with the settings... but only if it's not cli.
487
		if (isset($_SERVER['REMOTE_ADDR']) && session_id() === '')
488
		{
489
			trigger_error($txt['ssi_session_broken']);
490
		}
491
492
		// Without visiting the forum this session variable might not be set on submit.
493
		if (isset($_SESSION['USER_AGENT']))
494
		{
495
			return;
496
		}
497
498
		if (isset($_GET['ssi_function']) && $_GET['ssi_function'] === 'pollVote')
499
		{
500
			return;
501
		}
502
503
		$_SESSION['USER_AGENT'] = $req->user_agent();
504
	}
505
506
	/**
507
	 * Used to ensure SSI requests are valid and not a probing attempt
508
	 */
509
	private function _validRequestCheck()
510
	{
511
		global $ssi_theme, $ssi_layers;
512
513
		// Check on any hacking attempts.
514
		if (
515
			isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS'])
516
			|| (isset($_REQUEST['ssi_theme']) && ((int) $_REQUEST['ssi_theme'] === (int) $ssi_theme))
517
			|| (isset($_COOKIE['ssi_theme']) && ((int) $_COOKIE['ssi_theme'] === (int) $ssi_theme))
518
			|| (isset($_REQUEST['ssi_layers'], $ssi_layers) && ($_REQUEST['ssi_layers'] == $ssi_layers))
519
			|| isset($_REQUEST['context']))
520
		{
521
			die('No access...');
0 ignored issues
show
Best Practice introduced by
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...
522
		}
523
	}
524
}
525