Tidy up return values of envChecks
[lhc/web/wiklou.git] / includes / installer / Installer.php
1 <?php
2 /**
3 * Base code for MediaWiki installer.
4 *
5 * @file
6 * @ingroup Deployment
7 */
8
9 /**
10 * This documentation group collects source code files with deployment functionality.
11 *
12 * @defgroup Deployment Deployment
13 */
14
15 /**
16 * Base installer class.
17 *
18 * This class provides the base for installation and update functionality
19 * for both MediaWiki core and extensions.
20 *
21 * @ingroup Deployment
22 * @since 1.17
23 */
24 abstract class Installer {
25
26 // This is the absolute minimum PHP version we can support
27 const MINIMUM_PHP_VERSION = '5.2.3';
28
29 /**
30 * @var array
31 */
32 protected $settings;
33
34 /**
35 * Cached DB installer instances, access using getDBInstaller().
36 *
37 * @var array
38 */
39 protected $dbInstallers = array();
40
41 /**
42 * Minimum memory size in MB.
43 *
44 * @var integer
45 */
46 protected $minMemorySize = 50;
47
48 /**
49 * Cached Title, used by parse().
50 *
51 * @var Title
52 */
53 protected $parserTitle;
54
55 /**
56 * Cached ParserOptions, used by parse().
57 *
58 * @var ParserOptions
59 */
60 protected $parserOptions;
61
62 /**
63 * Known database types. These correspond to the class names <type>Installer,
64 * and are also MediaWiki database types valid for $wgDBtype.
65 *
66 * To add a new type, create a <type>Installer class and a Database<type>
67 * class, and add a config-type-<type> message to MessagesEn.php.
68 *
69 * @var array
70 */
71 protected static $dbTypes = array(
72 'mysql',
73 'postgres',
74 'oracle',
75 'sqlite',
76 'ibm_db2',
77 );
78
79 /**
80 * A list of environment check methods called by doEnvironmentChecks().
81 * These may output warnings using showMessage(), and/or abort the
82 * installation process by returning false.
83 *
84 * @var array
85 */
86 protected $envChecks = array(
87 'envCheckDB',
88 'envCheckRegisterGlobals',
89 'envCheckBrokenXML',
90 'envCheckPHP531',
91 'envCheckMagicQuotes',
92 'envCheckMagicSybase',
93 'envCheckMbstring',
94 'envCheckZE1',
95 'envCheckSafeMode',
96 'envCheckXML',
97 'envCheckPCRE',
98 'envCheckMemory',
99 'envCheckCache',
100 'envCheckModSecurity',
101 'envCheckDiff3',
102 'envCheckGraphics',
103 'envCheckServer',
104 'envCheckPath',
105 'envCheckExtension',
106 'envCheckShellLocale',
107 'envCheckUploadsDirectory',
108 'envCheckLibicu',
109 'envCheckSuhosinMaxValueLength',
110 'envCheckCtype',
111 );
112
113 /**
114 * MediaWiki configuration globals that will eventually be passed through
115 * to LocalSettings.php. The names only are given here, the defaults
116 * typically come from DefaultSettings.php.
117 *
118 * @var array
119 */
120 protected $defaultVarNames = array(
121 'wgSitename',
122 'wgPasswordSender',
123 'wgLanguageCode',
124 'wgRightsIcon',
125 'wgRightsText',
126 'wgRightsUrl',
127 'wgMainCacheType',
128 'wgEnableEmail',
129 'wgEnableUserEmail',
130 'wgEnotifUserTalk',
131 'wgEnotifWatchlist',
132 'wgEmailAuthentication',
133 'wgDBtype',
134 'wgDiff3',
135 'wgImageMagickConvertCommand',
136 'IP',
137 'wgServer',
138 'wgScriptPath',
139 'wgScriptExtension',
140 'wgMetaNamespace',
141 'wgDeletedDirectory',
142 'wgEnableUploads',
143 'wgLogo',
144 'wgShellLocale',
145 'wgSecretKey',
146 'wgUseInstantCommons',
147 'wgUpgradeKey',
148 'wgDefaultSkin',
149 'wgResourceLoaderMaxQueryLength',
150 );
151
152 /**
153 * Variables that are stored alongside globals, and are used for any
154 * configuration of the installation process aside from the MediaWiki
155 * configuration. Map of names to defaults.
156 *
157 * @var array
158 */
159 protected $internalDefaults = array(
160 '_UserLang' => 'en',
161 '_Environment' => false,
162 '_CompiledDBs' => array(),
163 '_SafeMode' => false,
164 '_RaiseMemory' => false,
165 '_UpgradeDone' => false,
166 '_InstallDone' => false,
167 '_Caches' => array(),
168 '_InstallPassword' => '',
169 '_SameAccount' => true,
170 '_CreateDBAccount' => false,
171 '_NamespaceType' => 'site-name',
172 '_AdminName' => '', // will be set later, when the user selects language
173 '_AdminPassword' => '',
174 '_AdminPassword2' => '',
175 '_AdminEmail' => '',
176 '_Subscribe' => false,
177 '_SkipOptional' => 'continue',
178 '_RightsProfile' => 'wiki',
179 '_LicenseCode' => 'none',
180 '_CCDone' => false,
181 '_Extensions' => array(),
182 '_MemCachedServers' => '',
183 '_UpgradeKeySupplied' => false,
184 '_ExistingDBSettings' => false,
185 );
186
187 /**
188 * The actual list of installation steps. This will be initialized by getInstallSteps()
189 *
190 * @var array
191 */
192 private $installSteps = array();
193
194 /**
195 * Extra steps for installation, for things like DatabaseInstallers to modify
196 *
197 * @var array
198 */
199 protected $extraInstallSteps = array();
200
201 /**
202 * Known object cache types and the functions used to test for their existence.
203 *
204 * @var array
205 */
206 protected $objectCaches = array(
207 'xcache' => 'xcache_get',
208 'apc' => 'apc_fetch',
209 'wincache' => 'wincache_ucache_get'
210 );
211
212 /**
213 * User rights profiles.
214 *
215 * @var array
216 */
217 public $rightsProfiles = array(
218 'wiki' => array(),
219 'no-anon' => array(
220 '*' => array( 'edit' => false )
221 ),
222 'fishbowl' => array(
223 '*' => array(
224 'createaccount' => false,
225 'edit' => false,
226 ),
227 ),
228 'private' => array(
229 '*' => array(
230 'createaccount' => false,
231 'edit' => false,
232 'read' => false,
233 ),
234 ),
235 );
236
237 /**
238 * License types.
239 *
240 * @var array
241 */
242 public $licenses = array(
243 'cc-by' => array(
244 'url' => 'http://creativecommons.org/licenses/by/3.0/',
245 'icon' => '{$wgStylePath}/common/images/cc-by.png',
246 ),
247 'cc-by-sa' => array(
248 'url' => 'http://creativecommons.org/licenses/by-sa/3.0/',
249 'icon' => '{$wgStylePath}/common/images/cc-by-sa.png',
250 ),
251 'cc-by-nc-sa' => array(
252 'url' => 'http://creativecommons.org/licenses/by-nc-sa/3.0/',
253 'icon' => '{$wgStylePath}/common/images/cc-by-nc-sa.png',
254 ),
255 'cc-0' => array(
256 'url' => 'https://creativecommons.org/publicdomain/zero/1.0/',
257 'icon' => '{$wgStylePath}/common/images/cc-0.png',
258 ),
259 'pd' => array(
260 'url' => '',
261 'icon' => '{$wgStylePath}/common/images/public-domain.png',
262 ),
263 'gfdl' => array(
264 'url' => 'http://www.gnu.org/copyleft/fdl.html',
265 'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
266 ),
267 'none' => array(
268 'url' => '',
269 'icon' => '',
270 'text' => ''
271 ),
272 'cc-choose' => array(
273 // Details will be filled in by the selector.
274 'url' => '',
275 'icon' => '',
276 'text' => '',
277 ),
278 );
279
280 /**
281 * URL to mediawiki-announce subscription
282 */
283 protected $mediaWikiAnnounceUrl = 'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce';
284
285 /**
286 * Supported language codes for Mailman
287 */
288 protected $mediaWikiAnnounceLanguages = array(
289 'ca', 'cs', 'da', 'de', 'en', 'es', 'et', 'eu', 'fi', 'fr', 'hr', 'hu',
290 'it', 'ja', 'ko', 'lt', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru',
291 'sl', 'sr', 'sv', 'tr', 'uk'
292 );
293
294 /**
295 * UI interface for displaying a short message
296 * The parameters are like parameters to wfMsg().
297 * The messages will be in wikitext format, which will be converted to an
298 * output format such as HTML or text before being sent to the user.
299 * @param $msg
300 */
301 public abstract function showMessage( $msg /*, ... */ );
302
303 /**
304 * Same as showMessage(), but for displaying errors
305 * @param $msg
306 */
307 public abstract function showError( $msg /*, ... */ );
308
309 /**
310 * Show a message to the installing user by using a Status object
311 * @param $status Status
312 */
313 public abstract function showStatusMessage( Status $status );
314
315 /**
316 * Constructor, always call this from child classes.
317 */
318 public function __construct() {
319 global $wgExtensionMessagesFiles, $wgUser;
320
321 // Disable the i18n cache and LoadBalancer
322 Language::getLocalisationCache()->disableBackend();
323 LBFactory::disableBackend();
324
325 // Load the installer's i18n file.
326 $wgExtensionMessagesFiles['MediawikiInstaller'] =
327 dirname( __FILE__ ) . '/Installer.i18n.php';
328
329 // Having a user with id = 0 safeguards us from DB access via User::loadOptions().
330 $wgUser = User::newFromId( 0 );
331
332 $this->settings = $this->internalDefaults;
333
334 foreach ( $this->defaultVarNames as $var ) {
335 $this->settings[$var] = $GLOBALS[$var];
336 }
337
338 $compiledDBs = array();
339 foreach ( self::getDBTypes() as $type ) {
340 $installer = $this->getDBInstaller( $type );
341
342 if ( !$installer->isCompiled() ) {
343 continue;
344 }
345 $compiledDBs[] = $type;
346
347 $defaults = $installer->getGlobalDefaults();
348
349 foreach ( $installer->getGlobalNames() as $var ) {
350 if ( isset( $defaults[$var] ) ) {
351 $this->settings[$var] = $defaults[$var];
352 } else {
353 $this->settings[$var] = $GLOBALS[$var];
354 }
355 }
356 }
357 $this->setVar( '_CompiledDBs', $compiledDBs );
358
359 $this->parserTitle = Title::newFromText( 'Installer' );
360 $this->parserOptions = new ParserOptions; // language will be wrong :(
361 $this->parserOptions->setEditSection( false );
362 }
363
364 /**
365 * Get a list of known DB types.
366 *
367 * @return array
368 */
369 public static function getDBTypes() {
370 return self::$dbTypes;
371 }
372
373 /**
374 * Do initial checks of the PHP environment. Set variables according to
375 * the observed environment.
376 *
377 * It's possible that this may be called under the CLI SAPI, not the SAPI
378 * that the wiki will primarily run under. In that case, the subclass should
379 * initialise variables such as wgScriptPath, before calling this function.
380 *
381 * Under the web subclass, it can already be assumed that PHP 5+ is in use
382 * and that sessions are working.
383 *
384 * @return Status
385 */
386 public function doEnvironmentChecks() {
387 $phpVersion = phpversion();
388 if( version_compare( $phpVersion, self::MINIMUM_PHP_VERSION, '>=' ) ) {
389 $this->showMessage( 'config-env-php', $phpVersion );
390 $good = true;
391 } else {
392 $this->showMessage( 'config-env-php-toolow', $phpVersion, self::MINIMUM_PHP_VERSION );
393 $good = false;
394 }
395
396 if( $good ) {
397 foreach ( $this->envChecks as $check ) {
398 $status = $this->$check();
399 if ( $status === false ) {
400 $good = false;
401 }
402 }
403 }
404
405 $this->setVar( '_Environment', $good );
406
407 return $good ? Status::newGood() : Status::newFatal( 'config-env-bad' );
408 }
409
410 /**
411 * Set a MW configuration variable, or internal installer configuration variable.
412 *
413 * @param $name String
414 * @param $value Mixed
415 */
416 public function setVar( $name, $value ) {
417 $this->settings[$name] = $value;
418 }
419
420 /**
421 * Get an MW configuration variable, or internal installer configuration variable.
422 * The defaults come from $GLOBALS (ultimately DefaultSettings.php).
423 * Installer variables are typically prefixed by an underscore.
424 *
425 * @param $name String
426 * @param $default Mixed
427 *
428 * @return mixed
429 */
430 public function getVar( $name, $default = null ) {
431 if ( !isset( $this->settings[$name] ) ) {
432 return $default;
433 } else {
434 return $this->settings[$name];
435 }
436 }
437
438 /**
439 * Get an instance of DatabaseInstaller for the specified DB type.
440 *
441 * @param $type Mixed: DB installer for which is needed, false to use default.
442 *
443 * @return DatabaseInstaller
444 */
445 public function getDBInstaller( $type = false ) {
446 if ( !$type ) {
447 $type = $this->getVar( 'wgDBtype' );
448 }
449
450 $type = strtolower( $type );
451
452 if ( !isset( $this->dbInstallers[$type] ) ) {
453 $class = ucfirst( $type ). 'Installer';
454 $this->dbInstallers[$type] = new $class( $this );
455 }
456
457 return $this->dbInstallers[$type];
458 }
459
460 /**
461 * Determine if LocalSettings.php exists. If it does, return its variables,
462 * merged with those from AdminSettings.php, as an array.
463 *
464 * @return Array
465 */
466 public static function getExistingLocalSettings() {
467 global $IP;
468
469 wfSuppressWarnings();
470 $_lsExists = file_exists( "$IP/LocalSettings.php" );
471 wfRestoreWarnings();
472
473 if( !$_lsExists ) {
474 return false;
475 }
476 unset($_lsExists);
477
478 require( "$IP/includes/DefaultSettings.php" );
479 require( "$IP/LocalSettings.php" );
480 if ( file_exists( "$IP/AdminSettings.php" ) ) {
481 require( "$IP/AdminSettings.php" );
482 }
483 return get_defined_vars();
484 }
485
486 /**
487 * Get a fake password for sending back to the user in HTML.
488 * This is a security mechanism to avoid compromise of the password in the
489 * event of session ID compromise.
490 *
491 * @param $realPassword String
492 *
493 * @return string
494 */
495 public function getFakePassword( $realPassword ) {
496 return str_repeat( '*', strlen( $realPassword ) );
497 }
498
499 /**
500 * Set a variable which stores a password, except if the new value is a
501 * fake password in which case leave it as it is.
502 *
503 * @param $name String
504 * @param $value Mixed
505 */
506 public function setPassword( $name, $value ) {
507 if ( !preg_match( '/^\*+$/', $value ) ) {
508 $this->setVar( $name, $value );
509 }
510 }
511
512 /**
513 * On POSIX systems return the primary group of the webserver we're running under.
514 * On other systems just returns null.
515 *
516 * This is used to advice the user that he should chgrp his mw-config/data/images directory as the
517 * webserver user before he can install.
518 *
519 * Public because SqliteInstaller needs it, and doesn't subclass Installer.
520 *
521 * @return mixed
522 */
523 public static function maybeGetWebserverPrimaryGroup() {
524 if ( !function_exists( 'posix_getegid' ) || !function_exists( 'posix_getpwuid' ) ) {
525 # I don't know this, this isn't UNIX.
526 return null;
527 }
528
529 # posix_getegid() *not* getmygid() because we want the group of the webserver,
530 # not whoever owns the current script.
531 $gid = posix_getegid();
532 $getpwuid = posix_getpwuid( $gid );
533 $group = $getpwuid['name'];
534
535 return $group;
536 }
537
538 /**
539 * Convert wikitext $text to HTML.
540 *
541 * This is potentially error prone since many parser features require a complete
542 * installed MW database. The solution is to just not use those features when you
543 * write your messages. This appears to work well enough. Basic formatting and
544 * external links work just fine.
545 *
546 * But in case a translator decides to throw in a #ifexist or internal link or
547 * whatever, this function is guarded to catch the attempted DB access and to present
548 * some fallback text.
549 *
550 * @param $text String
551 * @param $lineStart Boolean
552 * @return String
553 */
554 public function parse( $text, $lineStart = false ) {
555 global $wgParser;
556
557 try {
558 $out = $wgParser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart );
559 $html = $out->getText();
560 } catch ( DBAccessError $e ) {
561 $html = '<!--DB access attempted during parse--> ' . htmlspecialchars( $text );
562
563 if ( !empty( $this->debug ) ) {
564 $html .= "<!--\n" . $e->getTraceAsString() . "\n-->";
565 }
566 }
567
568 return $html;
569 }
570
571 /**
572 * @return ParserOptions
573 */
574 public function getParserOptions() {
575 return $this->parserOptions;
576 }
577
578 public function disableLinkPopups() {
579 $this->parserOptions->setExternalLinkTarget( false );
580 }
581
582 public function restoreLinkPopups() {
583 global $wgExternalLinkTarget;
584 $this->parserOptions->setExternalLinkTarget( $wgExternalLinkTarget );
585 }
586
587 /**
588 * Install step which adds a row to the site_stats table with appropriate
589 * initial values.
590 *
591 * @param $installer DatabaseInstaller
592 *
593 * @return Status
594 */
595 public function populateSiteStats( DatabaseInstaller $installer ) {
596 $status = $installer->getConnection();
597 if ( !$status->isOK() ) {
598 return $status;
599 }
600 $status->value->insert( 'site_stats', array(
601 'ss_row_id' => 1,
602 'ss_total_views' => 0,
603 'ss_total_edits' => 0,
604 'ss_good_articles' => 0,
605 'ss_total_pages' => 0,
606 'ss_users' => 0,
607 'ss_images' => 0 ),
608 __METHOD__, 'IGNORE' );
609 return Status::newGood();
610 }
611
612 /**
613 * Exports all wg* variables stored by the installer into global scope.
614 */
615 public function exportVars() {
616 foreach ( $this->settings as $name => $value ) {
617 if ( substr( $name, 0, 2 ) == 'wg' ) {
618 $GLOBALS[$name] = $value;
619 }
620 }
621 }
622
623 /**
624 * Environment check for DB types.
625 * @return bool
626 */
627 protected function envCheckDB() {
628 global $wgLang;
629
630 $allNames = array();
631
632 foreach ( self::getDBTypes() as $name ) {
633 $allNames[] = wfMsg( "config-type-$name" );
634 }
635
636 // cache initially available databases to make sure that everything will be displayed correctly
637 // after a refresh on env checks page
638 $databases = $this->getVar( '_CompiledDBs-preFilter' );
639 if ( !$databases ) {
640 $databases = $this->getVar( '_CompiledDBs' );
641 $this->setVar( '_CompiledDBs-preFilter', $databases );
642 }
643
644 $databases = array_flip ( $databases );
645 foreach ( array_keys( $databases ) as $db ) {
646 $installer = $this->getDBInstaller( $db );
647 $status = $installer->checkPrerequisites();
648 if ( !$status->isGood() ) {
649 $this->showStatusMessage( $status );
650 }
651 if ( !$status->isOK() ) {
652 unset( $databases[$db] );
653 }
654 }
655 $databases = array_flip( $databases );
656 if ( !$databases ) {
657 $this->showError( 'config-no-db', $wgLang->commaList( $allNames ) );
658 // @todo FIXME: This only works for the web installer!
659 return false;
660 }
661 $this->setVar( '_CompiledDBs', $databases );
662 return true;
663 }
664
665 /**
666 * Environment check for register_globals.
667 */
668 protected function envCheckRegisterGlobals() {
669 if( wfIniGetBool( 'register_globals' ) ) {
670 $this->showMessage( 'config-register-globals' );
671 }
672 }
673
674 /**
675 * Some versions of libxml+PHP break < and > encoding horribly
676 * @return bool
677 */
678 protected function envCheckBrokenXML() {
679 $test = new PhpXmlBugTester();
680 if ( !$test->ok ) {
681 $this->showError( 'config-brokenlibxml' );
682 return false;
683 }
684 return true;
685 }
686
687 /**
688 * Test PHP (probably 5.3.1, but it could regress again) to make sure that
689 * reference parameters to __call() are not converted to null
690 * @return bool
691 */
692 protected function envCheckPHP531() {
693 $test = new PhpRefCallBugTester;
694 $test->execute();
695 if ( !$test->ok ) {
696 $this->showError( 'config-using531', phpversion() );
697 return false;
698 }
699 return true;
700 }
701
702 /**
703 * Environment check for magic_quotes_runtime.
704 * @return bool
705 */
706 protected function envCheckMagicQuotes() {
707 if( wfIniGetBool( "magic_quotes_runtime" ) ) {
708 $this->showError( 'config-magic-quotes-runtime' );
709 return false;
710 }
711 return true;
712 }
713
714 /**
715 * Environment check for magic_quotes_sybase.
716 * @return bool
717 */
718 protected function envCheckMagicSybase() {
719 if ( wfIniGetBool( 'magic_quotes_sybase' ) ) {
720 $this->showError( 'config-magic-quotes-sybase' );
721 return false;
722 }
723 return true;
724 }
725
726 /**
727 * Environment check for mbstring.func_overload.
728 * @return bool
729 */
730 protected function envCheckMbstring() {
731 if ( wfIniGetBool( 'mbstring.func_overload' ) ) {
732 $this->showError( 'config-mbstring' );
733 return false;
734 }
735 return true;
736 }
737
738 /**
739 * Environment check for zend.ze1_compatibility_mode.
740 * @return bool
741 */
742 protected function envCheckZE1() {
743 if ( wfIniGetBool( 'zend.ze1_compatibility_mode' ) ) {
744 $this->showError( 'config-ze1' );
745 return false;
746 }
747 return true;
748 }
749
750 /**
751 * Environment check for safe_mode.
752 * @return bool
753 */
754 protected function envCheckSafeMode() {
755 if ( wfIniGetBool( 'safe_mode' ) ) {
756 $this->setVar( '_SafeMode', true );
757 $this->showMessage( 'config-safe-mode' );
758 }
759 return true;
760 }
761
762 /**
763 * Environment check for the XML module.
764 * @return bool
765 */
766 protected function envCheckXML() {
767 if ( !function_exists( "utf8_encode" ) ) {
768 $this->showError( 'config-xml-bad' );
769 return false;
770 }
771 return true;
772 }
773
774 /**
775 * Environment check for the PCRE module.
776 * @return bool
777 */
778 protected function envCheckPCRE() {
779 if ( !function_exists( 'preg_match' ) ) {
780 $this->showError( 'config-pcre' );
781 return false;
782 }
783 wfSuppressWarnings();
784 $regexd = preg_replace( '/[\x{0430}-\x{04FF}]/iu', '', '-АБВГД-' );
785 wfRestoreWarnings();
786 if ( $regexd != '--' ) {
787 $this->showError( 'config-pcre-no-utf8' );
788 return false;
789 }
790 return true;
791 }
792
793 /**
794 * Environment check for available memory.
795 * @return bool
796 */
797 protected function envCheckMemory() {
798 $limit = ini_get( 'memory_limit' );
799
800 if ( !$limit || $limit == -1 ) {
801 return true;
802 }
803
804 $n = wfShorthandToInteger( $limit );
805
806 if( $n < $this->minMemorySize * 1024 * 1024 ) {
807 $newLimit = "{$this->minMemorySize}M";
808
809 if( ini_set( "memory_limit", $newLimit ) === false ) {
810 $this->showMessage( 'config-memory-bad', $limit );
811 } else {
812 $this->showMessage( 'config-memory-raised', $limit, $newLimit );
813 $this->setVar( '_RaiseMemory', true );
814 }
815 }
816 return true;
817 }
818
819 /**
820 * Environment check for compiled object cache types.
821 */
822 protected function envCheckCache() {
823 $caches = array();
824 foreach ( $this->objectCaches as $name => $function ) {
825 if ( function_exists( $function ) ) {
826 if ( $name == 'xcache' && !wfIniGetBool( 'xcache.var_size' ) ) {
827 continue;
828 }
829 $caches[$name] = true;
830 }
831 }
832
833 if ( !$caches ) {
834 $this->showMessage( 'config-no-cache' );
835 }
836
837 $this->setVar( '_Caches', $caches );
838 }
839
840 /**
841 * Scare user to death if they have mod_security
842 * @return bool
843 */
844 protected function envCheckModSecurity() {
845 if ( self::apacheModulePresent( 'mod_security' ) ) {
846 $this->showMessage( 'config-mod-security' );
847 }
848 return true;
849 }
850
851 /**
852 * Search for GNU diff3.
853 * @return bool
854 */
855 protected function envCheckDiff3() {
856 $names = array( "gdiff3", "diff3", "diff3.exe" );
857 $versionInfo = array( '$1 --version 2>&1', 'GNU diffutils' );
858
859 $diff3 = self::locateExecutableInDefaultPaths( $names, $versionInfo );
860
861 if ( $diff3 ) {
862 $this->setVar( 'wgDiff3', $diff3 );
863 } else {
864 $this->setVar( 'wgDiff3', false );
865 $this->showMessage( 'config-diff3-bad' );
866 }
867 return true;
868 }
869
870 /**
871 * Environment check for ImageMagick and GD.
872 * @return bool
873 */
874 protected function envCheckGraphics() {
875 $names = array( wfIsWindows() ? 'convert.exe' : 'convert' );
876 $convert = self::locateExecutableInDefaultPaths( $names, array( '$1 -version', 'ImageMagick' ) );
877
878 $this->setVar( 'wgImageMagickConvertCommand', '' );
879 if ( $convert ) {
880 $this->setVar( 'wgImageMagickConvertCommand', $convert );
881 $this->showMessage( 'config-imagemagick', $convert );
882 return true;
883 } elseif ( function_exists( 'imagejpeg' ) ) {
884 $this->showMessage( 'config-gd' );
885
886 } else {
887 $this->showMessage( 'config-no-scaling' );
888 }
889 return true;
890 }
891
892 /**
893 * Environment check for the server hostname.
894 */
895 protected function envCheckServer() {
896 $server = $this->envGetDefaultServer();
897 $this->showMessage( 'config-using-server', $server );
898 $this->setVar( 'wgServer', $server );
899 return true;
900 }
901
902 /**
903 * Helper function to be called from envCheckServer()
904 * @return String
905 */
906 protected abstract function envGetDefaultServer();
907
908 /**
909 * Environment check for setting $IP and $wgScriptPath.
910 * @return bool
911 */
912 protected function envCheckPath() {
913 global $IP;
914 $IP = dirname( dirname( dirname( __FILE__ ) ) );
915 $this->setVar( 'IP', $IP );
916
917 $this->showMessage( 'config-using-uri', $this->getVar( 'wgServer' ), $this->getVar( 'wgScriptPath' ) );
918 return true;
919 }
920
921 /**
922 * Environment check for setting the preferred PHP file extension.
923 */
924 protected function envCheckExtension() {
925 // @todo FIXME: Detect this properly
926 if ( defined( 'MW_INSTALL_PHP5_EXT' ) ) {
927 $ext = 'php5';
928 } else {
929 $ext = 'php';
930 }
931 $this->setVar( 'wgScriptExtension', ".$ext" );
932 return true;
933 }
934
935 /**
936 * TODO: document
937 * @return bool
938 */
939 protected function envCheckShellLocale() {
940 $os = php_uname( 's' );
941 $supported = array( 'Linux', 'SunOS', 'HP-UX', 'Darwin' ); # Tested these
942
943 if ( !in_array( $os, $supported ) ) {
944 return true;
945 }
946
947 # Get a list of available locales.
948 $ret = false;
949 $lines = wfShellExec( '/usr/bin/locale -a', $ret );
950
951 if ( $ret ) {
952 return true;
953 }
954
955 $lines = wfArrayMap( 'trim', explode( "\n", $lines ) );
956 $candidatesByLocale = array();
957 $candidatesByLang = array();
958
959 foreach ( $lines as $line ) {
960 if ( $line === '' ) {
961 continue;
962 }
963
964 if ( !preg_match( '/^([a-zA-Z]+)(_[a-zA-Z]+|)\.(utf8|UTF-8)(@[a-zA-Z_]*|)$/i', $line, $m ) ) {
965 continue;
966 }
967
968 list( $all, $lang, $territory, $charset, $modifier ) = $m;
969
970 $candidatesByLocale[$m[0]] = $m;
971 $candidatesByLang[$lang][] = $m;
972 }
973
974 # Try the current value of LANG.
975 if ( isset( $candidatesByLocale[ getenv( 'LANG' ) ] ) ) {
976 $this->setVar( 'wgShellLocale', getenv( 'LANG' ) );
977 return true;
978 }
979
980 # Try the most common ones.
981 $commonLocales = array( 'en_US.UTF-8', 'en_US.utf8', 'de_DE.UTF-8', 'de_DE.utf8' );
982 foreach ( $commonLocales as $commonLocale ) {
983 if ( isset( $candidatesByLocale[$commonLocale] ) ) {
984 $this->setVar( 'wgShellLocale', $commonLocale );
985 return true;
986 }
987 }
988
989 # Is there an available locale in the Wiki's language?
990 $wikiLang = $this->getVar( 'wgLanguageCode' );
991
992 if ( isset( $candidatesByLang[$wikiLang] ) ) {
993 $m = reset( $candidatesByLang[$wikiLang] );
994 $this->setVar( 'wgShellLocale', $m[0] );
995 return true;
996 }
997
998 # Are there any at all?
999 if ( count( $candidatesByLocale ) ) {
1000 $m = reset( $candidatesByLocale );
1001 $this->setVar( 'wgShellLocale', $m[0] );
1002 return true;
1003 }
1004
1005 # Give up.
1006 return true;
1007 }
1008
1009 /**
1010 * TODO: document
1011 * @return bool
1012 */
1013 protected function envCheckUploadsDirectory() {
1014 global $IP;
1015
1016 $dir = $IP . '/images/';
1017 $url = $this->getVar( 'wgServer' ) . $this->getVar( 'wgScriptPath' ) . '/images/';
1018 $safe = !$this->dirIsExecutable( $dir, $url );
1019
1020 if ( !$safe ) {
1021 $this->showMessage( 'config-uploads-not-safe', $dir );
1022 }
1023 return true;
1024 }
1025
1026 /**
1027 * Checks if suhosin.get.max_value_length is set, and if so, sets
1028 * $wgResourceLoaderMaxQueryLength to that value in the generated
1029 * LocalSettings file
1030 * @return bool
1031 */
1032 protected function envCheckSuhosinMaxValueLength() {
1033 $maxValueLength = ini_get( 'suhosin.get.max_value_length' );
1034 if ( $maxValueLength > 0 ) {
1035 if( $maxValueLength < 1024 ) {
1036 # Only warn if the value is below the sane 1024
1037 $this->showMessage( 'config-suhosin-max-value-length', $maxValueLength );
1038 }
1039 } else {
1040 $maxValueLength = -1;
1041 }
1042 $this->setVar( 'wgResourceLoaderMaxQueryLength', $maxValueLength );
1043 return true;
1044 }
1045
1046 /**
1047 * Convert a hex string representing a Unicode code point to that code point.
1048 * @param $c String
1049 * @return string
1050 */
1051 protected function unicodeChar( $c ) {
1052 $c = hexdec($c);
1053 if ($c <= 0x7F) {
1054 return chr($c);
1055 } elseif ($c <= 0x7FF) {
1056 return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F);
1057 } elseif ($c <= 0xFFFF) {
1058 return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F)
1059 . chr(0x80 | $c & 0x3F);
1060 } elseif ($c <= 0x10FFFF) {
1061 return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F)
1062 . chr(0x80 | $c >> 6 & 0x3F)
1063 . chr(0x80 | $c & 0x3F);
1064 } else {
1065 return false;
1066 }
1067 }
1068
1069
1070 /**
1071 * Check the libicu version
1072 */
1073 protected function envCheckLibicu() {
1074 $utf8 = function_exists( 'utf8_normalize' );
1075 $intl = function_exists( 'normalizer_normalize' );
1076
1077 /**
1078 * This needs to be updated something that the latest libicu
1079 * will properly normalize. This normalization was found at
1080 * http://www.unicode.org/versions/Unicode5.2.0/#Character_Additions
1081 * Note that we use the hex representation to create the code
1082 * points in order to avoid any Unicode-destroying during transit.
1083 */
1084 $not_normal_c = $this->unicodeChar("FA6C");
1085 $normal_c = $this->unicodeChar("242EE");
1086
1087 $useNormalizer = 'php';
1088 $needsUpdate = false;
1089
1090 /**
1091 * We're going to prefer the pecl extension here unless
1092 * utf8_normalize is more up to date.
1093 */
1094 if( $utf8 ) {
1095 $useNormalizer = 'utf8';
1096 $utf8 = utf8_normalize( $not_normal_c, UtfNormal::UNORM_NFC );
1097 if ( $utf8 !== $normal_c ) $needsUpdate = true;
1098 }
1099 if( $intl ) {
1100 $useNormalizer = 'intl';
1101 $intl = normalizer_normalize( $not_normal_c, Normalizer::FORM_C );
1102 if ( $intl !== $normal_c ) $needsUpdate = true;
1103 }
1104
1105 // Uses messages 'config-unicode-using-php', 'config-unicode-using-utf8', 'config-unicode-using-intl'
1106 if( $useNormalizer === 'php' ) {
1107 $this->showMessage( 'config-unicode-pure-php-warning' );
1108 } else {
1109 $this->showMessage( 'config-unicode-using-' . $useNormalizer );
1110 if( $needsUpdate ) {
1111 $this->showMessage( 'config-unicode-update-warning' );
1112 }
1113 }
1114 }
1115
1116 /**
1117 * @return bool
1118 */
1119 protected function envCheckCtype() {
1120 if ( !function_exists( 'ctype_digit' ) ) {
1121 $this->showError( 'config-ctype' );
1122 return false;
1123 }
1124 return true;
1125 }
1126
1127 /**
1128 * Get an array of likely places we can find executables. Check a bunch
1129 * of known Unix-like defaults, as well as the PATH environment variable
1130 * (which should maybe make it work for Windows?)
1131 *
1132 * @return Array
1133 */
1134 protected static function getPossibleBinPaths() {
1135 return array_merge(
1136 array( '/usr/bin', '/usr/local/bin', '/opt/csw/bin',
1137 '/usr/gnu/bin', '/usr/sfw/bin', '/sw/bin', '/opt/local/bin' ),
1138 explode( PATH_SEPARATOR, getenv( 'PATH' ) )
1139 );
1140 }
1141
1142 /**
1143 * Search a path for any of the given executable names. Returns the
1144 * executable name if found. Also checks the version string returned
1145 * by each executable.
1146 *
1147 * Used only by environment checks.
1148 *
1149 * @param $path String: path to search
1150 * @param $names Array of executable names
1151 * @param $versionInfo Boolean false or array with two members:
1152 * 0 => Command to run for version check, with $1 for the full executable name
1153 * 1 => String to compare the output with
1154 *
1155 * If $versionInfo is not false, only executables with a version
1156 * matching $versionInfo[1] will be returned.
1157 * @return bool|string
1158 */
1159 public static function locateExecutable( $path, $names, $versionInfo = false ) {
1160 if ( !is_array( $names ) ) {
1161 $names = array( $names );
1162 }
1163
1164 foreach ( $names as $name ) {
1165 $command = $path . DIRECTORY_SEPARATOR . $name;
1166
1167 wfSuppressWarnings();
1168 $file_exists = file_exists( $command );
1169 wfRestoreWarnings();
1170
1171 if ( $file_exists ) {
1172 if ( !$versionInfo ) {
1173 return $command;
1174 }
1175
1176 $file = str_replace( '$1', wfEscapeShellArg( $command ), $versionInfo[0] );
1177 if ( strstr( wfShellExec( $file ), $versionInfo[1] ) !== false ) {
1178 return $command;
1179 }
1180 }
1181 }
1182 return false;
1183 }
1184
1185 /**
1186 * Same as locateExecutable(), but checks in getPossibleBinPaths() by default
1187 * @see locateExecutable()
1188 * @param $names
1189 * @param $versionInfo bool
1190 * @return bool|string
1191 */
1192 public static function locateExecutableInDefaultPaths( $names, $versionInfo = false ) {
1193 foreach( self::getPossibleBinPaths() as $path ) {
1194 $exe = self::locateExecutable( $path, $names, $versionInfo );
1195 if( $exe !== false ) {
1196 return $exe;
1197 }
1198 }
1199 return false;
1200 }
1201
1202 /**
1203 * Checks if scripts located in the given directory can be executed via the given URL.
1204 *
1205 * Used only by environment checks.
1206 * @param $dir string
1207 * @param $url string
1208 * @return bool|int|string
1209 */
1210 public function dirIsExecutable( $dir, $url ) {
1211 $scriptTypes = array(
1212 'php' => array(
1213 "<?php echo 'ex' . 'ec';",
1214 "#!/var/env php5\n<?php echo 'ex' . 'ec';",
1215 ),
1216 );
1217
1218 // it would be good to check other popular languages here, but it'll be slow.
1219
1220 wfSuppressWarnings();
1221
1222 foreach ( $scriptTypes as $ext => $contents ) {
1223 foreach ( $contents as $source ) {
1224 $file = 'exectest.' . $ext;
1225
1226 if ( !file_put_contents( $dir . $file, $source ) ) {
1227 break;
1228 }
1229
1230 try {
1231 $text = Http::get( $url . $file, array( 'timeout' => 3 ) );
1232 }
1233 catch( MWException $e ) {
1234 // Http::get throws with allow_url_fopen = false and no curl extension.
1235 $text = null;
1236 }
1237 unlink( $dir . $file );
1238
1239 if ( $text == 'exec' ) {
1240 wfRestoreWarnings();
1241 return $ext;
1242 }
1243 }
1244 }
1245
1246 wfRestoreWarnings();
1247
1248 return false;
1249 }
1250
1251 /**
1252 * Checks for presence of an Apache module. Works only if PHP is running as an Apache module, too.
1253 *
1254 * @param $moduleName String: Name of module to check.
1255 * @return bool
1256 */
1257 public static function apacheModulePresent( $moduleName ) {
1258 if ( function_exists( 'apache_get_modules' ) && in_array( $moduleName, apache_get_modules() ) ) {
1259 return true;
1260 }
1261 // try it the hard way
1262 ob_start();
1263 phpinfo( INFO_MODULES );
1264 $info = ob_get_clean();
1265 return strpos( $info, $moduleName ) !== false;
1266 }
1267
1268 /**
1269 * ParserOptions are constructed before we determined the language, so fix it
1270 *
1271 * @param $lang Language
1272 */
1273 public function setParserLanguage( $lang ) {
1274 $this->parserOptions->setTargetLanguage( $lang );
1275 $this->parserOptions->setUserLang( $lang );
1276 }
1277
1278 /**
1279 * Overridden by WebInstaller to provide lastPage parameters.
1280 * @param $page string
1281 * @return string
1282 */
1283 protected function getDocUrl( $page ) {
1284 return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
1285 }
1286
1287 /**
1288 * Finds extensions that follow the format /extensions/Name/Name.php,
1289 * and returns an array containing the value for 'Name' for each found extension.
1290 *
1291 * @return array
1292 */
1293 public function findExtensions() {
1294 if( $this->getVar( 'IP' ) === null ) {
1295 return false;
1296 }
1297
1298 $exts = array();
1299 $extDir = $this->getVar( 'IP' ) . '/extensions';
1300 $dh = opendir( $extDir );
1301
1302 while ( ( $file = readdir( $dh ) ) !== false ) {
1303 if( !is_dir( "$extDir/$file" ) ) {
1304 continue;
1305 }
1306 if( file_exists( "$extDir/$file/$file.php" ) ) {
1307 $exts[] = $file;
1308 }
1309 }
1310 natcasesort( $exts );
1311
1312 return $exts;
1313 }
1314
1315 /**
1316 * Installs the auto-detected extensions.
1317 *
1318 * @return Status
1319 */
1320 protected function includeExtensions() {
1321 global $IP;
1322 $exts = $this->getVar( '_Extensions' );
1323 $IP = $this->getVar( 'IP' );
1324
1325 /**
1326 * We need to include DefaultSettings before including extensions to avoid
1327 * warnings about unset variables. However, the only thing we really
1328 * want here is $wgHooks['LoadExtensionSchemaUpdates']. This won't work
1329 * if the extension has hidden hook registration in $wgExtensionFunctions,
1330 * but we're not opening that can of worms
1331 * @see https://bugzilla.wikimedia.org/show_bug.cgi?id=26857
1332 */
1333 global $wgAutoloadClasses;
1334 $wgAutoloadClasses = array();
1335
1336 require( "$IP/includes/DefaultSettings.php" );
1337
1338 foreach( $exts as $e ) {
1339 require_once( "$IP/extensions/$e/$e.php" );
1340 }
1341
1342 $hooksWeWant = isset( $wgHooks['LoadExtensionSchemaUpdates'] ) ?
1343 $wgHooks['LoadExtensionSchemaUpdates'] : array();
1344
1345 // Unset everyone else's hooks. Lord knows what someone might be doing
1346 // in ParserFirstCallInit (see bug 27171)
1347 $GLOBALS['wgHooks'] = array( 'LoadExtensionSchemaUpdates' => $hooksWeWant );
1348
1349 return Status::newGood();
1350 }
1351
1352 /**
1353 * Get an array of install steps. Should always be in the format of
1354 * array(
1355 * 'name' => 'someuniquename',
1356 * 'callback' => array( $obj, 'method' ),
1357 * )
1358 * There must be a config-install-$name message defined per step, which will
1359 * be shown on install.
1360 *
1361 * @param $installer DatabaseInstaller so we can make callbacks
1362 * @return array
1363 */
1364 protected function getInstallSteps( DatabaseInstaller $installer ) {
1365 $coreInstallSteps = array(
1366 array( 'name' => 'database', 'callback' => array( $installer, 'setupDatabase' ) ),
1367 array( 'name' => 'tables', 'callback' => array( $installer, 'createTables' ) ),
1368 array( 'name' => 'interwiki', 'callback' => array( $installer, 'populateInterwikiTable' ) ),
1369 array( 'name' => 'stats', 'callback' => array( $this, 'populateSiteStats' ) ),
1370 array( 'name' => 'keys', 'callback' => array( $this, 'generateKeys' ) ),
1371 array( 'name' => 'sysop', 'callback' => array( $this, 'createSysop' ) ),
1372 array( 'name' => 'mainpage', 'callback' => array( $this, 'createMainpage' ) ),
1373 );
1374
1375 // Build the array of install steps starting from the core install list,
1376 // then adding any callbacks that wanted to attach after a given step
1377 foreach( $coreInstallSteps as $step ) {
1378 $this->installSteps[] = $step;
1379 if( isset( $this->extraInstallSteps[ $step['name'] ] ) ) {
1380 $this->installSteps = array_merge(
1381 $this->installSteps,
1382 $this->extraInstallSteps[ $step['name'] ]
1383 );
1384 }
1385 }
1386
1387 // Prepend any steps that want to be at the beginning
1388 if( isset( $this->extraInstallSteps['BEGINNING'] ) ) {
1389 $this->installSteps = array_merge(
1390 $this->extraInstallSteps['BEGINNING'],
1391 $this->installSteps
1392 );
1393 }
1394
1395 // Extensions should always go first, chance to tie into hooks and such
1396 if( count( $this->getVar( '_Extensions' ) ) ) {
1397 array_unshift( $this->installSteps,
1398 array( 'name' => 'extensions', 'callback' => array( $this, 'includeExtensions' ) )
1399 );
1400 $this->installSteps[] = array(
1401 'name' => 'extension-tables',
1402 'callback' => array( $installer, 'createExtensionTables' )
1403 );
1404 }
1405 return $this->installSteps;
1406 }
1407
1408 /**
1409 * Actually perform the installation.
1410 *
1411 * @param $startCB Array A callback array for the beginning of each step
1412 * @param $endCB Array A callback array for the end of each step
1413 *
1414 * @return Array of Status objects
1415 */
1416 public function performInstallation( $startCB, $endCB ) {
1417 $installResults = array();
1418 $installer = $this->getDBInstaller();
1419 $installer->preInstall();
1420 $steps = $this->getInstallSteps( $installer );
1421 foreach( $steps as $stepObj ) {
1422 $name = $stepObj['name'];
1423 call_user_func_array( $startCB, array( $name ) );
1424
1425 // Perform the callback step
1426 $status = call_user_func( $stepObj['callback'], $installer );
1427
1428 // Output and save the results
1429 call_user_func( $endCB, $name, $status );
1430 $installResults[$name] = $status;
1431
1432 // If we've hit some sort of fatal, we need to bail.
1433 // Callback already had a chance to do output above.
1434 if( !$status->isOk() ) {
1435 break;
1436 }
1437 }
1438 if( $status->isOk() ) {
1439 $this->setVar( '_InstallDone', true );
1440 }
1441 return $installResults;
1442 }
1443
1444 /**
1445 * Generate $wgSecretKey. Will warn if we had to use mt_rand() instead of
1446 * /dev/urandom
1447 *
1448 * @return Status
1449 */
1450 public function generateKeys() {
1451 $keys = array( 'wgSecretKey' => 64 );
1452 if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) {
1453 $keys['wgUpgradeKey'] = 16;
1454 }
1455 return $this->doGenerateKeys( $keys );
1456 }
1457
1458 /**
1459 * Generate a secret value for variables using either
1460 * /dev/urandom or mt_rand(). Produce a warning in the later case.
1461 *
1462 * @param $keys Array
1463 * @return Status
1464 */
1465 protected function doGenerateKeys( $keys ) {
1466 $status = Status::newGood();
1467
1468 wfSuppressWarnings();
1469 $file = fopen( "/dev/urandom", "r" );
1470 wfRestoreWarnings();
1471
1472 foreach ( $keys as $name => $length ) {
1473 if ( $file ) {
1474 $secretKey = bin2hex( fread( $file, $length / 2 ) );
1475 } else {
1476 $secretKey = '';
1477
1478 for ( $i = 0; $i < $length / 8; $i++ ) {
1479 $secretKey .= dechex( mt_rand( 0, 0x7fffffff ) );
1480 }
1481 }
1482
1483 $this->setVar( $name, $secretKey );
1484 }
1485
1486 if ( $file ) {
1487 fclose( $file );
1488 } else {
1489 $names = array_keys ( $keys );
1490 $names = preg_replace( '/^(.*)$/', '\$$1', $names );
1491 global $wgLang;
1492 $status->warning( 'config-insecure-keys', $wgLang->listToText( $names ), count( $names ) );
1493 }
1494
1495 return $status;
1496 }
1497
1498 /**
1499 * Create the first user account, grant it sysop and bureaucrat rights
1500 *
1501 * @return Status
1502 */
1503 protected function createSysop() {
1504 $name = $this->getVar( '_AdminName' );
1505 $user = User::newFromName( $name );
1506
1507 if ( !$user ) {
1508 // We should've validated this earlier anyway!
1509 return Status::newFatal( 'config-admin-error-user', $name );
1510 }
1511
1512 if ( $user->idForName() == 0 ) {
1513 $user->addToDatabase();
1514
1515 try {
1516 $user->setPassword( $this->getVar( '_AdminPassword' ) );
1517 } catch( PasswordError $pwe ) {
1518 return Status::newFatal( 'config-admin-error-password', $name, $pwe->getMessage() );
1519 }
1520
1521 $user->addGroup( 'sysop' );
1522 $user->addGroup( 'bureaucrat' );
1523 if( $this->getVar( '_AdminEmail' ) ) {
1524 $user->setEmail( $this->getVar( '_AdminEmail' ) );
1525 }
1526 $user->saveSettings();
1527
1528 // Update user count
1529 $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
1530 $ssUpdate->doUpdate();
1531 }
1532 $status = Status::newGood();
1533
1534 if( $this->getVar( '_Subscribe' ) && $this->getVar( '_AdminEmail' ) ) {
1535 $this->subscribeToMediaWikiAnnounce( $status );
1536 }
1537
1538 return $status;
1539 }
1540
1541 /**
1542 * @param $s Status
1543 */
1544 private function subscribeToMediaWikiAnnounce( Status $s ) {
1545 $params = array(
1546 'email' => $this->getVar( '_AdminEmail' ),
1547 'language' => 'en',
1548 'digest' => 0
1549 );
1550
1551 // Mailman doesn't support as many languages as we do, so check to make
1552 // sure their selected language is available
1553 $myLang = $this->getVar( '_UserLang' );
1554 if( in_array( $myLang, $this->mediaWikiAnnounceLanguages ) ) {
1555 $myLang = $myLang == 'pt-br' ? 'pt_BR' : $myLang; // rewrite to Mailman's pt_BR
1556 $params['language'] = $myLang;
1557 }
1558
1559 if( MWHttpRequest::canMakeRequests() ) {
1560 $res = MWHttpRequest::factory( $this->mediaWikiAnnounceUrl,
1561 array( 'method' => 'POST', 'postData' => $params ) )->execute();
1562 if( !$res->isOK() ) {
1563 $s->warning( 'config-install-subscribe-fail', $res->getMessage() );
1564 }
1565 } else {
1566 $s->warning( 'config-install-subscribe-notpossible' );
1567 }
1568 }
1569
1570 /**
1571 * Insert Main Page with default content.
1572 *
1573 * @param $installer DatabaseInstaller
1574 * @return Status
1575 */
1576 protected function createMainpage( DatabaseInstaller $installer ) {
1577 $status = Status::newGood();
1578 try {
1579 $page = WikiPage::factory( Title::newMainPage() );
1580 $page->doEdit( wfMsgForContent( 'mainpagetext' ) . "\n\n" .
1581 wfMsgForContent( 'mainpagedocfooter' ),
1582 '',
1583 EDIT_NEW,
1584 false,
1585 User::newFromName( 'MediaWiki default' ) );
1586 } catch (MWException $e) {
1587 //using raw, because $wgShowExceptionDetails can not be set yet
1588 $status->fatal( 'config-install-mainpage-failed', $e->getMessage() );
1589 }
1590
1591 return $status;
1592 }
1593
1594 /**
1595 * Override the necessary bits of the config to run an installation.
1596 */
1597 public static function overrideConfig() {
1598 define( 'MW_NO_SESSION', 1 );
1599
1600 // Don't access the database
1601 $GLOBALS['wgUseDatabaseMessages'] = false;
1602 // Debug-friendly
1603 $GLOBALS['wgShowExceptionDetails'] = true;
1604 // Don't break forms
1605 $GLOBALS['wgExternalLinkTarget'] = '_blank';
1606
1607 // Extended debugging
1608 $GLOBALS['wgShowSQLErrors'] = true;
1609 $GLOBALS['wgShowDBErrorBacktrace'] = true;
1610
1611 // Allow multiple ob_flush() calls
1612 $GLOBALS['wgDisableOutputCompression'] = true;
1613
1614 // Use a sensible cookie prefix (not my_wiki)
1615 $GLOBALS['wgCookiePrefix'] = 'mw_installer';
1616
1617 // Some of the environment checks make shell requests, remove limits
1618 $GLOBALS['wgMaxShellMemory'] = 0;
1619 }
1620
1621 /**
1622 * Add an installation step following the given step.
1623 *
1624 * @param $callback Array A valid installation callback array, in this form:
1625 * array( 'name' => 'some-unique-name', 'callback' => array( $obj, 'function' ) );
1626 * @param $findStep String the step to find. Omit to put the step at the beginning
1627 */
1628 public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
1629 $this->extraInstallSteps[$findStep][] = $callback;
1630 }
1631
1632 /**
1633 * Disable the time limit for execution.
1634 * Some long-running pages (Install, Upgrade) will want to do this
1635 */
1636 protected function disableTimeLimit() {
1637 wfSuppressWarnings();
1638 set_time_limit( 0 );
1639 wfRestoreWarnings();
1640 }
1641 }