Issues (4122)

Security Analysis    not enabled

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.

includes/installer/DatabaseInstaller.php (2 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
 * DBMS-specific installation helper.
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @ingroup Deployment
22
 */
23
24
/**
25
 * Base class for DBMS-specific installation helper classes.
26
 *
27
 * @ingroup Deployment
28
 * @since 1.17
29
 */
30
abstract class DatabaseInstaller {
31
32
	/**
33
	 * The Installer object.
34
	 *
35
	 * @todo Naming this parent is confusing, 'installer' would be clearer.
36
	 *
37
	 * @var WebInstaller
38
	 */
39
	public $parent;
40
41
	/**
42
	 * The database connection.
43
	 *
44
	 * @var Database
45
	 */
46
	public $db = null;
47
48
	/**
49
	 * Internal variables for installation.
50
	 *
51
	 * @var array
52
	 */
53
	protected $internalDefaults = [];
54
55
	/**
56
	 * Array of MW configuration globals this class uses.
57
	 *
58
	 * @var array
59
	 */
60
	protected $globalNames = [];
61
62
	/**
63
	 * Return the internal name, e.g. 'mysql', or 'sqlite'.
64
	 */
65
	abstract public function getName();
66
67
	/**
68
	 * @return bool Returns true if the client library is compiled in.
69
	 */
70
	abstract public function isCompiled();
71
72
	/**
73
	 * Checks for installation prerequisites other than those checked by isCompiled()
74
	 * @since 1.19
75
	 * @return Status
76
	 */
77
	public function checkPrerequisites() {
78
		return Status::newGood();
79
	}
80
81
	/**
82
	 * Get HTML for a web form that configures this database. Configuration
83
	 * at this time should be the minimum needed to connect and test
84
	 * whether install or upgrade is required.
85
	 *
86
	 * If this is called, $this->parent can be assumed to be a WebInstaller.
87
	 */
88
	abstract public function getConnectForm();
89
90
	/**
91
	 * Set variables based on the request array, assuming it was submitted
92
	 * via the form returned by getConnectForm(). Validate the connection
93
	 * settings by attempting to connect with them.
94
	 *
95
	 * If this is called, $this->parent can be assumed to be a WebInstaller.
96
	 *
97
	 * @return Status
98
	 */
99
	abstract public function submitConnectForm();
100
101
	/**
102
	 * Get HTML for a web form that retrieves settings used for installation.
103
	 * $this->parent can be assumed to be a WebInstaller.
104
	 * If the DB type has no settings beyond those already configured with
105
	 * getConnectForm(), this should return false.
106
	 * @return bool
107
	 */
108
	public function getSettingsForm() {
109
		return false;
110
	}
111
112
	/**
113
	 * Set variables based on the request array, assuming it was submitted via
114
	 * the form return by getSettingsForm().
115
	 *
116
	 * @return Status
117
	 */
118
	public function submitSettingsForm() {
119
		return Status::newGood();
120
	}
121
122
	/**
123
	 * Open a connection to the database using the administrative user/password
124
	 * currently defined in the session, without any caching. Returns a status
125
	 * object. On success, the status object will contain a Database object in
126
	 * its value member.
127
	 *
128
	 * @return Status
129
	 */
130
	abstract public function openConnection();
131
132
	/**
133
	 * Create the database and return a Status object indicating success or
134
	 * failure.
135
	 *
136
	 * @return Status
137
	 */
138
	abstract public function setupDatabase();
139
140
	/**
141
	 * Connect to the database using the administrative user/password currently
142
	 * defined in the session. Returns a status object. On success, the status
143
	 * object will contain a Database object in its value member.
144
	 *
145
	 * This will return a cached connection if one is available.
146
	 *
147
	 * @return Status
148
	 */
149
	public function getConnection() {
150
		if ( $this->db ) {
151
			return Status::newGood( $this->db );
152
		}
153
154
		$status = $this->openConnection();
155
		if ( $status->isOK() ) {
156
			$this->db = $status->value;
157
			// Enable autocommit
158
			$this->db->clearFlag( DBO_TRX );
159
			$this->db->commit( __METHOD__ );
160
		}
161
162
		return $status;
163
	}
164
165
	/**
166
	 * Apply a SQL source file to the database as part of running an installation step.
167
	 *
168
	 * @param string $sourceFileMethod
169
	 * @param string $stepName
170
	 * @param bool $archiveTableMustNotExist
171
	 * @return Status
172
	 */
173
	private function stepApplySourceFile(
174
		$sourceFileMethod,
175
		$stepName,
176
		$archiveTableMustNotExist = false
177
	) {
178
		$status = $this->getConnection();
179
		if ( !$status->isOK() ) {
180
			return $status;
181
		}
182
		$this->db->selectDB( $this->getVar( 'wgDBname' ) );
183
184
		if ( $archiveTableMustNotExist && $this->db->tableExists( 'archive', __METHOD__ ) ) {
185
			$status->warning( "config-$stepName-tables-exist" );
186
			$this->enableLB();
187
188
			return $status;
189
		}
190
191
		$this->db->setFlag( DBO_DDLMODE ); // For Oracle's handling of schema files
192
		$this->db->begin( __METHOD__ );
193
194
		$error = $this->db->sourceFile(
195
			call_user_func( [ $this, $sourceFileMethod ], $this->db )
196
		);
197
		if ( $error !== true ) {
198
			$this->db->reportQueryError( $error, 0, '', __METHOD__ );
0 ignored issues
show
It seems like $error defined by $this->db->sourceFile(ca...ileMethod), $this->db)) on line 194 can also be of type boolean; however, Database::reportQueryError() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
199
			$this->db->rollback( __METHOD__ );
200
			$status->fatal( "config-$stepName-tables-failed", $error );
201
		} else {
202
			$this->db->commit( __METHOD__ );
203
		}
204
		// Resume normal operations
205
		if ( $status->isOK() ) {
206
			$this->enableLB();
207
		}
208
209
		return $status;
210
	}
