Issues (2473)

Branch: master

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

engine/classes/ElggInstaller.php (10 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
 * Elgg Installer.
5
 * Controller for installing Elgg. Supports both web-based on CLI installation.
6
 *
7
 * This controller steps the user through the install process. The method for
8
 * each step handles both the GET and POST requests. There is no XSS/CSRF protection
9
 * on the POST processing since the installer is only run once by the administrator.
10
 *
11
 * The installation process can be resumed by hitting the first page. The installer
12
 * will try to figure out where to pick up again.
13
 *
14
 * All the logic for the installation process is in this class, but it depends on
15
 * the core libraries. To do this, we selectively load a subset of the core libraries
16
 * for the first few steps and then load the entire engine once the database and
17
 * site settings are configured. In addition, this controller does its own session
18
 * handling until the database is setup.
19
 *
20
 * There is an aborted attempt in the code at creating the data directory for
21
 * users as a subdirectory of Elgg's root. The idea was to protect this directory
22
 * through a .htaccess file. The problem is that a malicious user can upload a
23
 * .htaccess of his own that overrides the protection for his user directory. The
24
 * best solution is server level configuration that turns off AllowOverride for the
25
 * data directory. See ticket #3453 for discussion on this.
26
 *
27
 * @package    Elgg.Core
28
 * @subpackage Installer
29
 */
30
class ElggInstaller {
31
	
32
	protected $steps = array(
33
		'welcome',
34
		'requirements',
35
		'database',
36
		'settings',
37
		'admin',
38
		'complete',
39
		);
40
41
	protected $status = array(
42
		'config' => FALSE,
43
		'database' => FALSE,
44
		'settings' => FALSE,
45
		'admin' => FALSE,
46
	);
47
48
	protected $isAction = FALSE;
49
50
	protected $autoLogin = TRUE;
51
52
	/**
53
	 * Global Elgg configuration
54
	 * 
55
	 * @var \stdClass
56
	 */
57
	private $CONFIG;
58
59
	/**
60
	 * Constructor bootstraps the Elgg engine
61
	 */
62
	public function __construct() {
63
		global $CONFIG;
64
		if (!isset($CONFIG)) {
65
			$CONFIG = new stdClass;
66
		}
67
68
		$this->CONFIG = $CONFIG;
69
70
		$this->isAction = isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST';
71
72
		$this->bootstrapConfig();
73
74
		$this->bootstrapEngine();
75
76
		_elgg_services()->setValue('session', \ElggSession::getMock());
77
78
		elgg_set_viewtype('installation');
79
80
		set_error_handler('_elgg_php_error_handler');
81
		set_exception_handler('_elgg_php_exception_handler');
82
83
		_elgg_services()->translator->registerTranslations("{$this->getElggRoot()}/install/languages/", TRUE);
84
	}
85
	
86
	/**
87
	 * @return string The absolute path to Elgg's root directory
88
	 */
89
	private function getElggRoot() {
90
		return dirname(dirname(__DIR__));
91
	}
92
93
	/**
94
	 * Dispatches a request to one of the step controllers
95
	 *
96
	 * @param string $step The installation step to run
97
	 *
98
	 * @return void
99
	 * @throws InstallationException
100
	 */
101
	public function run($step) {
102
		global $CONFIG;
103
104
		// language needs to be set before the first call to elgg_echo()
105
		$CONFIG->language = 'en';
106
107
		// check if this is a URL rewrite test coming in
108
		$this->processRewriteTest();
109
110
		if (!in_array($step, $this->getSteps())) {
111
			$msg = _elgg_services()->translator->translate('InstallationException:UnknownStep', array($step));
112
			throw new InstallationException($msg);
113
		}
114
115
		$this->setInstallStatus();
116
117
		$this->checkInstallCompletion($step);
118
119
		// check if this is an install being resumed
120
		$this->resumeInstall($step);
121
122
		$this->finishBootstraping($step);
123
124
		$params = $this->getPostVariables();
125
		$this->$step($params);
126
	}
127
128
	/**
129
	 * Set the auto login flag
130
	 *
131
	 * @param bool $flag Auto login
132
	 *
133
	 * @return void
134
	 */
135
	public function setAutoLogin($flag) {
136
		$this->autoLogin = (bool) $flag;
137
	}
138
139
	/**
140
	 * A batch install of Elgg
141
	 *
142
	 * All required parameters must be passed in as an associative array. See
143
	 * $requiredParams for a list of them. This creates the necessary files,
144
	 * loads the database, configures the site settings, and creates the admin
145
	 * account. If it fails, an exception is thrown. It does not check any of
146
	 * the requirements as the multiple step web installer does.
147
	 *
148
	 * If the settings.php file exists, it will use that rather than the parameters
149
	 * passed to this function.
150
	 *
151
	 * @param array $params         Array of key value pairs
152
	 * @param bool  $createHtaccess Should .htaccess be created
153
	 *
154
	 * @return void
155
	 * @throws InstallationException
156
	 */
157
	public function batchInstall(array $params, $createHtaccess = FALSE) {
158
		
159
160
		restore_error_handler();
161
		restore_exception_handler();
162
163
		$defaults = array(
164
			'dbhost' => 'localhost',
165
			'dbprefix' => 'elgg_',
166
			'language' => 'en',
167
			'siteaccess' => ACCESS_PUBLIC,
168
		);
169
		$params = array_merge($defaults, $params);
170
171
		$requiredParams = array(
172
			'dbuser',
173
			'dbpassword',
174
			'dbname',
175
			'sitename',
176
			'wwwroot',
177
			'dataroot',
178
			'displayname',
179
			'email',
180
			'username',
181
			'password',
182
		);
183
		foreach ($requiredParams as $key) {
184
			if (empty($params[$key])) {
185
				$msg = _elgg_services()->translator->translate('install:error:requiredfield', array($key));
186
				throw new InstallationException($msg);
187
			}
188
		}
189
190
		// password is passed in once
191
		$params['password1'] = $params['password2'] = $params['password'];
192
193
		if ($createHtaccess) {
194
			$rewriteTester = new ElggRewriteTester();
195
			if (!$rewriteTester->createHtaccess($params['wwwroot'], $this->CONFIG->path)) {
196
				throw new InstallationException(_elgg_services()->translator->translate('install:error:htaccess'));
197
			}
198
		}
199
200
		$this->setInstallStatus();
201
202 View Code Duplication
		if (!$this->status['config']) {
203
			if (!$this->createSettingsFile($params)) {
204
				throw new InstallationException(_elgg_services()->translator->translate('install:error:settings'));
205
			}
206
		}
207
208
		if (!$this->connectToDatabase()) {
209
			throw new InstallationException(_elgg_services()->translator->translate('install:error:databasesettings'));
210
		}
211
212 View Code Duplication
		if (!$this->status['database']) {
213
			if (!$this->installDatabase()) {
214
				throw new InstallationException(_elgg_services()->translator->translate('install:error:cannotloadtables'));
215
			}
216
		}
217
218
		// load remaining core libraries
219
		$this->finishBootstraping('settings');
220
221
		if (!$this->saveSiteSettings($params)) {
222
			throw new InstallationException(_elgg_services()->translator->translate('install:error:savesitesettings'));
223
		}
224
225
		if (!$this->createAdminAccount($params)) {
226
			throw new InstallationException(_elgg_services()->translator->translate('install:admin:cannot_create'));
227
		}
228
	}
229
230
	/**
231
	 * Renders the data passed by a controller
232
	 *
233
	 * @param string $step The current step
234
	 * @param array  $vars Array of vars to pass to the view
235
	 *
236
	 * @return void
237
	 */
238
	protected function render($step, $vars = array()) {
239
240
		$vars['next_step'] = $this->getNextStep($step);
241
242
		$title = _elgg_services()->translator->translate("install:$step");
243
		$body = elgg_view("install/pages/$step", $vars);
244
		echo elgg_view_page(
245
				$title,
246
				$body,
247
				'default',
248
				array(
249
					'step' => $step,
250
					'steps' => $this->getSteps(),
251
					)
252
				);
253
		exit;
254
	}
255
256
	/**
257
	 * Step controllers
258
	 */
259
260
	/**
261
	 * Welcome controller
262
	 *
263
	 * @param array $vars Not used
264
	 *
265
	 * @return void
266
	 */
267
	protected function welcome($vars) {
268
		$this->render('welcome');
269
	}
270
271
	/**
272
	 * Requirements controller
273
	 *
274
	 * Checks version of php, libraries, permissions, and rewrite rules
275
	 *
276
	 * @param array $vars Vars
277
	 *
278
	 * @return void
279
	 */
280
	protected function requirements($vars) {
281
282
		$report = array();
283
284
		// check PHP parameters and libraries
285
		$this->checkPHP($report);
286
287
		// check URL rewriting
288
		$this->checkRewriteRules($report);
289
290
		// check for existence of settings file
291
		if ($this->checkSettingsFile($report) != TRUE) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
292
			// no file, so check permissions on engine directory
293
			$this->checkEngineDir($report);
294
		}
295
296
		// check the database later
297
		$report['database'] = array(array(
298
			'severity' => 'info',
299
			'message' => _elgg_services()->translator->translate('install:check:database')
300
		));
301
302
		// any failures?
303
		$numFailures = $this->countNumConditions($report, 'failure');
304
305
		// any warnings
306
		$numWarnings = $this->countNumConditions($report, 'warning');
307
308
309
		$params = array(
310
			'report' => $report,
311
			'num_failures' => $numFailures,
312
			'num_warnings' => $numWarnings,
313
		);
314
315
		$this->render('requirements', $params);
316
	}
317
318
	/**
319
	 * Database set up controller
320
	 *
321
	 * Creates the settings.php file and creates the database tables
322
	 *
323
	 * @param array $submissionVars Submitted form variables
324
	 *
325
	 * @return void
326
	 */
327
	protected function database($submissionVars) {
328
329
		$formVars = array(
330
			'dbuser' => array(
331
				'type' => 'text',
332
				'value' => '',
333
				'required' => TRUE,
334
				),
335
			'dbpassword' => array(
336
				'type' => 'password',
337
				'value' => '',
338
				'required' => FALSE,
339
				),
340
			'dbname' => array(
341
				'type' => 'text',
342
				'value' => '',
343
				'required' => TRUE,
344
				),
345
			'dbhost' => array(
346
				'type' => 'text',
347
				'value' => 'localhost',
348
				'required' => TRUE,
349
				),
350
			'dbprefix' => array(
351
				'type' => 'text',
352
				'value' => 'elgg_',
353
				'required' => TRUE,
354
				),
355
		);
356
357
		if ($this->checkSettingsFile()) {
358
			// user manually created settings file so we fake out action test
359
			$this->isAction = TRUE;
360
		}
361
362
		if ($this->isAction) {
363
			do {
364
				// only create settings file if it doesn't exist
365
				if (!$this->checkSettingsFile()) {
366
					if (!$this->validateDatabaseVars($submissionVars, $formVars)) {
367
						// error so we break out of action and serve same page
368
						break;
369
					}
370
371
					if (!$this->createSettingsFile($submissionVars)) {
372
						break;
373
					}
374
				}
375
376
				// check db version and connect
377
				if (!$this->connectToDatabase()) {
378
					break;
379
				}
380
381
				if (!$this->installDatabase()) {
382
					break;
383
				}
384
385
				system_message(_elgg_services()->translator->translate('install:success:database'));
386
387
				$this->continueToNextStep('database');
388
			} while (FALSE);  // PHP doesn't support breaking out of if statements
389
		}
390
391
		$formVars = $this->makeFormSticky($formVars, $submissionVars);
392
393
		$params = array('variables' => $formVars,);
394
395
		if ($this->checkSettingsFile()) {
396
			// settings file exists and we're here so failed to create database
397
			$params['failure'] = TRUE;
398
		}
399
400
		$this->render('database', $params);
401
	}
402
403
	/**
404
	 * Site settings controller
405
	 *
406
	 * Sets the site name, URL, data directory, etc.
407
	 *
408
	 * @param array $submissionVars Submitted vars
409
	 *
410
	 * @return void
411
	 */
412
	protected function settings($submissionVars) {
413
		
414
415
		$formVars = array(
416
			'sitename' => array(
417
				'type' => 'text',
418
				'value' => 'My New Community',
419
				'required' => TRUE,
420
				),
421
			'siteemail' => array(
422
				'type' => 'email',
423
				'value' => '',
424
				'required' => FALSE,
425
				),
426
			'wwwroot' => array(
427
				'type' => 'url',
428
				'value' => _elgg_services()->config->getSiteUrl(),
429
				'required' => TRUE,
430
				),
431
			'dataroot' => array(
432
				'type' => 'text',
433
				'value' => '',
434
				'required' => TRUE,
435
				),
436
			'siteaccess' => array(
437
				'type' => 'access',
438
				'value' => ACCESS_PUBLIC,
439
				'required' => TRUE,
440
				),
441
		);
442
443
		// if Apache, we give user option of having Elgg create data directory
444
		//if (ElggRewriteTester::guessWebServer() == 'apache') {
445
		//	$formVars['dataroot']['type'] = 'combo';
446
		//	$this->CONFIG->translations['en']['install:settings:help:dataroot'] =
447
		//			$this->CONFIG->translations['en']['install:settings:help:dataroot:apache'];
448
		//}
449
450 View Code Duplication
		if ($this->isAction) {
451
			do {
452
				//if (!$this->createDataDirectory($submissionVars, $formVars)) {
453
				//	break;
454
				//}
455
456
				if (!$this->validateSettingsVars($submissionVars, $formVars)) {
457
					break;
458
				}
459
460
				if (!$this->saveSiteSettings($submissionVars)) {
461
					break;
462
				}
463
464
				system_message(_elgg_services()->translator->translate('install:success:settings'));
465
466
				$this->continueToNextStep('settings');
467
468
			} while (FALSE);  // PHP doesn't support breaking out of if statements
469
		}
470
471
		$formVars = $this->makeFormSticky($formVars, $submissionVars);
472
473
		$this->render('settings', array('variables' => $formVars));
474
	}
475
476
	/**
477
	 * Admin account controller
478
	 *
479
	 * Creates an admin user account
480
	 *
481
	 * @param array $submissionVars Submitted vars
482
	 *
483
	 * @return void
484
	 */
485
	protected function admin($submissionVars) {
486
		$formVars = array(
487
			'displayname' => array(
488
				'type' => 'text',
489
				'value' => '',
490
				'required' => TRUE,
491
				),
492
			'email' => array(
493
				'type' => 'email',
494
				'value' => '',
495
				'required' => TRUE,
496
				),
497
			'username' => array(
498
				'type' => 'text',
499
				'value' => '',
500
				'required' => TRUE,
501
				),
502
			'password1' => array(
503
				'type' => 'password',
504
				'value' => '',
505
				'required' => TRUE,
506
				'pattern' => '.{6,}',
507
				),
508
			'password2' => array(
509
				'type' => 'password',
510
				'value' => '',
511
				'required' => TRUE,
512
				),
513
		);
514
		
515 View Code Duplication
		if ($this->isAction) {
516
			do {
517
				if (!$this->validateAdminVars($submissionVars, $formVars)) {
518
					break;
519
				}
520
521
				if (!$this->createAdminAccount($submissionVars, $this->autoLogin)) {
522
					break;
523
				}
524
525
				system_message(_elgg_services()->translator->translate('install:success:admin'));
526
527
				$this->continueToNextStep('admin');
528
529
			} while (FALSE);  // PHP doesn't support breaking out of if statements
530
		}
531
532
		// bit of a hack to get the password help to show right number of characters
533
		
534
		$lang = _elgg_services()->translator->getCurrentLanguage();
535
		$this->CONFIG->translations[$lang]['install:admin:help:password1'] =
536
				sprintf($this->CONFIG->translations[$lang]['install:admin:help:password1'],
537
				$this->CONFIG->min_password_length);
538
539
		$formVars = $this->makeFormSticky($formVars, $submissionVars);
540
541
		$this->render('admin', array('variables' => $formVars));
542
	}
543
544
	/**
545
	 * Controller for last step
546
	 *
547
	 * @return void
548
	 */
549
	protected function complete() {
550
551
		$params = array();
552
		if ($this->autoLogin) {
553
			$params['destination'] = 'admin';
554
		} else {
555
			$params['destination'] = 'index.php';
556
		}
557
558
		$this->render('complete', $params);
559
	}
560
561
	/**
562
	 * Step management
563
	 */
564
565
	/**
566
	 * Get an array of steps
567
	 *
568
	 * @return array
569
	 */
570
	protected function getSteps() {
571
		return $this->steps;
572
	}
573
574
	/**
575
	 * Forwards the browser to the next step
576
	 *
577
	 * @param string $currentStep Current installation step
578
	 *
579
	 * @return void
580
	 */
581
	protected function continueToNextStep($currentStep) {
582
		$this->isAction = FALSE;
583
		forward($this->getNextStepUrl($currentStep));
584
	}
585
586
	/**
587
	 * Get the next step as a string
588
	 *
589
	 * @param string $currentStep Current installation step
590
	 *
591
	 * @return string
592
	 */
593
	protected function getNextStep($currentStep) {
594
		$index = 1 + array_search($currentStep, $this->steps);
595
		if (isset($this->steps[$index])) {
596
			return $this->steps[$index];
597
		} else {
598
			return null;
599
		}
600
	}
601
602
	/**
603
	 * Get the URL of the next step
604
	 *
605
	 * @param string $currentStep Current installation step
606
	 *
607
	 * @return string
608
	 */
609
	protected function getNextStepUrl($currentStep) {
610
		$nextStep = $this->getNextStep($currentStep);
611
		return _elgg_services()->config->getSiteUrl() . "install.php?step=$nextStep";
612
	}
613
614
	/**
615
	 * Check the different install steps for completion
616
	 *
617
	 * @return void
618
	 * @throws InstallationException
619
	 */
620
	protected function setInstallStatus() {
621
		
622
623
		if (!is_readable("{$this->CONFIG->path}engine/settings.php")) {
624
			return;
625
		}
626
627
		$this->loadSettingsFile();
628
629
		$this->status['config'] = TRUE;
630
631
		// must be able to connect to database to jump install steps
632
		$dbSettingsPass = $this->checkDatabaseSettings(
633
				$this->CONFIG->dbuser,
634
				$this->CONFIG->dbpass,
635
				$this->CONFIG->dbname,
636
				$this->CONFIG->dbhost
637
				);
638
		if ($dbSettingsPass == FALSE) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
639
			return;
640
		}
641
642
		if (!include_once("{$this->CONFIG->path}engine/lib/database.php")) {
643
			throw new InstallationException(_elgg_services()->translator->translate('InstallationException:MissingLibrary', array('database.php')));
644
		}
645
646
		// check that the config table has been created
647
		$query = "show tables";
648
		$result = _elgg_services()->db->getData($query);
649
		if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
650
			foreach ($result as $table) {
651
				$table = (array) $table;
652
				if (in_array("{$this->CONFIG->dbprefix}config", $table)) {
653
					$this->status['database'] = TRUE;
654
				}
655
			}
656
			if ($this->status['database'] == FALSE) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
657
				return;
658
			}
