MediaWiki  REL1_20
Installer.php
Go to the documentation of this file.
00001 <?php
00039 abstract class Installer {
00040 
00041         // This is the absolute minimum PHP version we can support
00042         const MINIMUM_PHP_VERSION = '5.3.2';
00043 
00047         protected $settings;
00048 
00054         protected $dbInstallers = array();
00055 
00061         protected $minMemorySize = 50;
00062 
00068         protected $parserTitle;
00069 
00075         protected $parserOptions;
00076 
00086         protected static $dbTypes = array(
00087                 'mysql',
00088                 'postgres',
00089                 'oracle',
00090                 'sqlite',
00091                 'ibm_db2',
00092         );
00093 
00101         protected $envChecks = array(
00102                 'envCheckDB',
00103                 'envCheckRegisterGlobals',
00104                 'envCheckBrokenXML',
00105                 'envCheckPHP531',
00106                 'envCheckMagicQuotes',
00107                 'envCheckMagicSybase',
00108                 'envCheckMbstring',
00109                 'envCheckZE1',
00110                 'envCheckSafeMode',
00111                 'envCheckXML',
00112                 'envCheckPCRE',
00113                 'envCheckMemory',
00114                 'envCheckCache',
00115                 'envCheckModSecurity',
00116                 'envCheckDiff3',
00117                 'envCheckGraphics',
00118                 'envCheckServer',
00119                 'envCheckPath',
00120                 'envCheckExtension',
00121                 'envCheckShellLocale',
00122                 'envCheckUploadsDirectory',
00123                 'envCheckLibicu',
00124                 'envCheckSuhosinMaxValueLength',
00125                 'envCheckCtype',
00126         );
00127 
00135         protected $defaultVarNames = array(
00136                 'wgSitename',
00137                 'wgPasswordSender',
00138                 'wgLanguageCode',
00139                 'wgRightsIcon',
00140                 'wgRightsText',
00141                 'wgRightsUrl',
00142                 'wgMainCacheType',
00143                 'wgEnableEmail',
00144                 'wgEnableUserEmail',
00145                 'wgEnotifUserTalk',
00146                 'wgEnotifWatchlist',
00147                 'wgEmailAuthentication',
00148                 'wgDBtype',
00149                 'wgDiff3',
00150                 'wgImageMagickConvertCommand',
00151                 'IP',
00152                 'wgServer',
00153                 'wgScriptPath',
00154                 'wgScriptExtension',
00155                 'wgMetaNamespace',
00156                 'wgDeletedDirectory',
00157                 'wgEnableUploads',
00158                 'wgLogo',
00159                 'wgShellLocale',
00160                 'wgSecretKey',
00161                 'wgUseInstantCommons',
00162                 'wgUpgradeKey',
00163                 'wgDefaultSkin',
00164                 'wgResourceLoaderMaxQueryLength',
00165         );
00166 
00174         protected $internalDefaults = array(
00175                 '_UserLang' => 'en',
00176                 '_Environment' => false,
00177                 '_CompiledDBs' => array(),
00178                 '_SafeMode' => false,
00179                 '_RaiseMemory' => false,
00180                 '_UpgradeDone' => false,
00181                 '_InstallDone' => false,
00182                 '_Caches' => array(),
00183                 '_InstallPassword' => '',
00184                 '_SameAccount' => true,
00185                 '_CreateDBAccount' => false,
00186                 '_NamespaceType' => 'site-name',
00187                 '_AdminName' => '', // will be set later, when the user selects language
00188                 '_AdminPassword' => '',
00189                 '_AdminPassword2' => '',
00190                 '_AdminEmail' => '',
00191                 '_Subscribe' => false,
00192                 '_SkipOptional' => 'continue',
00193                 '_RightsProfile' => 'wiki',
00194                 '_LicenseCode' => 'none',
00195                 '_CCDone' => false,
00196                 '_Extensions' => array(),
00197                 '_MemCachedServers' => '',
00198                 '_UpgradeKeySupplied' => false,
00199                 '_ExistingDBSettings' => false,
00200         );
00201 
00207         private $installSteps = array();
00208 
00214         protected $extraInstallSteps = array();
00215 
00221         protected $objectCaches = array(
00222                 'xcache' => 'xcache_get',
00223                 'apc' => 'apc_fetch',
00224                 'wincache' => 'wincache_ucache_get'
00225         );
00226 
00232         public $rightsProfiles = array(
00233                 'wiki' => array(),
00234                 'no-anon' => array(
00235                         '*' => array( 'edit' => false )
00236                 ),
00237                 'fishbowl' => array(
00238                         '*' => array(
00239                                 'createaccount' => false,
00240                                 'edit' => false,
00241                         ),
00242                 ),
00243                 'private' => array(
00244                         '*' => array(
00245                                 'createaccount' => false,
00246                                 'edit' => false,
00247                                 'read' => false,
00248                         ),
00249                 ),
00250         );
00251 
00257         public $licenses = array(
00258                 'cc-by' => array(
00259                         'url' => 'http://creativecommons.org/licenses/by/3.0/',
00260                         'icon' => '{$wgStylePath}/common/images/cc-by.png',
00261                 ),
00262                 'cc-by-sa' => array(
00263                         'url' => 'http://creativecommons.org/licenses/by-sa/3.0/',
00264                         'icon' => '{$wgStylePath}/common/images/cc-by-sa.png',
00265                 ),
00266                 'cc-by-nc-sa' => array(
00267                         'url' => 'http://creativecommons.org/licenses/by-nc-sa/3.0/',
00268                         'icon' => '{$wgStylePath}/common/images/cc-by-nc-sa.png',
00269                 ),
00270                 'cc-0' => array(
00271                         'url' => 'https://creativecommons.org/publicdomain/zero/1.0/',
00272                         'icon' => '{$wgStylePath}/common/images/cc-0.png',
00273                 ),
00274                 'pd' => array(
00275                         'url' => '',
00276                         'icon' => '{$wgStylePath}/common/images/public-domain.png',
00277                 ),
00278                 'gfdl' => array(
00279                         'url' => 'http://www.gnu.org/copyleft/fdl.html',
00280                         'icon' => '{$wgStylePath}/common/images/gnu-fdl.png',
00281                 ),
00282                 'none' => array(
00283                         'url' => '',
00284                         'icon' => '',
00285                         'text' => ''
00286                 ),
00287                 'cc-choose' => array(
00288                         // Details will be filled in by the selector.
00289                         'url' => '',
00290                         'icon' => '',
00291                         'text' => '',
00292                 ),
00293         );
00294 
00298         protected $mediaWikiAnnounceUrl = 'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce';
00299 
00303         protected $mediaWikiAnnounceLanguages = array(
00304                 'ca', 'cs', 'da', 'de', 'en', 'es', 'et', 'eu', 'fi', 'fr', 'hr', 'hu',
00305                 'it', 'ja', 'ko', 'lt', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru',
00306                 'sl', 'sr', 'sv', 'tr', 'uk'
00307         );
00308 
00316         public abstract function showMessage( $msg /*, ... */ );
00317 
00322         public abstract function showError( $msg /*, ... */ );
00323 
00328         public abstract function showStatusMessage( Status $status );
00329 
00333         public function __construct() {
00334                 global $wgExtensionMessagesFiles, $wgUser;
00335 
00336                 // Disable the i18n cache and LoadBalancer
00337                 Language::getLocalisationCache()->disableBackend();
00338                 LBFactory::disableBackend();
00339 
00340                 // Load the installer's i18n file.
00341                 $wgExtensionMessagesFiles['MediawikiInstaller'] =
00342                         __DIR__ . '/Installer.i18n.php';
00343 
00344                 // Having a user with id = 0 safeguards us from DB access via User::loadOptions().
00345                 $wgUser = User::newFromId( 0 );
00346 
00347                 $this->settings = $this->internalDefaults;
00348 
00349                 foreach ( $this->defaultVarNames as $var ) {
00350                         $this->settings[$var] = $GLOBALS[$var];
00351                 }
00352 
00353                 $compiledDBs = array();
00354                 foreach ( self::getDBTypes() as $type ) {
00355                         $installer = $this->getDBInstaller( $type );
00356 
00357                         if ( !$installer->isCompiled() ) {
00358                                 continue;
00359                         }
00360                         $compiledDBs[] = $type;
00361 
00362                         $defaults = $installer->getGlobalDefaults();
00363 
00364                         foreach ( $installer->getGlobalNames() as $var ) {
00365                                 if ( isset( $defaults[$var] ) ) {
00366                                         $this->settings[$var] = $defaults[$var];
00367                                 } else {
00368                                         $this->settings[$var] = $GLOBALS[$var];
00369                                 }
00370                         }
00371                 }
00372                 $this->setVar( '_CompiledDBs', $compiledDBs );
00373 
00374                 $this->parserTitle = Title::newFromText( 'Installer' );
00375                 $this->parserOptions = new ParserOptions; // language will  be wrong :(
00376                 $this->parserOptions->setEditSection( false );
00377         }
00378 
00384         public static function getDBTypes() {
00385                 return self::$dbTypes;
00386         }
00387 
00401         public function doEnvironmentChecks() {
00402                 $phpVersion = phpversion();
00403                 if( version_compare( $phpVersion, self::MINIMUM_PHP_VERSION, '>=' ) ) {
00404                         $this->showMessage( 'config-env-php', $phpVersion );
00405                         $good = true;
00406                 } else {
00407                         $this->showMessage( 'config-env-php-toolow', $phpVersion, self::MINIMUM_PHP_VERSION );
00408                         $good = false;
00409                 }
00410 
00411                 if( $good ) {
00412                         foreach ( $this->envChecks as $check ) {
00413                                 $status = $this->$check();
00414                                 if ( $status === false ) {
00415                                         $good = false;
00416                                 }
00417                         }
00418                 }
00419 
00420                 $this->setVar( '_Environment', $good );
00421 
00422                 return $good ? Status::newGood() : Status::newFatal( 'config-env-bad' );
00423         }
00424 
00431         public function setVar( $name, $value ) {
00432                 $this->settings[$name] = $value;
00433         }
00434 
00445         public function getVar( $name, $default = null ) {
00446                 if ( !isset( $this->settings[$name] ) ) {
00447                         return $default;
00448                 } else {
00449                         return $this->settings[$name];
00450                 }
00451         }
00452 
00460         public function getDBInstaller( $type = false ) {
00461                 if ( !$type ) {
00462                         $type = $this->getVar( 'wgDBtype' );
00463                 }
00464 
00465                 $type = strtolower( $type );
00466 
00467                 if ( !isset( $this->dbInstallers[$type] ) ) {
00468                         $class = ucfirst( $type ). 'Installer';
00469                         $this->dbInstallers[$type] = new $class( $this );
00470                 }
00471 
00472                 return $this->dbInstallers[$type];
00473         }
00474 
00481         public static function getExistingLocalSettings() {
00482                 global $IP;
00483 
00484                 wfSuppressWarnings();
00485                 $_lsExists = file_exists( "$IP/LocalSettings.php" );
00486                 wfRestoreWarnings();
00487 
00488                 if( !$_lsExists ) {
00489                         return false;
00490                 }
00491                 unset($_lsExists);
00492 
00493                 require( "$IP/includes/DefaultSettings.php" );
00494                 require( "$IP/LocalSettings.php" );
00495                 if ( file_exists( "$IP/AdminSettings.php" ) ) {
00496                         require( "$IP/AdminSettings.php" );
00497                 }
00498                 return get_defined_vars();
00499         }
00500 
00510         public function getFakePassword( $realPassword ) {
00511                 return str_repeat( '*', strlen( $realPassword ) );
00512         }
00513 
00521         public function setPassword( $name, $value ) {
00522                 if ( !preg_match( '/^\*+$/', $value ) ) {
00523                         $this->setVar( $name, $value );
00524                 }
00525         }
00526 
00538         public static function maybeGetWebserverPrimaryGroup() {
00539                 if ( !function_exists( 'posix_getegid' ) || !function_exists( 'posix_getpwuid' ) ) {
00540                         # I don't know this, this isn't UNIX.
00541                         return null;
00542                 }
00543 
00544                 # posix_getegid() *not* getmygid() because we want the group of the webserver,
00545                 # not whoever owns the current script.
00546                 $gid = posix_getegid();
00547                 $getpwuid = posix_getpwuid( $gid );
00548                 $group = $getpwuid['name'];
00549 
00550                 return $group;
00551         }
00552 
00569         public function parse( $text, $lineStart = false ) {
00570                 global $wgParser;
00571 
00572                 try {
00573                         $out = $wgParser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart );
00574                         $html = $out->getText();
00575                 } catch ( DBAccessError $e ) {
00576                         $html = '<!--DB access attempted during parse-->  ' . htmlspecialchars( $text );
00577 
00578                         if ( !empty( $this->debug ) ) {
00579                                 $html .= "<!--\n" . $e->getTraceAsString() . "\n-->";
00580                         }
00581                 }
00582 
00583                 return $html;
00584         }
00585 
00589         public function getParserOptions() {
00590                 return $this->parserOptions;
00591         }
00592 
00593         public function disableLinkPopups() {
00594                 $this->parserOptions->setExternalLinkTarget( false );
00595         }
00596 
00597         public function restoreLinkPopups() {
00598                 global $wgExternalLinkTarget;
00599                 $this->parserOptions->setExternalLinkTarget( $wgExternalLinkTarget );
00600         }
00601 
00610         public function populateSiteStats( DatabaseInstaller $installer ) {
00611                 $status = $installer->getConnection();
00612                 if ( !$status->isOK() ) {
00613                         return $status;
00614                 }
00615                 $status->value->insert( 'site_stats', array(
00616                         'ss_row_id' => 1,
00617                         'ss_total_views' => 0,
00618                         'ss_total_edits' => 0,
00619                         'ss_good_articles' => 0,
00620                         'ss_total_pages' => 0,
00621                         'ss_users' => 0,
00622                         'ss_images' => 0 ),
00623                         __METHOD__, 'IGNORE' );
00624                 return Status::newGood();
00625         }
00626 
00630         public function exportVars() {
00631                 foreach ( $this->settings as $name => $value ) {
00632                         if ( substr( $name, 0, 2 ) == 'wg' ) {
00633                                 $GLOBALS[$name] = $value;
00634                         }
00635                 }
00636         }
00637 
00642         protected function envCheckDB() {
00643                 global $wgLang;
00644 
00645                 $allNames = array();
00646 
00647                 foreach ( self::getDBTypes() as $name ) {
00648                         $allNames[] = wfMessage( "config-type-$name" )->text();
00649                 }
00650 
00651                 // cache initially available databases to make sure that everything will be displayed correctly
00652                 // after a refresh on env checks page
00653                 $databases = $this->getVar( '_CompiledDBs-preFilter' );
00654                 if ( !$databases ) {
00655                         $databases = $this->getVar( '_CompiledDBs' );
00656                         $this->setVar( '_CompiledDBs-preFilter', $databases );
00657                 }
00658 
00659                 $databases = array_flip ( $databases );
00660                 foreach ( array_keys( $databases ) as $db ) {
00661                         $installer = $this->getDBInstaller( $db );
00662                         $status = $installer->checkPrerequisites();
00663                         if ( !$status->isGood() ) {
00664                                 $this->showStatusMessage( $status );
00665                         }
00666                         if ( !$status->isOK() ) {
00667                                 unset( $databases[$db] );
00668                         }
00669                 }
00670                 $databases = array_flip( $databases );
00671                 if ( !$databases ) {
00672                         $this->showError( 'config-no-db', $wgLang->commaList( $allNames ) );
00673                         // @todo FIXME: This only works for the web installer!
00674                         return false;
00675                 }
00676                 $this->setVar( '_CompiledDBs', $databases );
00677                 return true;
00678         }
00679 
00683         protected function envCheckRegisterGlobals() {
00684                 if( wfIniGetBool( 'register_globals' ) ) {
00685                         $this->showMessage( 'config-register-globals' );
00686                 }
00687         }
00688 
00693         protected function envCheckBrokenXML() {
00694                 $test = new PhpXmlBugTester();
00695                 if ( !$test->ok ) {
00696                         $this->showError( 'config-brokenlibxml' );
00697                         return false;
00698                 }
00699                 return true;
00700         }
00701 
00707         protected function envCheckPHP531() {
00708                 $test = new PhpRefCallBugTester;
00709                 $test->execute();
00710                 if ( !$test->ok ) {
00711                         $this->showError( 'config-using531', phpversion() );
00712                         return false;
00713                 }
00714                 return true;
00715         }
00716 
00721         protected function envCheckMagicQuotes() {
00722                 if( wfIniGetBool( "magic_quotes_runtime" ) ) {
00723                         $this->showError( 'config-magic-quotes-runtime' );
00724                         return false;
00725                 }
00726                 return true;
00727         }
00728 
00733         protected function envCheckMagicSybase() {
00734                 if ( wfIniGetBool( 'magic_quotes_sybase' ) ) {
00735                         $this->showError( 'config-magic-quotes-sybase' );
00736                         return false;
00737                 }
00738                 return true;
00739         }
00740 
00745         protected function envCheckMbstring() {
00746                 if ( wfIniGetBool( 'mbstring.func_overload' ) ) {
00747                         $this->showError( 'config-mbstring' );
00748                         return false;
00749                 }
00750                 return true;
00751         }
00752 
00757         protected function envCheckZE1() {
00758                 if ( wfIniGetBool( 'zend.ze1_compatibility_mode' ) ) {
00759                         $this->showError( 'config-ze1' );
00760                         return false;
00761                 }
00762                 return true;
00763         }
00764 
00769         protected function envCheckSafeMode() {
00770                 if ( wfIniGetBool( 'safe_mode' ) ) {
00771                         $this->setVar( '_SafeMode', true );
00772                         $this->showMessage( 'config-safe-mode' );
00773                 }
00774                 return true;
00775         }
00776 
00781         protected function envCheckXML() {
00782                 if ( !function_exists( "utf8_encode" ) ) {
00783                         $this->showError( 'config-xml-bad' );
00784                         return false;
00785                 }
00786                 return true;
00787         }
00788 
00797         protected function envCheckPCRE() {
00798                 if ( !function_exists( 'preg_match' ) ) {
00799                         $this->showError( 'config-pcre' );
00800                         return false;
00801                 }
00802                 wfSuppressWarnings();
00803                 $regexd = preg_replace( '/[\x{0430}-\x{04FF}]/iu', '', '-АБВГД-' );
00804                 // Need to check for \p support too, as PCRE can be compiled
00805                 // with utf8 support, but not unicode property support.
00806                 // check that \p{Zs} (space separators) matches
00807                 // U+3000 (Ideographic space)
00808                 $regexprop = preg_replace( '/\p{Zs}/u', '', "-\xE3\x80\x80-" );
00809                 wfRestoreWarnings();
00810                 if ( $regexd != '--' || $regexprop != '--' ) {
00811                         $this->showError( 'config-pcre-no-utf8' );
00812                         return false;
00813                 }
00814                 return true;
00815         }
00816 
00821         protected function envCheckMemory() {
00822                 $limit = ini_get( 'memory_limit' );
00823 
00824                 if ( !$limit || $limit == -1 ) {
00825                         return true;
00826                 }
00827 
00828                 $n = wfShorthandToInteger( $limit );
00829 
00830                 if( $n < $this->minMemorySize * 1024 * 1024 ) {
00831                         $newLimit = "{$this->minMemorySize}M";
00832 
00833                         if( ini_set( "memory_limit", $newLimit ) === false ) {
00834                                 $this->showMessage( 'config-memory-bad', $limit );
00835                         } else {
00836                                 $this->showMessage( 'config-memory-raised', $limit, $newLimit );
00837                                 $this->setVar( '_RaiseMemory', true );
00838                         }
00839                 }
00840                 return true;
00841         }
00842 
00846         protected function envCheckCache() {
00847                 $caches = array();
00848                 foreach ( $this->objectCaches as $name => $function ) {
00849                         if ( function_exists( $function ) ) {
00850                                 if ( $name == 'xcache' && !wfIniGetBool( 'xcache.var_size' ) ) {
00851                                         continue;
00852                                 }
00853                                 $caches[$name] = true;
00854                         }
00855                 }
00856 
00857                 if ( !$caches ) {
00858                         $this->showMessage( 'config-no-cache' );
00859                 }
00860 
00861                 $this->setVar( '_Caches', $caches );
00862         }
00863 
00868         protected function envCheckModSecurity() {
00869                 if ( self::apacheModulePresent( 'mod_security' ) ) {
00870                         $this->showMessage( 'config-mod-security' );
00871                 }
00872                 return true;
00873         }
00874 
00879         protected function envCheckDiff3() {
00880                 $names = array( "gdiff3", "diff3", "diff3.exe" );
00881                 $versionInfo = array( '$1 --version 2>&1', 'GNU diffutils' );
00882 
00883                 $diff3 = self::locateExecutableInDefaultPaths( $names, $versionInfo );
00884 
00885                 if ( $diff3 ) {
00886                         $this->setVar( 'wgDiff3', $diff3 );
00887                 } else {
00888                         $this->setVar( 'wgDiff3', false );
00889                         $this->showMessage( 'config-diff3-bad' );
00890                 }
00891                 return true;
00892         }
00893 
00898         protected function envCheckGraphics() {
00899                 $names = array( wfIsWindows() ? 'convert.exe' : 'convert' );
00900                 $convert = self::locateExecutableInDefaultPaths( $names, array( '$1 -version', 'ImageMagick' ) );
00901 
00902                 $this->setVar( 'wgImageMagickConvertCommand', '' );
00903                 if ( $convert ) {
00904                         $this->setVar( 'wgImageMagickConvertCommand', $convert );
00905                         $this->showMessage( 'config-imagemagick', $convert );
00906                         return true;
00907                 } elseif ( function_exists( 'imagejpeg' ) ) {
00908                         $this->showMessage( 'config-gd' );
00909 
00910                 } else {
00911                         $this->showMessage( 'config-no-scaling' );
00912                 }
00913                 return true;
00914         }
00915 
00919         protected function envCheckServer() {
00920                 $server = $this->envGetDefaultServer();
00921                 $this->showMessage( 'config-using-server', $server );
00922                 $this->setVar( 'wgServer', $server );
00923                 return true;
00924         }
00925 
00930         protected abstract function envGetDefaultServer();
00931 
00936         protected function envCheckPath() {
00937                 global $IP;
00938                 $IP = dirname( dirname( __DIR__ ) );
00939                 $this->setVar( 'IP', $IP );
00940 
00941                 $this->showMessage( 'config-using-uri', $this->getVar( 'wgServer' ), $this->getVar( 'wgScriptPath' ) );
00942                 return true;
00943         }
00944 
00948         protected function envCheckExtension() {
00949                 // @todo FIXME: Detect this properly
00950                 if ( defined( 'MW_INSTALL_PHP5_EXT' ) ) {
00951                         $ext = 'php5';
00952                 } else {
00953                         $ext = 'php';
00954                 }
00955                 $this->setVar( 'wgScriptExtension', ".$ext" );
00956                 return true;
00957         }
00958 
00963         protected function envCheckShellLocale() {
00964                 $os = php_uname( 's' );
00965                 $supported = array( 'Linux', 'SunOS', 'HP-UX', 'Darwin' ); # Tested these
00966 
00967                 if ( !in_array( $os, $supported ) ) {
00968                         return true;
00969                 }
00970 
00971                 # Get a list of available locales.
00972                 $ret = false;
00973                 $lines = wfShellExec( '/usr/bin/locale -a', $ret );
00974 
00975                 if ( $ret ) {
00976                         return true;
00977                 }
00978 
00979                 $lines = wfArrayMap( 'trim', explode( "\n", $lines ) );
00980                 $candidatesByLocale = array();
00981                 $candidatesByLang = array();
00982 
00983                 foreach ( $lines as $line ) {
00984                         if ( $line === '' ) {
00985                                 continue;
00986                         }
00987 
00988                         if ( !preg_match( '/^([a-zA-Z]+)(_[a-zA-Z]+|)\.(utf8|UTF-8)(@[a-zA-Z_]*|)$/i', $line, $m ) ) {
00989                                 continue;
00990                         }
00991 
00992                         list( $all, $lang, $territory, $charset, $modifier ) = $m;
00993 
00994                         $candidatesByLocale[$m[0]] = $m;
00995                         $candidatesByLang[$lang][] = $m;
00996                 }
00997 
00998                 # Try the current value of LANG.
00999                 if ( isset( $candidatesByLocale[ getenv( 'LANG' ) ] ) ) {
01000                         $this->setVar( 'wgShellLocale', getenv( 'LANG' ) );
01001                         return true;
01002                 }
01003 
01004                 # Try the most common ones.
01005                 $commonLocales = array( 'en_US.UTF-8', 'en_US.utf8', 'de_DE.UTF-8', 'de_DE.utf8' );
01006                 foreach ( $commonLocales as $commonLocale ) {
01007                         if ( isset( $candidatesByLocale[$commonLocale] ) ) {
01008                                 $this->setVar( 'wgShellLocale', $commonLocale );
01009                                 return true;
01010                         }
01011                 }
01012 
01013                 # Is there an available locale in the Wiki's language?
01014                 $wikiLang = $this->getVar( 'wgLanguageCode' );
01015 
01016                 if ( isset( $candidatesByLang[$wikiLang] ) ) {
01017                         $m = reset( $candidatesByLang[$wikiLang] );
01018                         $this->setVar( 'wgShellLocale', $m[0] );
01019                         return true;
01020                 }
01021 
01022                 # Are there any at all?
01023                 if ( count( $candidatesByLocale ) ) {
01024                         $m = reset( $candidatesByLocale );
01025                         $this->setVar( 'wgShellLocale', $m[0] );
01026                         return true;
01027                 }
01028 
01029                 # Give up.
01030                 return true;
01031         }
01032 
01037         protected function envCheckUploadsDirectory() {
01038                 global $IP;
01039 
01040                 $dir = $IP . '/images/';
01041                 $url = $this->getVar( 'wgServer' ) . $this->getVar( 'wgScriptPath' ) . '/images/';
01042                 $safe = !$this->dirIsExecutable( $dir, $url );
01043 
01044                 if ( !$safe ) {
01045                         $this->showMessage( 'config-uploads-not-safe', $dir );
01046                 }
01047                 return true;
01048         }
01049 
01056         protected function envCheckSuhosinMaxValueLength() {
01057                 $maxValueLength = ini_get( 'suhosin.get.max_value_length' );
01058                 if ( $maxValueLength > 0 ) {
01059                         if( $maxValueLength < 1024 ) {
01060                                 # Only warn if the value is below the sane 1024
01061                                 $this->showMessage( 'config-suhosin-max-value-length', $maxValueLength );
01062                         }
01063                 } else {
01064                         $maxValueLength = -1;
01065                 }
01066                 $this->setVar( 'wgResourceLoaderMaxQueryLength', $maxValueLength );
01067                 return true;
01068         }
01069 
01075         protected function unicodeChar( $c ) {
01076                 $c = hexdec($c);
01077                 if ($c <= 0x7F) {
01078                         return chr($c);
01079                 } elseif ($c <= 0x7FF) {
01080                         return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F);
01081                 } elseif ($c <= 0xFFFF) {
01082                         return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F)
01083                                 . chr(0x80 | $c & 0x3F);
01084                 } elseif ($c <= 0x10FFFF) {
01085                         return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F)
01086                                 . chr(0x80 | $c >> 6 & 0x3F)
01087                                 . chr(0x80 | $c & 0x3F);
01088                 } else {
01089                         return false;
01090                 }
01091         }
01092 
01093 
01097         protected function envCheckLibicu() {
01098                 $utf8 = function_exists( 'utf8_normalize' );
01099                 $intl = function_exists( 'normalizer_normalize' );
01100 
01108                 $not_normal_c = $this->unicodeChar("FA6C");
01109                 $normal_c = $this->unicodeChar("242EE");
01110 
01111                 $useNormalizer = 'php';
01112                 $needsUpdate = false;
01113 
01118                 if( $utf8 ) {
01119                         $useNormalizer = 'utf8';
01120                         $utf8 = utf8_normalize( $not_normal_c, UtfNormal::UNORM_NFC );
01121                         if ( $utf8 !== $normal_c ) {
01122                                 $needsUpdate = true;
01123                         }
01124                 }
01125                 if( $intl ) {
01126                         $useNormalizer = 'intl';
01127                         $intl = normalizer_normalize( $not_normal_c, Normalizer::FORM_C );
01128                         if ( $intl !== $normal_c ) {
01129                                 $needsUpdate = true;
01130                         }
01131                 }
01132 
01133                 // Uses messages 'config-unicode-using-php', 'config-unicode-using-utf8', 'config-unicode-using-intl'
01134                 if( $useNormalizer === 'php' ) {
01135                         $this->showMessage( 'config-unicode-pure-php-warning' );
01136                 } else {
01137                         $this->showMessage( 'config-unicode-using-' . $useNormalizer );
01138                         if( $needsUpdate ) {
01139                                 $this->showMessage( 'config-unicode-update-warning' );
01140                         }
01141                 }
01142         }
01143 
01147         protected function envCheckCtype() {
01148                 if ( !function_exists( 'ctype_digit' ) ) {
01149                         $this->showError( 'config-ctype' );
01150                         return false;
01151                 }
01152                 return true;
01153         }
01154 
01162         protected static function getPossibleBinPaths() {
01163                 return array_merge(
01164                         array( '/usr/bin', '/usr/local/bin', '/opt/csw/bin',
01165                                 '/usr/gnu/bin', '/usr/sfw/bin', '/sw/bin', '/opt/local/bin' ),
01166                         explode( PATH_SEPARATOR, getenv( 'PATH' ) )
01167                 );
01168         }
01169 
01187         public static function locateExecutable( $path, $names, $versionInfo = false ) {
01188                 if ( !is_array( $names ) ) {
01189                         $names = array( $names );
01190                 }
01191 
01192                 foreach ( $names as $name ) {
01193                         $command = $path . DIRECTORY_SEPARATOR . $name;
01194 
01195                         wfSuppressWarnings();
01196                         $file_exists = file_exists( $command );
01197                         wfRestoreWarnings();
01198 
01199                         if ( $file_exists ) {
01200                                 if ( !$versionInfo ) {
01201                                         return $command;
01202                                 }
01203 
01204                                 $file = str_replace( '$1', wfEscapeShellArg( $command ), $versionInfo[0] );
01205                                 if ( strstr( wfShellExec( $file ), $versionInfo[1] ) !== false ) {
01206                                         return $command;
01207                                 }
01208                         }
01209                 }
01210                 return false;
01211         }
01212 
01220         public static function locateExecutableInDefaultPaths( $names, $versionInfo = false ) {
01221                 foreach( self::getPossibleBinPaths() as $path ) {
01222                         $exe = self::locateExecutable( $path, $names, $versionInfo );
01223                         if( $exe !== false ) {
01224                                 return $exe;
01225                         }
01226                 }
01227                 return false;
01228         }
01229 
01238         public function dirIsExecutable( $dir, $url ) {
01239                 $scriptTypes = array(
01240                         'php' => array(
01241                                 "<?php echo 'ex' . 'ec';",
01242                                 "#!/var/env php5\n<?php echo 'ex' . 'ec';",
01243                         ),
01244                 );
01245 
01246                 // it would be good to check other popular languages here, but it'll be slow.
01247 
01248                 wfSuppressWarnings();
01249 
01250                 foreach ( $scriptTypes as $ext => $contents ) {
01251                         foreach ( $contents as $source ) {
01252                                 $file = 'exectest.' . $ext;
01253 
01254                                 if ( !file_put_contents( $dir . $file, $source ) ) {
01255                                         break;
01256                                 }
01257 
01258                                 try {
01259                                         $text = Http::get( $url . $file, array( 'timeout' => 3 ) );
01260                                 }
01261                                 catch( MWException $e ) {
01262                                         // Http::get throws with allow_url_fopen = false and no curl extension.
01263                                         $text = null;
01264                                 }
01265                                 unlink( $dir . $file );
01266 
01267                                 if ( $text == 'exec' ) {
01268                                         wfRestoreWarnings();
01269                                         return $ext;
01270                                 }
01271                         }
01272                 }
01273 
01274                 wfRestoreWarnings();
01275 
01276                 return false;
01277         }
01278 
01285         public static function apacheModulePresent( $moduleName ) {
01286                 if ( function_exists( 'apache_get_modules' ) && in_array( $moduleName, apache_get_modules() ) ) {
01287                         return true;
01288                 }
01289                 // try it the hard way
01290                 ob_start();
01291                 phpinfo( INFO_MODULES );
01292                 $info = ob_get_clean();
01293                 return strpos( $info, $moduleName ) !== false;
01294         }
01295 
01301         public function setParserLanguage( $lang ) {
01302                 $this->parserOptions->setTargetLanguage( $lang );
01303                 $this->parserOptions->setUserLang( $lang );
01304         }
01305 
01311         protected function getDocUrl( $page ) {
01312                 return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page );
01313         }
01314 
01321         public function findExtensions() {
01322                 if( $this->getVar( 'IP' ) === null ) {
01323                         return false;
01324                 }
01325 
01326                 $exts = array();
01327                 $extDir = $this->getVar( 'IP' ) . '/extensions';
01328                 $dh = opendir( $extDir );
01329 
01330                 while ( ( $file = readdir( $dh ) ) !== false ) {
01331                         if( !is_dir( "$extDir/$file" ) ) {
01332                                 continue;
01333                         }
01334                         if( file_exists( "$extDir/$file/$file.php" ) ) {
01335                                 $exts[] = $file;
01336                         }
01337                 }
01338                 natcasesort( $exts );
01339 
01340                 return $exts;
01341         }
01342 
01348         protected function includeExtensions() {
01349                 global $IP;
01350                 $exts = $this->getVar( '_Extensions' );
01351                 $IP = $this->getVar( 'IP' );
01352 
01361                 global $wgAutoloadClasses;
01362                 $wgAutoloadClasses = array();
01363 
01364                 require( "$IP/includes/DefaultSettings.php" );
01365 
01366                 foreach( $exts as $e ) {
01367                         require_once( "$IP/extensions/$e/$e.php" );
01368                 }
01369 
01370                 $hooksWeWant = isset( $wgHooks['LoadExtensionSchemaUpdates'] ) ?
01371                         $wgHooks['LoadExtensionSchemaUpdates'] : array();
01372 
01373                 // Unset everyone else's hooks. Lord knows what someone might be doing
01374                 // in ParserFirstCallInit (see bug 27171)
01375                 $GLOBALS['wgHooks'] = array( 'LoadExtensionSchemaUpdates' => $hooksWeWant );
01376 
01377                 return Status::newGood();
01378         }
01379 
01392         protected function getInstallSteps( DatabaseInstaller $installer ) {
01393                 $coreInstallSteps = array(
01394                         array( 'name' => 'database',   'callback' => array( $installer, 'setupDatabase' ) ),
01395                         array( 'name' => 'tables',     'callback' => array( $installer, 'createTables' ) ),
01396                         array( 'name' => 'interwiki',  'callback' => array( $installer, 'populateInterwikiTable' ) ),
01397                         array( 'name' => 'stats',      'callback' => array( $this, 'populateSiteStats' ) ),
01398                         array( 'name' => 'keys',       'callback' => array( $this, 'generateKeys' ) ),
01399                         array( 'name' => 'sysop',      'callback' => array( $this, 'createSysop' ) ),
01400                         array( 'name' => 'mainpage',   'callback' => array( $this, 'createMainpage' ) ),
01401                 );
01402 
01403                 // Build the array of install steps starting from the core install list,
01404                 // then adding any callbacks that wanted to attach after a given step
01405                 foreach( $coreInstallSteps as $step ) {
01406                         $this->installSteps[] = $step;
01407                         if( isset( $this->extraInstallSteps[ $step['name'] ] ) ) {
01408                                 $this->installSteps = array_merge(
01409                                         $this->installSteps,
01410                                         $this->extraInstallSteps[ $step['name'] ]
01411                                 );
01412                         }
01413                 }
01414 
01415                 // Prepend any steps that want to be at the beginning
01416                 if( isset( $this->extraInstallSteps['BEGINNING'] ) ) {
01417                         $this->installSteps = array_merge(
01418                                 $this->extraInstallSteps['BEGINNING'],
01419                                 $this->installSteps
01420                         );
01421                 }
01422 
01423                 // Extensions should always go first, chance to tie into hooks and such
01424                 if( count( $this->getVar( '_Extensions' ) ) ) {
01425                         array_unshift( $this->installSteps,
01426                                 array( 'name' => 'extensions', 'callback' => array( $this, 'includeExtensions' ) )
01427                         );
01428                         $this->installSteps[] = array(
01429                                 'name' => 'extension-tables',
01430                                 'callback' => array( $installer, 'createExtensionTables' )
01431                         );
01432                 }
01433                 return $this->installSteps;
01434         }
01435 
01444         public function performInstallation( $startCB, $endCB ) {
01445                 $installResults = array();
01446                 $installer = $this->getDBInstaller();
01447                 $installer->preInstall();
01448                 $steps = $this->getInstallSteps( $installer );
01449                 foreach( $steps as $stepObj ) {
01450                         $name = $stepObj['name'];
01451                         call_user_func_array( $startCB, array( $name ) );
01452 
01453                         // Perform the callback step
01454                         $status = call_user_func( $stepObj['callback'], $installer );
01455 
01456                         // Output and save the results
01457                         call_user_func( $endCB, $name, $status );
01458                         $installResults[$name] = $status;
01459 
01460                         // If we've hit some sort of fatal, we need to bail.
01461                         // Callback already had a chance to do output above.
01462                         if( !$status->isOk() ) {
01463                                 break;
01464                         }
01465                 }
01466                 if( $status->isOk() ) {
01467                         $this->setVar( '_InstallDone', true );
01468                 }
01469                 return $installResults;
01470         }
01471 
01477         public function generateKeys() {
01478                 $keys = array( 'wgSecretKey' => 64 );
01479                 if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) {
01480                         $keys['wgUpgradeKey'] = 16;
01481                 }
01482                 return $this->doGenerateKeys( $keys );
01483         }
01484 
01492         protected function doGenerateKeys( $keys ) {
01493                 $status = Status::newGood();
01494 
01495                 $strong = true;
01496                 foreach ( $keys as $name => $length ) {
01497                         $secretKey = MWCryptRand::generateHex( $length, true );
01498                         if ( !MWCryptRand::wasStrong() ) {
01499                                 $strong = false;
01500                         }
01501 
01502                         $this->setVar( $name, $secretKey );
01503                 }
01504 
01505                 if ( !$strong ) {
01506                         $names = array_keys( $keys );
01507                         $names = preg_replace( '/^(.*)$/', '\$$1', $names );
01508                         global $wgLang;
01509                         $status->warning( 'config-insecure-keys', $wgLang->listToText( $names ), count( $names ) );
01510                 }
01511 
01512                 return $status;
01513         }
01514 
01520         protected function createSysop() {
01521                 $name = $this->getVar( '_AdminName' );
01522                 $user = User::newFromName( $name );
01523 
01524                 if ( !$user ) {
01525                         // We should've validated this earlier anyway!
01526                         return Status::newFatal( 'config-admin-error-user', $name );
01527                 }
01528 
01529                 if ( $user->idForName() == 0 ) {
01530                         $user->addToDatabase();
01531 
01532                         try {
01533                                 $user->setPassword( $this->getVar( '_AdminPassword' ) );
01534                         } catch( PasswordError $pwe ) {
01535                                 return Status::newFatal( 'config-admin-error-password', $name, $pwe->getMessage() );
01536                         }
01537 
01538                         $user->addGroup( 'sysop' );
01539                         $user->addGroup( 'bureaucrat' );
01540                         if( $this->getVar( '_AdminEmail' ) ) {
01541                                 $user->setEmail( $this->getVar( '_AdminEmail' ) );
01542                         }
01543                         $user->saveSettings();
01544 
01545                         // Update user count
01546                         $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
01547                         $ssUpdate->doUpdate();
01548                 }
01549                 $status = Status::newGood();
01550 
01551                 if( $this->getVar( '_Subscribe' ) && $this->getVar( '_AdminEmail' ) ) {
01552                         $this->subscribeToMediaWikiAnnounce( $status );
01553                 }
01554 
01555                 return $status;
01556         }
01557 
01561         private function subscribeToMediaWikiAnnounce( Status $s ) {
01562                 $params = array(
01563                         'email'    => $this->getVar( '_AdminEmail' ),
01564                         'language' => 'en',
01565                         'digest'   => 0
01566                 );
01567 
01568                 // Mailman doesn't support as many languages as we do, so check to make
01569                 // sure their selected language is available
01570                 $myLang = $this->getVar( '_UserLang' );
01571                 if( in_array( $myLang, $this->mediaWikiAnnounceLanguages ) ) {
01572                         $myLang = $myLang == 'pt-br' ? 'pt_BR' : $myLang; // rewrite to Mailman's pt_BR
01573                         $params['language'] = $myLang;
01574                 }
01575 
01576                 if( MWHttpRequest::canMakeRequests() ) {
01577                         $res = MWHttpRequest::factory( $this->mediaWikiAnnounceUrl,
01578                                 array( 'method' => 'POST', 'postData' => $params ) )->execute();
01579                         if( !$res->isOK() ) {
01580                                 $s->warning( 'config-install-subscribe-fail', $res->getMessage() );
01581                         }
01582                 } else {
01583                         $s->warning( 'config-install-subscribe-notpossible' );
01584                 }
01585         }
01586 
01593         protected function createMainpage( DatabaseInstaller $installer ) {
01594                 $status = Status::newGood();
01595                 try {
01596                         $page = WikiPage::factory( Title::newMainPage() );
01597                         $page->doEdit( wfMessage( 'mainpagetext' )->inContentLanguage()->text() . "\n\n" .
01598                                         wfMessage( 'mainpagedocfooter' )->inContentLanguage()->text(),
01599                                         '',
01600                                         EDIT_NEW,
01601                                         false,
01602                                         User::newFromName( 'MediaWiki default' )
01603                         );
01604                 } catch (MWException $e) {
01605                         //using raw, because $wgShowExceptionDetails can not be set yet
01606                         $status->fatal( 'config-install-mainpage-failed', $e->getMessage() );
01607                 }
01608 
01609                 return $status;
01610         }
01611 
01615         public static function overrideConfig() {
01616                 define( 'MW_NO_SESSION', 1 );
01617 
01618                 // Don't access the database
01619                 $GLOBALS['wgUseDatabaseMessages'] = false;
01620                 // Don't cache langconv tables
01621                 $GLOBALS['wgLanguageConverterCacheType'] = CACHE_NONE;
01622                 // Debug-friendly
01623                 $GLOBALS['wgShowExceptionDetails'] = true;
01624                 // Don't break forms
01625                 $GLOBALS['wgExternalLinkTarget'] = '_blank';
01626 
01627                 // Extended debugging
01628                 $GLOBALS['wgShowSQLErrors'] = true;
01629                 $GLOBALS['wgShowDBErrorBacktrace'] = true;
01630 
01631                 // Allow multiple ob_flush() calls
01632                 $GLOBALS['wgDisableOutputCompression'] = true;
01633 
01634                 // Use a sensible cookie prefix (not my_wiki)
01635                 $GLOBALS['wgCookiePrefix'] = 'mw_installer';
01636 
01637                 // Some of the environment checks make shell requests, remove limits
01638                 $GLOBALS['wgMaxShellMemory'] = 0;
01639         }
01640 
01648         public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
01649                 $this->extraInstallSteps[$findStep][] = $callback;
01650         }
01651 
01656         protected function disableTimeLimit() {
01657                 wfSuppressWarnings();
01658                 set_time_limit( 0 );
01659                 wfRestoreWarnings();
01660         }
01661 }