211
212
	/**
213
	 * Create database tables from scratch.
214
	 *
215
	 * @return Status
216
	 */
217
	public function createTables() {
218
		return $this->stepApplySourceFile( 'getSchemaPath', 'install', true );
219
	}
220
221
	/**
222
	 * Insert update keys into table to prevent running unneded updates.
223
	 *
224
	 * @return Status
225
	 */
226
	public function insertUpdateKeys() {
227
		return $this->stepApplySourceFile( 'getUpdateKeysPath', 'updates', false );
228
	}
229
230
	/**
231
	 * Return a path to the DBMS-specific SQL file if it exists,
232
	 * otherwise default SQL file
233
	 *
234
	 * @param IDatabase $db
235
	 * @param string $filename
236
	 * @return string
237
	 */
238 View Code Duplication
	private function getSqlFilePath( $db, $filename ) {
239
		global $IP;
240
241
		$dbmsSpecificFilePath = "$IP/maintenance/" . $db->getType() . "/$filename";
242
		if ( file_exists( $dbmsSpecificFilePath ) ) {
243
			return $dbmsSpecificFilePath;
244
		} else {
245
			return "$IP/maintenance/$filename";
246
		}
247
	}
248
249
	/**
250
	 * Return a path to the DBMS-specific schema file,
251
	 * otherwise default to tables.sql
252
	 *
253
	 * @param IDatabase $db
254
	 * @return string
255
	 */
256
	public function getSchemaPath( $db ) {
257
		return $this->getSqlFilePath( $db, 'tables.sql' );
258
	}
259
260
	/**
261
	 * Return a path to the DBMS-specific update key file,
262
	 * otherwise default to update-keys.sql
263
	 *
264
	 * @param IDatabase $db
265
	 * @return string
266
	 */
267
	public function getUpdateKeysPath( $db ) {
268
		return $this->getSqlFilePath( $db, 'update-keys.sql' );
269
	}
270
271
	/**
272
	 * Create the tables for each extension the user enabled
273
	 * @return Status
274
	 */