659
		} else {
660
			// no tables
661
			return;
662
		}
663
664
		// check that the config table has entries
665
		$query = "SELECT COUNT(*) AS total FROM {$this->CONFIG->dbprefix}config";
666
		$result = _elgg_services()->db->getData($query);
667
		if ($result && $result[0]->total > 0) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
668
			$this->status['settings'] = TRUE;
669
		} else {
670
			return;
671
		}
672
673
		// check that the users entity table has an entry
674
		$query = "SELECT COUNT(*) AS total FROM {$this->CONFIG->dbprefix}users_entity";
675
		$result = _elgg_services()->db->getData($query);
676
		if ($result && $result[0]->total > 0) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
677
			$this->status['admin'] = TRUE;
678
		} else {
679
			return;
680
		}
681
	}
682
683
	/**
684
	 * Security check to ensure the installer cannot be run after installation
685
	 * has finished. If this is detected, the viewer is sent to the front page.
686
	 *
687
	 * @param string $step Installation step to check against
688
	 *
689
	 * @return void
690
	 */
691
	protected function checkInstallCompletion($step) {
692
		if ($step != 'complete') {
693
			if (!in_array(FALSE, $this->status)) {
694
				// install complete but someone is trying to view an install page
695
				forward();
696
			}
697
		}
698
	}
699
700
	/**
701
	 * Check if this is a case of a install being resumed and figure
702
	 * out where to continue from. Returns the best guess on the step.
703
	 *
704
	 * @param string $step Installation step to resume from
705
	 *
706
	 * @return string
707
	 */
708
	protected function resumeInstall($step) {
709
		// only do a resume from the first step
710
		if ($step !== 'welcome') {
711
			return;
712
		}
713
714
		if ($this->status['database'] == FALSE) {
715
			return;
716
		}
717
718
		if ($this->status['settings'] == FALSE) {
719
			forward("install.php?step=settings");
720
		}
721
722
		if ($this->status['admin'] == FALSE) {
723
			forward("install.php?step=admin");
724
		}
725
726
		// everything appears to be set up
727
		forward("install.php?step=complete");
728
	}
729
730
	/**
731
	 * Bootstraping
732
	 */
733
734
	/**
735
	 * Load the essential libraries of the engine
736
	 *
737
	 * @return void
738
	 */
739
	protected function bootstrapEngine() {
740
		
741
742
		require_once $this->CONFIG->path . 'engine/load.php';
743
	}
744
745
	/**
746
	 * Load remaining engine libraries and complete bootstraping (see start.php)
747
	 *
748
	 * @param string $step Which step to boot strap for. Required because
749
	 *                     boot strapping is different until the DB is populated.
750
	 *
751
	 * @return void
752
	 * @throws InstallationException
753
	 */