275
	public function createExtensionTables() {
276
		$status = $this->getConnection();
277
		if ( !$status->isOK() ) {
278
			return $status;
279
		}
280
281
		// Now run updates to create tables for old extensions
282
		DatabaseUpdater::newForDB( $this->db )->doUpdates( [ 'extensions' ] );
283
284
		return $status;
285
	}
286
287
	/**
288
	 * Get the DBMS-specific options for LocalSettings.php generation.
289
	 *
290
	 * @return string
291
	 */
292
	abstract public function getLocalSettings();
293
294
	/**
295
	 * Override this to provide DBMS-specific schema variables, to be
296
	 * substituted into tables.sql and other schema files.
297
	 * @return array
298
	 */
299
	public function getSchemaVars() {
300
		return [];
301
	}
302
303
	/**
304
	 * Set appropriate schema variables in the current database connection.
305
	 *
306
	 * This should be called after any request data has been imported, but before
307
	 * any write operations to the database.
308
	 */
309
	public function setupSchemaVars() {
310
		$status = $this->getConnection();
311
		if ( $status->isOK() ) {
312
			$status->value->setSchemaVars( $this->getSchemaVars() );
313
		} else {
314
			$msg = __METHOD__ . ': unexpected error while establishing'
315
				. ' a database connection with message: '
316
				. $status->getMessage()->plain();
317
			throw new MWException( $msg );
318
		}
319
	}
320
321
	/**
322
	 * Set up LBFactory so that wfGetDB() etc. works.
323
	 * We set up a special LBFactory instance which returns the current
324
	 * installer connection.
325
	 */
326
	public function enableLB() {
327
		$status = $this->getConnection();
328
		if ( !$status->isOK() ) {
329
			throw new MWException( __METHOD__ . ': unexpected DB connection error' );
330
		}
331
332
		\MediaWiki\MediaWikiServices::resetGlobalInstance();
333
		$services = \MediaWiki\MediaWikiServices::getInstance();
334
335
		$connection = $status->value;
336
		$services->redefineService( 'DBLoadBalancerFactory', function() use ( $connection ) {
337
			return LBFactorySingle::newFromConnection( $connection );
338
		} );
339
	}
340
341
	/**
342
	 * Perform database upgrades
343
	 *
344
	 * @return bool
345
	 */
346
	public function doUpgrade() {
347
		$this->setupSchemaVars();
348
		$this->enableLB();
349
350
		$ret = true;
351
		ob_start( [ $this, 'outputHandler' ] );
352
		$up = DatabaseUpdater::newForDB( $this->db );
353
		try {
354
			$up->doUpdates();
355
		} catch ( MWException $e ) {
356
			echo "\nAn error occurred:\n";
357
			echo $e->getText();
358
			$ret = false;
359
		} catch ( Exception $e ) {
360
			echo "\nAn error occurred:\n";
361
			echo $e->getMessage();
362
			$ret = false;
363
		}
364
		$up->purgeCache();
365
		ob_end_flush();
366
367
		return $ret;
368
	}
369
370
	/**
371
	 * Allow DB installers a chance to make last-minute changes before installation
372
	 * occurs. This happens before setupDatabase() or createTables() is called, but
373
	 * long after the constructor. Helpful for things like modifying setup steps :)
374
	 */
375
	public function preInstall() {
376
	}
377
378
	/**
379
	 * Allow DB installers a chance to make checks before upgrade.
380
	 */
381
	public function preUpgrade() {
382
	}
383
384
	/**
385
	 * Get an array of MW configuration globals that will be configured by this class.
386
	 * @return array
387
	 */
388
	public function getGlobalNames() {
389
		return $this->globalNames;
390
	}
391
392
	/**
393
	 * Construct and initialise parent.
394
	 * This is typically only called from Installer::getDBInstaller()
395
	 * @param WebInstaller $parent
396
	 */
397
	public function __construct( $parent ) {
398
		$this->parent = $parent;
399
	}