754
	protected function finishBootstraping($step) {
755
756
		$dbIndex = array_search('database', $this->getSteps());
757
		$settingsIndex = array_search('settings', $this->getSteps());
758
		$adminIndex = array_search('admin', $this->getSteps());
759
		$completeIndex = array_search('complete', $this->getSteps());
760
		$stepIndex = array_search($step, $this->getSteps());
761
762
		// To log in the user, we need to use the Elgg core session handling.
763
		// Otherwise, use default php session handling
764
		$useElggSession = ($stepIndex == $adminIndex && $this->isAction) ||
765
				$stepIndex == $completeIndex;
766
		if (!$useElggSession) {
767
			session_name('Elgg_install');
768
			session_start();
769
			_elgg_services()->events->unregisterHandler('boot', 'system', 'session_init');
770
		}
771
772
		if ($stepIndex > $dbIndex) {
773
			// once the database has been created, load rest of engine
774
			
775
			$lib_dir = $this->CONFIG->path . 'engine/lib/';
776
777
			$this->loadSettingsFile();
778
779
			$lib_files = array(
780
				// these want to be loaded first apparently?
781
				'autoloader.php',
782
				'database.php',
783
				'actions.php',
784
785
				'admin.php',
786
				'annotations.php',
787
				'cron.php',
788
				'entities.php',
789
				'extender.php',
790
				'filestore.php',
791
				'group.php',
792
				'mb_wrapper.php',
793
				'memcache.php',
794
				'metadata.php',
795
				'metastrings.php',
796
				'navigation.php',
797
				'notification.php',
798
				'objects.php',
799
				'pagehandler.php',
800
				'pam.php',
801
				'plugins.php',
802
				'private_settings.php',
803
				'relationships.php',
804
				'river.php',
805
				'sites.php',
806
				'statistics.php',
807
				'tags.php',
808
				'user_settings.php',
809
				'users.php',
810
				'upgrade.php',
811
				'widgets.php',
812
				'deprecated-1.7.php',
813
				'deprecated-1.8.php',
814
				'deprecated-1.9.php',
815
			);
816
817
			foreach ($lib_files as $file) {
818
				$path = $lib_dir . $file;
819
				if (!include_once($path)) {
820
					throw new InstallationException('InstallationException:MissingLibrary', array($file));
821
				}
822
			}
823
824
			_elgg_services()->db->setupConnections();
825
			_elgg_services()->translator->registerTranslations("{$this->getElggRoot()}/languages/");
826
			$this->CONFIG->language = 'en';
827
828
			if ($stepIndex > $settingsIndex) {
829
				$this->CONFIG->site_guid = (int) _elgg_services()->datalist->get('default_site');
830
				$this->CONFIG->site_id = $this->CONFIG->site_guid;
831
				$this->CONFIG->site = get_entity($this->CONFIG->site_guid);
832
				$this->CONFIG->dataroot = _elgg_services()->datalist->get('dataroot');
833
				_elgg_session_boot();
834
			}
835
836
			_elgg_services()->events->trigger('init', 'system');
837
		}
838
	}
839
840
	/**
841
	 * Set up configuration variables
842
	 *
843
	 * @return void
844
	 */
845
	protected function bootstrapConfig() {
846
		$this->CONFIG->installer_running = true;
847
848
		$this->CONFIG->wwwroot = $this->getBaseUrl();
849
		$this->CONFIG->url = $this->CONFIG->wwwroot;
850
		$this->CONFIG->path = "{$this->getElggRoot()}/";
851
		$this->CONFIG->viewpath =	$this->CONFIG->path . 'views/';
852
		$this->CONFIG->pluginspath = $this->CONFIG->path . 'mod/';
853
		$this->CONFIG->context = array();
854
		$this->CONFIG->entity_types = array('group', 'object', 'site', 'user');
855
856
		// required by elgg_view_page()
857
		$this->CONFIG->sitename = '';
858
		$this->CONFIG->sitedescription = '';
859
860
		// required by Elgg\Config::get
861
		$this->CONFIG->site_guid = 1;
862
	}
863
	
864
	/**
865
	 * @return bool Whether the install process is encrypted.
866
	 */
867
	private function isHttps() {
868
		return (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") ||
869
			$_SERVER['SERVER_PORT'] == 443;
870
	}
871
872
	/**
873
	 * Get the best guess at the base URL
874
	 *
875
	 * @note Cannot use current_page_url() because it depends on $this->CONFIG->wwwroot
876
	 * @todo Should this be a core function?
877
	 *
878
	 * @return string
879
	 */
880
	protected function getBaseUrl() {
881
		$protocol = $this->isHttps() ? 'https' : 'http';
882
		
883
		if (isset($_SERVER["SERVER_PORT"])) {
884
			$port = ':' . $_SERVER["SERVER_PORT"];
885
		} else {
886
			$port = '';
887
		}
888
		if ($port == ':80' || $port == ':443') {
889
			$port = '';
890
		}
891
		$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
892
		$cutoff = strpos($uri, 'install.php');
893
		$uri = substr($uri, 0, $cutoff);
894
		$serverName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
895
896
		return "$protocol://{$serverName}$port{$uri}";
897
	}
898
899
	/**
900
	 * Load settings.php
901
	 *
902
	 * @return void
903
	 * @throws InstallationException
904
	 */
905
	protected function loadSettingsFile() {
906
		
907
908
		if (!include_once("{$this->CONFIG->path}engine/settings.php")) {
909
			throw new InstallationException(_elgg_services()->translator->translate('InstallationException:CannotLoadSettings'));
910
		}
911
	}
912
913
	/**
914
	 * Action handling methods
915
	 */
916
917
	/**
918
	 * Return an associative array of post variables
919
	 * (could be selective based on expected variables)
920
	 *
921
	 * Does not filter as person installing the site should not be attempting
922
	 * XSS attacks. If filtering is added, it should not be done for passwords.
923
	 *
924
	 * @return array
925
	 */
926
	protected function getPostVariables() {
927
		$vars = array();
928
		foreach ($_POST as $k => $v) {
929
			$vars[$k] = $v;
930
		}
931
		return $vars;
932
	}
933
934
	/**
935
	 * If form is reshown, remember previously submitted variables
936
	 *
937
	 * @param array $formVars       Vars int he form
938
	 * @param array $submissionVars Submitted vars
939
	 *
940
	 * @return array
941
	 */
942
	protected function makeFormSticky($formVars, $submissionVars) {
943
		foreach ($submissionVars as $field => $value) {
944
			$formVars[$field]['value'] = $value;
945
		}
946
		return $formVars;
947
	}
948
949
	/**
950
	 * Requirement checks support methods
951
	 */
952
953
	/**
954
	 * Check that the engine dir is writable
955
	 *
956
	 * @param array &$report The requirements report object
957
	 *
958
	 * @return bool
959
	 */
960
	protected function checkEngineDir(&$report) {
961
		
962
963
		$writable = is_writable("{$this->CONFIG->path}engine");
964
		if (!$writable) {
965
			$report['settings'] = array(
966
				array(
967
					'severity' => 'failure',
968
					'message' => _elgg_services()->translator->translate('install:check:enginedir'),
969
				)
970
			);
971
			return FALSE;
972
		}
973
974
		return TRUE;
975
	}
976
977
	/**
978
	 * Check that the settings file exists
979
	 *
980
	 * @param array &$report The requirements report array
981
	 *
982
	 * @return bool
983
	 */
984
	protected function checkSettingsFile(&$report = array()) {
985
		
986
987
		if (!file_exists("{$this->CONFIG->path}engine/settings.php")) {
988
			return FALSE;
989
		}
990
991
		if (!is_readable("{$this->CONFIG->path}engine/settings.php")) {
992
			$report['settings'] = array(
993
				array(
994
					'severity' => 'failure',
995
					'message' => _elgg_services()->translator->translate('install:check:readsettings'),
996
				)
997
			);
998
		}
999
1000
		return TRUE;
1001
	}
1002
1003
	/**
1004
	 * Check version of PHP, extensions, and variables
1005
	 *
1006
	 * @param array &$report The requirements report array
1007
	 *
1008
	 * @return void
1009
	 */
1010
	protected function checkPHP(&$report) {
1011
		$phpReport = array();
1012
1013
		$min_php_version = '5.4.0';
1014
		if (version_compare(PHP_VERSION, $min_php_version, '<')) {
1015
			$phpReport[] = array(
1016
				'severity' => 'failure',
1017
				'message' => _elgg_services()->translator->translate('install:check:php:version', array($min_php_version, PHP_VERSION))
1018
			);
1019
		}
1020
1021
		$this->checkPhpExtensions($phpReport);
1022
1023
		$this->checkPhpDirectives($phpReport);
1024
1025
		if (count($phpReport) == 0) {
1026
			$phpReport[] = array(
1027
				'severity' => 'pass',
1028
				'message' => _elgg_services()->translator->translate('install:check:php:success')
1029
			);
1030
		}
1031
1032
		$report['php'] = $phpReport;
1033
	}
1034
1035
	/**
1036
	 * Check the server's PHP extensions
1037
	 *
1038
	 * @param array &$phpReport The PHP requirements report array
1039
	 *
1040
	 * @return void
1041
	 */
1042
	protected function checkPhpExtensions(&$phpReport) {
1043
		$extensions = get_loaded_extensions();
1044
		$requiredExtensions = array(
1045
			'mysql',
1046
			'json',
1047
			'xml',
1048
			'gd',
1049
		);
1050 View Code Duplication
		foreach ($requiredExtensions as $extension) {
1051
			if (!in_array($extension, $extensions)) {
1052
				$phpReport[] = array(
1053
					'severity' => 'failure',
1054
					'message' => _elgg_services()->translator->translate('install:check:php:extension', array($extension))
1055
				);
1056
			}
1057
		}
1058
1059
		$recommendedExtensions = array(
1060
			'mbstring',
1061
		);
1062 View Code Duplication
		foreach ($recommendedExtensions as $extension) {
1063
			if (!in_array($extension, $extensions)) {
1064
				$phpReport[] = array(
1065
					'severity' => 'warning',
1066
					'message' => _elgg_services()->translator->translate('install:check:php:extension:recommend', array($extension))
1067
				);
1068
			}
1069
		}
1070
	}
1071
1072
	/**
1073
	 * Check PHP parameters
1074
	 *
1075
	 * @param array &$phpReport The PHP requirements report array
1076
	 *
1077
	 * @return void
1078
	 */
1079
	protected function checkPhpDirectives(&$phpReport) {
1080
		if (ini_get('open_basedir')) {
1081
			$phpReport[] = array(
1082
				'severity' => 'warning',
1083
				'message' => _elgg_services()->translator->translate("install:check:php:open_basedir")
1084
			);
1085
		}
1086
1087
		if (ini_get('safe_mode')) {
1088
			$phpReport[] = array(
1089
				'severity' => 'warning',
1090
				'message' => _elgg_services()->translator->translate("install:check:php:safe_mode")
1091
			);
1092
		}
1093
1094
		if (ini_get('arg_separator.output') !== '&') {
1095
			$separator = htmlspecialchars(ini_get('arg_separator.output'));
1096
			$msg = _elgg_services()->translator->translate("install:check:php:arg_separator", array($separator));
1097
			$phpReport[] = array(
1098
				'severity' => 'failure',
1099
				'message' => $msg,
1100
			);
1101
		}
1102
1103
		if (ini_get('register_globals')) {
1104
			$phpReport[] = array(
1105
				'severity' => 'failure',
1106
				'message' => _elgg_services()->translator->translate("install:check:php:register_globals")
1107
			);
1108
		}
1109
1110
		if (ini_get('session.auto_start')) {
1111
			$phpReport[] = array(
1112
				'severity' => 'failure',
1113
				'message' => _elgg_services()->translator->translate("install:check:php:session.auto_start")
1114
			);
1115
		}
1116
	}
1117
1118
	/**
1119
	 * Confirm that the rewrite rules are firing
1120
	 *
1121
	 * @param array &$report The requirements report array
1122
	 *
1123
	 * @return void
1124
	 */
1125
	protected function checkRewriteRules(&$report) {
1126
		
1127
1128
		$tester = new ElggRewriteTester();
1129
		$url = _elgg_services()->config->getSiteUrl() . "rewrite.php";
1130
		$report['rewrite'] = array($tester->run($url, $this->CONFIG->path));
1131
	}
1132
1133
	/**
1134
	 * Check if the request is coming from the URL rewrite test on the
1135
	 * requirements page.
1136
	 *
1137
	 * @return void
1138
	 */
1139
	protected function processRewriteTest() {
1140
		if (strpos($_SERVER['REQUEST_URI'], 'rewrite.php') !== FALSE) {
1141
			echo 'success';
1142
			exit;
1143
		}
1144
	}
1145
1146
	/**
1147
	 * Count the number of failures in the requirements report
1148
	 *
1149
	 * @param array  $report    The requirements report array
1150
	 * @param string $condition 'failure' or 'warning'
1151
	 *
1152
	 * @return int
1153
	 */
1154
	protected function countNumConditions($report, $condition) {
1155
		$count = 0;
1156
		foreach ($report as $category => $checks) {
1157
			foreach ($checks as $check) {
1158
				if ($check['severity'] === $condition) {
1159
					$count++;
1160
				}
1161
			}
1162
		}
1163
1164
		return $count;
1165
	}
1166
1167
1168
	/**
1169
	 * Database support methods
1170
	 */
1171
1172
	/**
1173
	 * Validate the variables for the database step
1174
	 *
1175
	 * @param array $submissionVars Submitted vars
1176
	 * @param array $formVars       Vars in the form
1177
	 *
1178
	 * @return bool
1179
	 */
1180
	protected function validateDatabaseVars($submissionVars, $formVars) {
1181
1182 View Code Duplication
		foreach ($formVars as $field => $info) {
1183
			if ($info['required'] == TRUE && !$submissionVars[$field]) {
1184
				$name = _elgg_services()->translator->translate("install:database:label:$field");
1185
				register_error(_elgg_services()->translator->translate('install:error:requiredfield', array($name)));
1186
				return FALSE;
1187
			}
1188
		}
1189
1190
		// according to postgres documentation: SQL identifiers and key words must
1191
		// begin with a letter (a-z, but also letters with diacritical marks and
1192
		// non-Latin letters) or an underscore (_). Subsequent characters in an
1193
		// identifier or key word can be letters, underscores, digits (0-9), or dollar signs ($).
1194
		// Refs #4994
1195
		if (!preg_match("/^[a-zA-Z_][\w]*$/", $submissionVars['dbprefix'])) {
1196
			register_error(_elgg_services()->translator->translate('install:error:database_prefix'));
1197
			return FALSE;
1198
		}
1199
1200
		return $this->checkDatabaseSettings(
1201
					$submissionVars['dbuser'],
1202
					$submissionVars['dbpassword'],
1203
					$submissionVars['dbname'],
1204
					$submissionVars['dbhost']
1205
				);
1206
	}
1207
1208
	/**
1209
	 * Confirm the settings for the database
1210
	 *
1211
	 * @param string $user     Username
1212
	 * @param string $password Password
1213
	 * @param string $dbname   Database name
1214
	 * @param string $host     Host
1215
	 *
1216
	 * @return bool
1217
	 */
1218
	protected function checkDatabaseSettings($user, $password, $dbname, $host) {
1219
		$config = new \Elgg\Database\Config((object)[
1220
			'dbhost' => $host,
1221
			'dbuser' => $user,
1222
			'dbpass' => $password,
1223
			'dbname' => $dbname,
1224
		]);
1225
		$logger = new \Elgg\Logger(new \Elgg\PluginHooksService());
1226
		$db = new \Elgg\Database($config, $logger);
1227
1228
		try {
1229
			$db->getDataRow("SELECT 1");
1230
		} catch (DatabaseException $e) {
1231
			if (0 === strpos($e->getMessage(), "Elgg couldn't connect")) {
1232
				register_error(_elgg_services()->translator->translate('install:error:databasesettings'));
1233
			} else {
1234
				register_error(_elgg_services()->translator->translate('install:error:nodatabase', array($dbname)));
1235
			}
1236
			return FALSE;
1237
		}
1238
1239
		// check MySQL version - must be 5.0 or >
1240
		$version = $db->getServerVersion(\Elgg\Database\Config::READ_WRITE);
1241
		$required_version = 5.0;
1242
		$points = explode('.', $version);
1243
		if ($points[0] < $required_version) {
1244
			register_error(_elgg_services()->translator->translate('install:error:oldmysql', array($version)));
1245
			return FALSE;
1246
		}
1247
1248
		return TRUE;
1249
	}
1250
1251
	/**
1252
	 * Writes the settings file to the engine directory
1253
	 *
1254
	 * @param array $params Array of inputted params from the user
1255
	 *
1256
	 * @return bool
1257
	 */
1258
	protected function createSettingsFile($params) {
1259
		
1260
1261
		$templateFile = "{$this->CONFIG->path}engine/settings.example.php";
1262
		$template = file_get_contents($templateFile);
1263
		if (!$template) {
1264
			register_error(_elgg_services()->translator->translate('install:error:readsettingsphp'));
1265
			return FALSE;
1266
		}
1267
1268
		foreach ($params as $k => $v) {
1269
			$template = str_replace("{{" . $k . "}}", $v, $template);
1270
		}
1271
1272
		$settingsFilename = "{$this->CONFIG->path}engine/settings.php";
1273
		$result = file_put_contents($settingsFilename, $template);
1274
		if (!$result) {
1275
			register_error(_elgg_services()->translator->translate('install:error:writesettingphp'));
1276
			return FALSE;
1277
		}
1278
1279
		return TRUE;
1280
	}
1281
1282
	/**
1283
	 * Bootstrap database connection before entire engine is available
1284
	 *
1285
	 * @return bool
1286
	 */
1287
	protected function connectToDatabase() {
1288
		
1289
1290
		if (!include_once("{$this->CONFIG->path}engine/settings.php")) {
1291
			register_error('Elgg could not load the settings file. It does not exist or there is a file permissions issue.');
1292
			return FALSE;
1293
		}
1294
1295
		if (!include_once("{$this->CONFIG->path}engine/lib/database.php")) {
1296
			register_error('Could not load database.php');
1297
			return FALSE;
1298
		}
1299
1300
		try  {
1301
			_elgg_services()->db->setupConnections();
1302
		} catch (DatabaseException $e) {
1303
			register_error($e->getMessage());
1304
			return FALSE;
1305
		}
1306
1307
		return TRUE;
1308
	}
1309
1310
	/**
1311
	 * Create the database tables
1312
	 *
1313
	 * @return bool
1314
	 */
1315
	protected function installDatabase() {
1316
		
1317
1318
		try {
1319
			_elgg_services()->db->runSqlScript("{$this->CONFIG->path}engine/schema/mysql.sql");
1320
		} catch (Exception $e) {
1321
			$msg = $e->getMessage();
1322
			if (strpos($msg, 'already exists')) {
1323
				$msg = _elgg_services()->translator->translate('install:error:tables_exist');
1324
			}
1325
			register_error($msg);
1326
			return FALSE;
1327
		}
1328
1329
		return TRUE;
1330
	}
1331
1332
	/**
1333
	 * Site settings support methods
1334
	 */
1335
1336
	/**
1337
	 * Create the data directory if requested
1338
	 *
1339
	 * @param array &$submissionVars Submitted vars
1340
	 * @param array $formVars        Variables in the form
1341
	 * 
1342
	 * @return bool
1343
	 */
1344
	protected function createDataDirectory(&$submissionVars, $formVars) {
1345
		// did the user have option of Elgg creating the data directory
1346
		if ($formVars['dataroot']['type'] != 'combo') {
1347
			return TRUE;
1348
		}
1349
1350
		// did the user select the option
1351
		if ($submissionVars['dataroot'] != 'dataroot-checkbox') {
1352
			return TRUE;
1353
		}
1354
1355
		$dir = sanitise_filepath($submissionVars['path']) . 'data';
1356
		if (file_exists($dir) || mkdir($dir, 0700)) {
1357
			$submissionVars['dataroot'] = $dir;
1358
			if (!file_exists("$dir/.htaccess")) {
1359
				$htaccess = "Order Deny,Allow\nDeny from All\n";
1360
				if (!file_put_contents("$dir/.htaccess", $htaccess)) {
1361
					return FALSE;
1362
				}
1363
			}
1364
			return TRUE;
1365
		}
1366
1367
		return FALSE;
1368
	}
1369
1370
	/**
1371
	 * Validate the site settings form variables
1372
	 *
1373
	 * @param array $submissionVars Submitted vars
1374
	 * @param array $formVars       Vars in the form
1375
	 *
1376
	 * @return bool
1377
	 */
1378
	protected function validateSettingsVars($submissionVars, $formVars) {
1379
		
1380
1381
		foreach ($formVars as $field => $info) {
1382
			$submissionVars[$field] = trim($submissionVars[$field]);
1383
			if ($info['required'] == TRUE && $submissionVars[$field] === '') {
1384
				$name = _elgg_services()->translator->translate("install:settings:label:$field");
1385
				register_error(_elgg_services()->translator->translate('install:error:requiredfield', array($name)));
1386
				return FALSE;
1387
			}
1388
		}
1389
1390
		// check that data root is absolute path
1391
		if (stripos(PHP_OS, 'win') === 0) {
1392 View Code Duplication
			if (strpos($submissionVars['dataroot'], ':') !== 1) {
1393
				$msg = _elgg_services()->translator->translate('install:error:relative_path', array($submissionVars['dataroot']));
1394
				register_error($msg);
1395
				return FALSE;
1396
			}
1397 View Code Duplication
		} else {
1398
			if (strpos($submissionVars['dataroot'], '/') !== 0) {
1399
				$msg = _elgg_services()->translator->translate('install:error:relative_path', array($submissionVars['dataroot']));
1400
				register_error($msg);
1401
				return FALSE;
1402
			}
1403
		}
1404
1405
		// check that data root exists
1406 View Code Duplication
		if (!file_exists($submissionVars['dataroot'])) {
1407
			$msg = _elgg_services()->translator->translate('install:error:datadirectoryexists', array($submissionVars['dataroot']));
1408
			register_error($msg);
1409
			return FALSE;
1410
		}
1411
1412
		// check that data root is writable
1413 View Code Duplication
		if (!is_writable($submissionVars['dataroot'])) {
1414
			$msg = _elgg_services()->translator->translate('install:error:writedatadirectory', array($submissionVars['dataroot']));
1415
			register_error($msg);
1416
			return FALSE;
1417
		}
1418
1419
		if (!isset($this->CONFIG->data_dir_override) || !$this->CONFIG->data_dir_override) {
1420
			// check that data root is not subdirectory of Elgg root
1421 View Code Duplication
			if (stripos($submissionVars['dataroot'], $submissionVars['path']) === 0) {
1422
				$msg = _elgg_services()->translator->translate('install:error:locationdatadirectory', array($submissionVars['dataroot']));
1423
				register_error($msg);
1424
				return FALSE;
1425
			}
1426
		}
1427
1428
		// check that email address is email address
1429 View Code Duplication
		if ($submissionVars['siteemail'] && !is_email_address($submissionVars['siteemail'])) {
1430
			$msg = _elgg_services()->translator->translate('install:error:emailaddress', array($submissionVars['siteemail']));
1431
			register_error($msg);
1432
			return FALSE;
1433
		}
1434
1435
		// @todo check that url is a url
1436
		// @note filter_var cannot be used because it doesn't work on international urls
1437
1438
		return TRUE;
1439
	}
1440
1441
	/**
1442
	 * Initialize the site including site entity, plugins, and configuration
1443
	 *
1444
	 * @param array $submissionVars Submitted vars
1445
	 *
1446
	 * @return bool
1447
	 */
1448
	protected function saveSiteSettings($submissionVars) {
1449
		
1450
1451
		// ensure that file path, data path, and www root end in /
1452
		$submissionVars['dataroot'] = sanitise_filepath($submissionVars['dataroot']);
1453
		$submissionVars['wwwroot'] = sanitise_filepath($submissionVars['wwwroot']);
1454
1455
		$site = new ElggSite();
1456
		$site->name = strip_tags($submissionVars['sitename']);
1457
		$site->url = $submissionVars['wwwroot'];
1458
		$site->access_id = ACCESS_PUBLIC;
1459
		$site->email = $submissionVars['siteemail'];
1460
		$guid = $site->save();
1461
1462
		if (!$guid) {
1463
			register_error(_elgg_services()->translator->translate('install:error:createsite'));
1464
			return FALSE;
1465
		}
1466
1467
		// bootstrap site info
1468
		$this->CONFIG->site_guid = $guid;
1469
		$this->CONFIG->site_id = $guid;
1470
		$this->CONFIG->site = $site;
1471
1472
		_elgg_services()->datalist->set('installed', time());
1473
		_elgg_services()->datalist->set('dataroot', $submissionVars['dataroot']);
1474
		_elgg_services()->datalist->set('default_site', $site->getGUID());
1475
		_elgg_services()->datalist->set('version', elgg_get_version());
0 ignored issues
show
It seems like elgg_get_version() can also be of type false; however, Elgg\Database\Datalist::set() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
1476
		_elgg_services()->datalist->set('simplecache_enabled', 1);
1477
		_elgg_services()->datalist->set('system_cache_enabled', 1);
1478
		_elgg_services()->datalist->set('simplecache_lastupdate', time());
1479
1480
		// @todo plugins might use this, but core doesn't. remove in 2.0
1481
		_elgg_services()->datalist->set('path', $this->CONFIG->path);
1482
1483
		// new installations have run all the upgrades
1484
		$upgrades = elgg_get_upgrade_files("{$this->CONFIG->path}engine/lib/upgrades/");
1485
		_elgg_services()->datalist->set('processed_upgrades', serialize($upgrades));
1486
1487
		_elgg_services()->configTable->set('view', 'default', $site->getGUID());
1488
		_elgg_services()->configTable->set('language', 'en', $site->getGUID());
1489
		_elgg_services()->configTable->set('default_access', $submissionVars['siteaccess'], $site->getGUID());
1490
		_elgg_services()->configTable->set('allow_registration', TRUE, $site->getGUID());
1491
		_elgg_services()->configTable->set('walled_garden', FALSE, $site->getGUID());
1492
		_elgg_services()->configTable->set('allow_user_default_access', '', $site->getGUID());
1493
		_elgg_services()->configTable->set('default_limit', 10, $site->getGUID());
1494
1495
		$this->setSubtypeClasses();
1496
1497
		$this->enablePlugins();
1498
1499
		return TRUE;
1500
	}
1501
1502
	/**
1503
	 * Register classes for core objects
1504
	 *
1505
	 * @return void
1506
	 */
1507
	protected function setSubtypeClasses() {
1508
		add_subtype("object", "plugin", "ElggPlugin");
1509
		add_subtype("object", "file", "ElggFile");
1510
		add_subtype("object", "widget", "ElggWidget");
1511
		add_subtype("object", "comment", "ElggComment");
1512
		add_subtype("object", "elgg_upgrade", 'ElggUpgrade');
1513
	}
1514
1515
	/**
1516
	 * Enable a set of default plugins
1517
	 *
1518
	 * @return void
1519
	 */
1520
	protected function enablePlugins() {
1521
		_elgg_generate_plugin_entities();
1522
		$plugins = elgg_get_plugins('any');
1523
		foreach ($plugins as $plugin) {
1524
			if ($plugin->getManifest()) {
1525
				if ($plugin->getManifest()->getActivateOnInstall()) {
1526
					$plugin->activate();
1527
				}
1528
				if (in_array('theme', $plugin->getManifest()->getCategories())) {
1529
					$plugin->setPriority('last');
1530
				}
1531
			}
1532
		}
1533
	}
1534
1535
	/**
1536
	 * Admin account support methods
1537
	 */
1538
1539
	/**
1540
	 * Validate account form variables
1541
	 *
1542
	 * @param array $submissionVars Submitted vars
1543
	 * @param array $formVars       Form vars
1544
	 *
1545
	 * @return bool
1546
	 */
1547
	protected function validateAdminVars($submissionVars, $formVars) {
1548
1549 View Code Duplication
		foreach ($formVars as $field => $info) {
1550
			if ($info['required'] == TRUE && !$submissionVars[$field]) {
1551
				$name = _elgg_services()->translator->translate("install:admin:label:$field");
1552
				register_error(_elgg_services()->translator->translate('install:error:requiredfield', array($name)));
1553
				return FALSE;
1554
			}
1555
		}
1556
1557
		if ($submissionVars['password1'] !== $submissionVars['password2']) {
1558
			register_error(_elgg_services()->translator->translate('install:admin:password:mismatch'));
1559
			return FALSE;
1560
		}
1561
1562
		if (trim($submissionVars['password1']) == "") {
1563
			register_error(_elgg_services()->translator->translate('install:admin:password:empty'));
1564
			return FALSE;
1565
		}
1566
1567
		$minLength = _elgg_services()->configTable->get('min_password_length');
1568
		if (strlen($submissionVars['password1']) < $minLength) {
1569
			register_error(_elgg_services()->translator->translate('install:admin:password:tooshort'));
1570
			return FALSE;
1571
		}
1572
1573
		// check that email address is email address
1574 View Code Duplication
		if ($submissionVars['email'] && !is_email_address($submissionVars['email'])) {
1575
			$msg = _elgg_services()->translator->translate('install:error:emailaddress', array($submissionVars['email']));
1576
			register_error($msg);
1577
			return FALSE;
1578
		}
1579
1580
		return TRUE;
1581
	}
1582
1583
	/**
1584
	 * Create a user account for the admin
1585
	 *
1586
	 * @param array $submissionVars Submitted vars
1587
	 * @param bool  $login          Login in the admin user?
1588
	 *
1589
	 * @return bool
1590
	 */
1591
	protected function createAdminAccount($submissionVars, $login = FALSE) {
1592
		try {
1593
			$guid = register_user(
1594
					$submissionVars['username'],
1595
					$submissionVars['password1'],
1596
					$submissionVars['displayname'],
1597
					$submissionVars['email']
1598
					);
1599
		} catch (Exception $e) {
1600
			register_error($e->getMessage());
1601
			return false;
1602
		}
1603
1604
		if (!$guid) {
1605
			register_error(_elgg_services()->translator->translate('install:admin:cannot_create'));
1606
			return false;
1607
		}
1608
1609
		$user = get_entity($guid);
1610
		if (!$user instanceof ElggUser) {
1611
			register_error(_elgg_services()->translator->translate('install:error:loadadmin'));
1612
			return false;
1613
		}
1614
1615
		elgg_set_ignore_access(TRUE);
1616
		if ($user->makeAdmin() == FALSE) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1617
			register_error(_elgg_services()->translator->translate('install:error:adminaccess'));
1618
		} else {
1619
			_elgg_services()->datalist->set('admin_registered', 1);
1620
		}
1621
		elgg_set_ignore_access(false);
1622
1623
		// add validation data to satisfy user validation plugins
1624
		create_metadata($guid, 'validated', TRUE, '', 0, ACCESS_PUBLIC);
0 ignored issues
show
TRUE is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1625
		create_metadata($guid, 'validated_method', 'admin_user', '', 0, ACCESS_PUBLIC);
1626
1627
		if ($login) {
1628
			$handler = new Elgg\Http\DatabaseSessionHandler(_elgg_services()->db);
1629
1630
			// session.cache_limiter is unfortunately set to "" by the NativeSessionStorage constructor,
1631
			// so we must capture and inject it directly.
1632
			$options = [
1633
				'cache_limiter' => session_cache_limiter(),
1634
			];
1635
			$storage = new Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage($options, $handler);
1636
1637
			$session = new ElggSession(new Symfony\Component\HttpFoundation\Session\Session($storage));
1638
			$session->setName('Elgg');
1639
			_elgg_services()->setValue('session', $session);
1640
			if (login($user) == FALSE) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1641
				register_error(_elgg_services()->translator->translate('install:error:adminlogin'));
1642
			}
1643
		}
1644
1645
		return TRUE;
1646
	}
1647
}
1648