400
401
	/**
402
	 * Convenience function.
403
	 * Check if a named extension is present.
404
	 *
405
	 * @param string $name
406
	 * @return bool
407
	 */
408
	protected static function checkExtension( $name ) {
409
		return extension_loaded( $name );
410
	}
411
412
	/**
413
	 * Get the internationalised name for this DBMS.
414
	 * @return string
415
	 */
416
	public function getReadableName() {
417
		// Messages: config-type-mysql, config-type-postgres, config-type-sqlite,
418
		// config-type-oracle
419
		return wfMessage( 'config-type-' . $this->getName() )->text();
420
	}
421
422
	/**
423
	 * Get a name=>value map of MW configuration globals for the default values.
424
	 * @return array
425
	 */
426
	public function getGlobalDefaults() {
0 ignored issues
show
getGlobalDefaults uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
427
		$defaults = [];
428
		foreach ( $this->getGlobalNames() as $var ) {
429
			if ( isset( $GLOBALS[$var] ) ) {
430
				$defaults[$var] = $GLOBALS[$var];
431
			}
432
		}
433
		return $defaults;
434
	}
435
436
	/**
437
	 * Get a name=>value map of internal variables used during installation.
438
	 * @return array
439
	 */
440
	public function getInternalDefaults() {
441
		return $this->internalDefaults;
442
	}
443
444
	/**
445
	 * Get a variable, taking local defaults into account.
446
	 * @param string $var
447
	 * @param mixed|null $default
448
	 * @return mixed
449
	 */
450
	public function getVar( $var, $default = null ) {
451
		$defaults = $this->getGlobalDefaults();
452
		$internal = $this->getInternalDefaults();
453
		if ( isset( $defaults[$var] ) ) {
454
			$default = $defaults[$var];
455
		} elseif ( isset( $internal[$var] ) ) {
456
			$default = $internal[$var];
457
		}
458
459
		return $this->parent->getVar( $var, $default );
460
	}
461
462
	/**
463
	 * Convenience alias for $this->parent->setVar()
464
	 * @param string $name
465
	 * @param mixed $value
466
	 */
467
	public function setVar( $name, $value ) {
468
		$this->parent->setVar( $name, $value );
469
	}
470
471
	/**
472
	 * Get a labelled text box to configure a local variable.
473
	 *
474
	 * @param string $var
475
	 * @param string $label
476
	 * @param array $attribs
477
	 * @param string $helpData
478
	 * @return string
479
	 */
480 View Code Duplication
	public function getTextBox( $var, $label, $attribs = [], $helpData = "" ) {
481
		$name = $this->getName() . '_' . $var;
482
		$value = $this->getVar( $var );
483
		if ( !isset( $attribs ) ) {
484
			$attribs = [];
485
		}
486
487
		return $this->parent->getTextBox( [
488
			'var' => $var,
489
			'label' => $label,
490
			'attribs' => $attribs,
491
			'controlName' => $name,
492
			'value' => $value,
493
			'help' => $helpData
494
		] );
495
	}
496
497
	/**
498
	 * Get a labelled password box to configure a local variable.
499
	 * Implements password hiding.
500
	 *
501
	 * @param string $var
502
	 * @param string $label
503
	 * @param array $attribs
504
	 * @param string $helpData
505
	 * @return string
506
	 */
507 View Code Duplication
	public function getPasswordBox( $var, $label, $attribs = [], $helpData = "" ) {
508
		$name = $this->getName() . '_' . $var;
509
		$value = $this->getVar( $var );
510
		if ( !isset( $attribs ) ) {
511
			$attribs = [];
512
		}
513
514
		return $this->parent->getPasswordBox( [
515
			'var' => $var,
516
			'label' => $label,
517
			'attribs' => $attribs,
518
			'controlName' => $name,
519
			'value' => $value,
520
			'help' => $helpData
521
		] );
522
	}
523
524
	/**
525
	 * Get a labelled checkbox to configure a local boolean variable.
526
	 *
527
	 * @param string $var
528
	 * @param string $label
529
	 * @param array $attribs Optional.
530
	 * @param string $helpData Optional.
531
	 * @return string
532
	 */
533 View Code Duplication
	public function getCheckBox( $var, $label, $attribs = [], $helpData = "" ) {
534
		$name = $this->getName() . '_' . $var;
535
		$value = $this->getVar( $var );
536
537
		return $this->parent->getCheckBox( [
538
			'var' => $var,
539
			'label' => $label,
540
			'attribs' => $attribs,
541
			'controlName' => $name,
542
			'value' => $value,
543
			'help' => $helpData
544
		] );
545
	}
546
547
	/**
548
	 * Get a set of labelled radio buttons.
549
	 *
550
	 * @param array $params Parameters are:
551
	 *      var:            The variable to be configured (required)
552
	 *      label:          The message name for the label (required)
553
	 *      itemLabelPrefix: The message name prefix for the item labels (required)
554
	 *      values:         List of allowed values (required)
555
	 *      itemAttribs     Array of attribute arrays, outer key is the value name (optional)
556
	 *
557
	 * @return string
558
	 */
559
	public function getRadioSet( $params ) {
560
		$params['controlName'] = $this->getName() . '_' . $params['var'];
561
		$params['value'] = $this->getVar( $params['var'] );
562
563
		return $this->parent->getRadioSet( $params );
564
	}
565
566
	/**
567
	 * Convenience function to set variables based on form data.
568
	 * Assumes that variables containing "password" in the name are (potentially
569
	 * fake) passwords.
570
	 * @param array $varNames
571
	 * @return array
572
	 */
573
	public function setVarsFromRequest( $varNames ) {
574
		return $this->parent->setVarsFromRequest( $varNames, $this->getName() . '_' );
575
	}
576
577
	/**
578
	 * Determine whether an existing installation of MediaWiki is present in
579
	 * the configured administrative connection. Returns true if there is
580
	 * such a wiki, false if the database doesn't exist.
581
	 *
582
	 * Traditionally, this is done by testing for the existence of either
583
	 * the revision table or the cur table.
584
	 *
585
	 * @return bool
586
	 */
587
	public function needsUpgrade() {
588
		$status = $this->getConnection();
589
		if ( !$status->isOK() ) {
590
			return false;
591
		}
592
593
		if ( !$this->db->selectDB( $this->getVar( 'wgDBname' ) ) ) {
594
			return false;
595
		}
596
597
		return $this->db->tableExists( 'cur', __METHOD__ ) ||
598
			$this->db->tableExists( 'revision', __METHOD__ );
599
	}
600
601
	/**
602
	 * Get a standard install-user fieldset.
603
	 *
604
	 * @return string
605
	 */
606
	public function getInstallUserBox() {
607
		return Html::openElement( 'fieldset' ) .
608
			Html::element( 'legend', [], wfMessage( 'config-db-install-account' )->text() ) .
609
			$this->getTextBox(
610
				'_InstallUser',
611
				'config-db-username',
612
				[ 'dir' => 'ltr' ],
613
				$this->parent->getHelpBox( 'config-db-install-username' )
614
			) .
615
			$this->getPasswordBox(
616
				'_InstallPassword',
617
				'config-db-password',
618
				[ 'dir' => 'ltr' ],
619
				$this->parent->getHelpBox( 'config-db-install-password' )
620
			) .
621
			Html::closeElement( 'fieldset' );
622
	}
623
624
	/**
625
	 * Submit a standard install user fieldset.
626
	 * @return Status
627
	 */
628
	public function submitInstallUserBox() {
629
		$this->setVarsFromRequest( [ '_InstallUser', '_InstallPassword' ] );
630
631
		return Status::newGood();
632
	}
633
634
	/**
635
	 * Get a standard web-user fieldset
636
	 * @param string|bool $noCreateMsg Message to display instead of the creation checkbox.
637
	 *   Set this to false to show a creation checkbox (default).
638
	 *
639
	 * @return string
640
	 */
641
	public function getWebUserBox( $noCreateMsg = false ) {
642
		$wrapperStyle = $this->getVar( '_SameAccount' ) ? 'display: none' : '';
643
		$s = Html::openElement( 'fieldset' ) .
644
			Html::element( 'legend', [], wfMessage( 'config-db-web-account' )->text() ) .
645
			$this->getCheckBox(
646
				'_SameAccount', 'config-db-web-account-same',
647
				[ 'class' => 'hideShowRadio', 'rel' => 'dbOtherAccount' ]
648
			) .
649
			Html::openElement( 'div', [ 'id' => 'dbOtherAccount', 'style' => $wrapperStyle ] ) .
650
			$this->getTextBox( 'wgDBuser', 'config-db-username' ) .
651
			$this->getPasswordBox( 'wgDBpassword', 'config-db-password' ) .
652
			$this->parent->getHelpBox( 'config-db-web-help' );
653 View Code Duplication
		if ( $noCreateMsg ) {
654
			$s .= $this->parent->getWarningBox( wfMessage( $noCreateMsg )->plain() );
655
		} else {
656
			$s .= $this->getCheckBox( '_CreateDBAccount', 'config-db-web-create' );
657
		}
658
		$s .= Html::closeElement( 'div' ) . Html::closeElement( 'fieldset' );
659
660
		return $s;
661
	}
662
663
	/**
664
	 * Submit the form from getWebUserBox().
665
	 *
666
	 * @return Status
667
	 */
668
	public function submitWebUserBox() {
669
		$this->setVarsFromRequest(
670
			[ 'wgDBuser', 'wgDBpassword', '_SameAccount', '_CreateDBAccount' ]
671
		);
672
673
		if ( $this->getVar( '_SameAccount' ) ) {
674
			$this->setVar( 'wgDBuser', $this->getVar( '_InstallUser' ) );
675
			$this->setVar( 'wgDBpassword', $this->getVar( '_InstallPassword' ) );
676
		}
677
678 View Code Duplication
		if ( $this->getVar( '_CreateDBAccount' ) && strval( $this->getVar( 'wgDBpassword' ) ) == '' ) {
679
			return Status::newFatal( 'config-db-password-empty', $this->getVar( 'wgDBuser' ) );
680
		}
681
682
		return Status::newGood();
683
	}
684
685
	/**
686
	 * Common function for databases that don't understand the MySQLish syntax of interwiki.sql.
687
	 *
688
	 * @return Status
689
	 */
690
	public function populateInterwikiTable() {
691
		$status = $this->getConnection();
692
		if ( !$status->isOK() ) {
693
			return $status;
694
		}
695
		$this->db->selectDB( $this->getVar( 'wgDBname' ) );
696
697
		if ( $this->db->selectRow( 'interwiki', '*', [], __METHOD__ ) ) {
698
			$status->warning( 'config-install-interwiki-exists' );
699
700
			return $status;
701
		}
702
		global $IP;
703
		MediaWiki\suppressWarnings();
704
		$rows = file( "$IP/maintenance/interwiki.list",
705
			FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
706
		MediaWiki\restoreWarnings();
707
		$interwikis = [];
708
		if ( !$rows ) {
709
			return Status::newFatal( 'config-install-interwiki-list' );
710
		}
711
		foreach ( $rows as $row ) {
712
			$row = preg_replace( '/^\s*([^#]*?)\s*(#.*)?$/', '\\1', $row ); // strip comments - whee
713
			if ( $row == "" ) {
714
				continue;
715
			}
716
			$row .= "|";
717
			$interwikis[] = array_combine(
718
				[ 'iw_prefix', 'iw_url', 'iw_local', 'iw_api', 'iw_wikiid' ],
719
				explode( '|', $row )
720
			);
721
		}
722
		$this->db->insert( 'interwiki', $interwikis, __METHOD__ );
723
724
		return Status::newGood();
725
	}
726
727
	public function outputHandler( $string ) {
728
		return htmlspecialchars( $string );
729
	}
730
}
731