MediaWiki
REL1_21
|
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 00049 00055 protected $compiledDBs; 00056 00062 protected $dbInstallers = array(); 00063 00069 protected $minMemorySize = 50; 00070 00076 protected $parserTitle; 00077 00083 protected $parserOptions; 00084 00094 protected static $dbTypes = array( 00095 'mysql', 00096 'postgres', 00097 'oracle', 00098 'sqlite', 00099 ); 00100 00108 protected $envChecks = array( 00109 'envCheckDB', 00110 'envCheckRegisterGlobals', 00111 'envCheckBrokenXML', 00112 'envCheckPHP531', 00113 'envCheckMagicQuotes', 00114 'envCheckMagicSybase', 00115 'envCheckMbstring', 00116 'envCheckZE1', 00117 'envCheckSafeMode', 00118 'envCheckXML', 00119 'envCheckPCRE', 00120 'envCheckMemory', 00121 'envCheckCache', 00122 'envCheckModSecurity', 00123 'envCheckDiff3', 00124 'envCheckGraphics', 00125 'envCheckServer', 00126 'envCheckPath', 00127 'envCheckExtension', 00128 'envCheckShellLocale', 00129 'envCheckUploadsDirectory', 00130 'envCheckLibicu', 00131 'envCheckSuhosinMaxValueLength', 00132 'envCheckCtype', 00133 ); 00134 00142 protected $defaultVarNames = array( 00143 'wgSitename', 00144 'wgPasswordSender', 00145 'wgLanguageCode', 00146 'wgRightsIcon', 00147 'wgRightsText', 00148 'wgRightsUrl', 00149 'wgMainCacheType', 00150 'wgEnableEmail', 00151 'wgEnableUserEmail', 00152 'wgEnotifUserTalk', 00153 'wgEnotifWatchlist', 00154 'wgEmailAuthentication', 00155 'wgDBtype', 00156 'wgDiff3', 00157 'wgImageMagickConvertCommand', 00158 'IP', 00159 'wgServer', 00160 'wgScriptPath', 00161 'wgScriptExtension', 00162 'wgMetaNamespace', 00163 'wgDeletedDirectory', 00164 'wgEnableUploads', 00165 'wgLogo', 00166 'wgShellLocale', 00167 'wgSecretKey', 00168 'wgUseInstantCommons', 00169 'wgUpgradeKey', 00170 'wgDefaultSkin', 00171 'wgResourceLoaderMaxQueryLength', 00172 ); 00173 00181 protected $internalDefaults = array( 00182 '_UserLang' => 'en', 00183 '_Environment' => false, 00184 '_SafeMode' => false, 00185 '_RaiseMemory' => false, 00186 '_UpgradeDone' => false, 00187 '_InstallDone' => false, 00188 '_Caches' => array(), 00189 '_InstallPassword' => '', 00190 '_SameAccount' => true, 00191 '_CreateDBAccount' => false, 00192 '_NamespaceType' => 'site-name', 00193 '_AdminName' => '', // will be set later, when the user selects language 00194 '_AdminPassword' => '', 00195 '_AdminPassword2' => '', 00196 '_AdminEmail' => '', 00197 '_Subscribe' => false, 00198 '_SkipOptional' => 'continue', 00199 '_RightsProfile' => 'wiki', 00200 '_LicenseCode' => 'none', 00201 '_CCDone' => false, 00202 '_Extensions' => array(), 00203 '_MemCachedServers' => '', 00204 '_UpgradeKeySupplied' => false, 00205 '_ExistingDBSettings' => false, 00206 ); 00207 00213 private $installSteps = array(); 00214 00220 protected $extraInstallSteps = array(); 00221 00227 protected $objectCaches = array( 00228 'xcache' => 'xcache_get', 00229 'apc' => 'apc_fetch', 00230 'wincache' => 'wincache_ucache_get' 00231 ); 00232 00238 public $rightsProfiles = array( 00239 'wiki' => array(), 00240 'no-anon' => array( 00241 '*' => array( 'edit' => false ) 00242 ), 00243 'fishbowl' => array( 00244 '*' => array( 00245 'createaccount' => false, 00246 'edit' => false, 00247 ), 00248 ), 00249 'private' => array( 00250 '*' => array( 00251 'createaccount' => false, 00252 'edit' => false, 00253 'read' => false, 00254 ), 00255 ), 00256 ); 00257 00263 public $licenses = array( 00264 'cc-by' => array( 00265 'url' => 'http://creativecommons.org/licenses/by/3.0/', 00266 'icon' => '{$wgStylePath}/common/images/cc-by.png', 00267 ), 00268 'cc-by-sa' => array( 00269 'url' => 'http://creativecommons.org/licenses/by-sa/3.0/', 00270 'icon' => '{$wgStylePath}/common/images/cc-by-sa.png', 00271 ), 00272 'cc-by-nc-sa' => array( 00273 'url' => 'http://creativecommons.org/licenses/by-nc-sa/3.0/', 00274 'icon' => '{$wgStylePath}/common/images/cc-by-nc-sa.png', 00275 ), 00276 'cc-0' => array( 00277 'url' => 'https://creativecommons.org/publicdomain/zero/1.0/', 00278 'icon' => '{$wgStylePath}/common/images/cc-0.png', 00279 ), 00280 'pd' => array( 00281 'url' => '', 00282 'icon' => '{$wgStylePath}/common/images/public-domain.png', 00283 ), 00284 'gfdl' => array( 00285 'url' => 'http://www.gnu.org/copyleft/fdl.html', 00286 'icon' => '{$wgStylePath}/common/images/gnu-fdl.png', 00287 ), 00288 'none' => array( 00289 'url' => '', 00290 'icon' => '', 00291 'text' => '' 00292 ), 00293 'cc-choose' => array( 00294 // Details will be filled in by the selector. 00295 'url' => '', 00296 'icon' => '', 00297 'text' => '', 00298 ), 00299 ); 00300 00304 protected $mediaWikiAnnounceUrl = 'https://lists.wikimedia.org/mailman/subscribe/mediawiki-announce'; 00305 00309 protected $mediaWikiAnnounceLanguages = array( 00310 'ca', 'cs', 'da', 'de', 'en', 'es', 'et', 'eu', 'fi', 'fr', 'hr', 'hu', 00311 'it', 'ja', 'ko', 'lt', 'nl', 'no', 'pl', 'pt', 'pt-br', 'ro', 'ru', 00312 'sl', 'sr', 'sv', 'tr', 'uk' 00313 ); 00314 00322 abstract public function showMessage( $msg /*, ... */ ); 00323 00328 abstract public function showError( $msg /*, ... */ ); 00329 00334 abstract public function showStatusMessage( Status $status ); 00335 00339 public function __construct() { 00340 global $wgExtensionMessagesFiles, $wgUser; 00341 00342 // Disable the i18n cache and LoadBalancer 00343 Language::getLocalisationCache()->disableBackend(); 00344 LBFactory::disableBackend(); 00345 00346 // Load the installer's i18n file. 00347 $wgExtensionMessagesFiles['MediawikiInstaller'] = 00348 __DIR__ . '/Installer.i18n.php'; 00349 00350 // Having a user with id = 0 safeguards us from DB access via User::loadOptions(). 00351 $wgUser = User::newFromId( 0 ); 00352 00353 $this->settings = $this->internalDefaults; 00354 00355 foreach ( $this->defaultVarNames as $var ) { 00356 $this->settings[$var] = $GLOBALS[$var]; 00357 } 00358 00359 $compiledDBs = array(); 00360 foreach ( self::getDBTypes() as $type ) { 00361 $installer = $this->getDBInstaller( $type ); 00362 00363 if ( !$installer->isCompiled() ) { 00364 continue; 00365 } 00366 $compiledDBs[] = $type; 00367 00368 $defaults = $installer->getGlobalDefaults(); 00369 00370 foreach ( $installer->getGlobalNames() as $var ) { 00371 if ( isset( $defaults[$var] ) ) { 00372 $this->settings[$var] = $defaults[$var]; 00373 } else { 00374 $this->settings[$var] = $GLOBALS[$var]; 00375 } 00376 } 00377 } 00378 $this->compiledDBs = $compiledDBs; 00379 00380 $this->parserTitle = Title::newFromText( 'Installer' ); 00381 $this->parserOptions = new ParserOptions; // language will be wrong :( 00382 $this->parserOptions->setEditSection( false ); 00383 } 00384 00390 public static function getDBTypes() { 00391 return self::$dbTypes; 00392 } 00393 00407 public function doEnvironmentChecks() { 00408 $phpVersion = phpversion(); 00409 if( version_compare( $phpVersion, self::MINIMUM_PHP_VERSION, '>=' ) ) { 00410 $this->showMessage( 'config-env-php', $phpVersion ); 00411 $good = true; 00412 } else { 00413 $this->showMessage( 'config-env-php-toolow', $phpVersion, self::MINIMUM_PHP_VERSION ); 00414 $good = false; 00415 } 00416 00417 if( $good ) { 00418 foreach ( $this->envChecks as $check ) { 00419 $status = $this->$check(); 00420 if ( $status === false ) { 00421 $good = false; 00422 } 00423 } 00424 } 00425 00426 $this->setVar( '_Environment', $good ); 00427 00428 return $good ? Status::newGood() : Status::newFatal( 'config-env-bad' ); 00429 } 00430 00437 public function setVar( $name, $value ) { 00438 $this->settings[$name] = $value; 00439 } 00440 00451 public function getVar( $name, $default = null ) { 00452 if ( !isset( $this->settings[$name] ) ) { 00453 return $default; 00454 } else { 00455 return $this->settings[$name]; 00456 } 00457 } 00458 00464 public function getCompiledDBs() { 00465 return $this->compiledDBs; 00466 } 00467 00475 public function getDBInstaller( $type = false ) { 00476 if ( !$type ) { 00477 $type = $this->getVar( 'wgDBtype' ); 00478 } 00479 00480 $type = strtolower( $type ); 00481 00482 if ( !isset( $this->dbInstallers[$type] ) ) { 00483 $class = ucfirst( $type ). 'Installer'; 00484 $this->dbInstallers[$type] = new $class( $this ); 00485 } 00486 00487 return $this->dbInstallers[$type]; 00488 } 00489 00496 public static function getExistingLocalSettings() { 00497 global $IP; 00498 00499 wfSuppressWarnings(); 00500 $_lsExists = file_exists( "$IP/LocalSettings.php" ); 00501 wfRestoreWarnings(); 00502 00503 if( !$_lsExists ) { 00504 return false; 00505 } 00506 unset( $_lsExists ); 00507 00508 require( "$IP/includes/DefaultSettings.php" ); 00509 require( "$IP/LocalSettings.php" ); 00510 if ( file_exists( "$IP/AdminSettings.php" ) ) { 00511 require( "$IP/AdminSettings.php" ); 00512 } 00513 return get_defined_vars(); 00514 } 00515 00525 public function getFakePassword( $realPassword ) { 00526 return str_repeat( '*', strlen( $realPassword ) ); 00527 } 00528 00536 public function setPassword( $name, $value ) { 00537 if ( !preg_match( '/^\*+$/', $value ) ) { 00538 $this->setVar( $name, $value ); 00539 } 00540 } 00541 00553 public static function maybeGetWebserverPrimaryGroup() { 00554 if ( !function_exists( 'posix_getegid' ) || !function_exists( 'posix_getpwuid' ) ) { 00555 # I don't know this, this isn't UNIX. 00556 return null; 00557 } 00558 00559 # posix_getegid() *not* getmygid() because we want the group of the webserver, 00560 # not whoever owns the current script. 00561 $gid = posix_getegid(); 00562 $getpwuid = posix_getpwuid( $gid ); 00563 $group = $getpwuid['name']; 00564 00565 return $group; 00566 } 00567 00584 public function parse( $text, $lineStart = false ) { 00585 global $wgParser; 00586 00587 try { 00588 $out = $wgParser->parse( $text, $this->parserTitle, $this->parserOptions, $lineStart ); 00589 $html = $out->getText(); 00590 } catch ( DBAccessError $e ) { 00591 $html = '<!--DB access attempted during parse--> ' . htmlspecialchars( $text ); 00592 00593 if ( !empty( $this->debug ) ) { 00594 $html .= "<!--\n" . $e->getTraceAsString() . "\n-->"; 00595 } 00596 } 00597 00598 return $html; 00599 } 00600 00604 public function getParserOptions() { 00605 return $this->parserOptions; 00606 } 00607 00608 public function disableLinkPopups() { 00609 $this->parserOptions->setExternalLinkTarget( false ); 00610 } 00611 00612 public function restoreLinkPopups() { 00613 global $wgExternalLinkTarget; 00614 $this->parserOptions->setExternalLinkTarget( $wgExternalLinkTarget ); 00615 } 00616 00625 public function populateSiteStats( DatabaseInstaller $installer ) { 00626 $status = $installer->getConnection(); 00627 if ( !$status->isOK() ) { 00628 return $status; 00629 } 00630 $status->value->insert( 'site_stats', array( 00631 'ss_row_id' => 1, 00632 'ss_total_views' => 0, 00633 'ss_total_edits' => 0, 00634 'ss_good_articles' => 0, 00635 'ss_total_pages' => 0, 00636 'ss_users' => 0, 00637 'ss_images' => 0 ), 00638 __METHOD__, 'IGNORE' ); 00639 return Status::newGood(); 00640 } 00641 00645 public function exportVars() { 00646 foreach ( $this->settings as $name => $value ) { 00647 if ( substr( $name, 0, 2 ) == 'wg' ) { 00648 $GLOBALS[$name] = $value; 00649 } 00650 } 00651 } 00652 00657 protected function envCheckDB() { 00658 global $wgLang; 00659 00660 $allNames = array(); 00661 00662 foreach ( self::getDBTypes() as $name ) { 00663 $allNames[] = wfMessage( "config-type-$name" )->text(); 00664 } 00665 00666 $databases = $this->getCompiledDBs(); 00667 00668 $databases = array_flip ( $databases ); 00669 foreach ( array_keys( $databases ) as $db ) { 00670 $installer = $this->getDBInstaller( $db ); 00671 $status = $installer->checkPrerequisites(); 00672 if ( !$status->isGood() ) { 00673 $this->showStatusMessage( $status ); 00674 } 00675 if ( !$status->isOK() ) { 00676 unset( $databases[$db] ); 00677 } 00678 } 00679 $databases = array_flip( $databases ); 00680 if ( !$databases ) { 00681 $this->showError( 'config-no-db', $wgLang->commaList( $allNames ) ); 00682 // @todo FIXME: This only works for the web installer! 00683 return false; 00684 } 00685 return true; 00686 } 00687 00691 protected function envCheckRegisterGlobals() { 00692 if( wfIniGetBool( 'register_globals' ) ) { 00693 $this->showMessage( 'config-register-globals' ); 00694 } 00695 } 00696 00701 protected function envCheckBrokenXML() { 00702 $test = new PhpXmlBugTester(); 00703 if ( !$test->ok ) { 00704 $this->showError( 'config-brokenlibxml' ); 00705 return false; 00706 } 00707 return true; 00708 } 00709 00715 protected function envCheckPHP531() { 00716 $test = new PhpRefCallBugTester; 00717 $test->execute(); 00718 if ( !$test->ok ) { 00719 $this->showError( 'config-using531', phpversion() ); 00720 return false; 00721 } 00722 return true; 00723 } 00724 00729 protected function envCheckMagicQuotes() { 00730 if( wfIniGetBool( "magic_quotes_runtime" ) ) { 00731 $this->showError( 'config-magic-quotes-runtime' ); 00732 return false; 00733 } 00734 return true; 00735 } 00736 00741 protected function envCheckMagicSybase() { 00742 if ( wfIniGetBool( 'magic_quotes_sybase' ) ) { 00743 $this->showError( 'config-magic-quotes-sybase' ); 00744 return false; 00745 } 00746 return true; 00747 } 00748 00753 protected function envCheckMbstring() { 00754 if ( wfIniGetBool( 'mbstring.func_overload' ) ) { 00755 $this->showError( 'config-mbstring' ); 00756 return false; 00757 } 00758 return true; 00759 } 00760 00765 protected function envCheckZE1() { 00766 if ( wfIniGetBool( 'zend.ze1_compatibility_mode' ) ) { 00767 $this->showError( 'config-ze1' ); 00768 return false; 00769 } 00770 return true; 00771 } 00772 00777 protected function envCheckSafeMode() { 00778 if ( wfIniGetBool( 'safe_mode' ) ) { 00779 $this->setVar( '_SafeMode', true ); 00780 $this->showMessage( 'config-safe-mode' ); 00781 } 00782 return true; 00783 } 00784 00789 protected function envCheckXML() { 00790 if ( !function_exists( "utf8_encode" ) ) { 00791 $this->showError( 'config-xml-bad' ); 00792 return false; 00793 } 00794 return true; 00795 } 00796 00805 protected function envCheckPCRE() { 00806 if ( !function_exists( 'preg_match' ) ) { 00807 $this->showError( 'config-pcre' ); 00808 return false; 00809 } 00810 wfSuppressWarnings(); 00811 $regexd = preg_replace( '/[\x{0430}-\x{04FF}]/iu', '', '-АБВГД-' ); 00812 // Need to check for \p support too, as PCRE can be compiled 00813 // with utf8 support, but not unicode property support. 00814 // check that \p{Zs} (space separators) matches 00815 // U+3000 (Ideographic space) 00816 $regexprop = preg_replace( '/\p{Zs}/u', '', "-\xE3\x80\x80-" ); 00817 wfRestoreWarnings(); 00818 if ( $regexd != '--' || $regexprop != '--' ) { 00819 $this->showError( 'config-pcre-no-utf8' ); 00820 return false; 00821 } 00822 return true; 00823 } 00824 00829 protected function envCheckMemory() { 00830 $limit = ini_get( 'memory_limit' ); 00831 00832 if ( !$limit || $limit == -1 ) { 00833 return true; 00834 } 00835 00836 $n = wfShorthandToInteger( $limit ); 00837 00838 if( $n < $this->minMemorySize * 1024 * 1024 ) { 00839 $newLimit = "{$this->minMemorySize}M"; 00840 00841 if( ini_set( "memory_limit", $newLimit ) === false ) { 00842 $this->showMessage( 'config-memory-bad', $limit ); 00843 } else { 00844 $this->showMessage( 'config-memory-raised', $limit, $newLimit ); 00845 $this->setVar( '_RaiseMemory', true ); 00846 } 00847 } 00848 return true; 00849 } 00850 00854 protected function envCheckCache() { 00855 $caches = array(); 00856 foreach ( $this->objectCaches as $name => $function ) { 00857 if ( function_exists( $function ) ) { 00858 if ( $name == 'xcache' && !wfIniGetBool( 'xcache.var_size' ) ) { 00859 continue; 00860 } 00861 $caches[$name] = true; 00862 } 00863 } 00864 00865 if ( !$caches ) { 00866 $this->showMessage( 'config-no-cache' ); 00867 } 00868 00869 $this->setVar( '_Caches', $caches ); 00870 } 00871 00876 protected function envCheckModSecurity() { 00877 if ( self::apacheModulePresent( 'mod_security' ) ) { 00878 $this->showMessage( 'config-mod-security' ); 00879 } 00880 return true; 00881 } 00882 00887 protected function envCheckDiff3() { 00888 $names = array( "gdiff3", "diff3", "diff3.exe" ); 00889 $versionInfo = array( '$1 --version 2>&1', 'GNU diffutils' ); 00890 00891 $diff3 = self::locateExecutableInDefaultPaths( $names, $versionInfo ); 00892 00893 if ( $diff3 ) { 00894 $this->setVar( 'wgDiff3', $diff3 ); 00895 } else { 00896 $this->setVar( 'wgDiff3', false ); 00897 $this->showMessage( 'config-diff3-bad' ); 00898 } 00899 return true; 00900 } 00901 00906 protected function envCheckGraphics() { 00907 $names = array( wfIsWindows() ? 'convert.exe' : 'convert' ); 00908 $convert = self::locateExecutableInDefaultPaths( $names, array( '$1 -version', 'ImageMagick' ) ); 00909 00910 $this->setVar( 'wgImageMagickConvertCommand', '' ); 00911 if ( $convert ) { 00912 $this->setVar( 'wgImageMagickConvertCommand', $convert ); 00913 $this->showMessage( 'config-imagemagick', $convert ); 00914 return true; 00915 } elseif ( function_exists( 'imagejpeg' ) ) { 00916 $this->showMessage( 'config-gd' ); 00917 00918 } else { 00919 $this->showMessage( 'config-no-scaling' ); 00920 } 00921 return true; 00922 } 00923 00927 protected function envCheckServer() { 00928 $server = $this->envGetDefaultServer(); 00929 $this->showMessage( 'config-using-server', $server ); 00930 $this->setVar( 'wgServer', $server ); 00931 return true; 00932 } 00933 00938 abstract protected function envGetDefaultServer(); 00939 00944 protected function envCheckPath() { 00945 global $IP; 00946 $IP = dirname( dirname( __DIR__ ) ); 00947 $this->setVar( 'IP', $IP ); 00948 00949 $this->showMessage( 'config-using-uri', $this->getVar( 'wgServer' ), $this->getVar( 'wgScriptPath' ) ); 00950 return true; 00951 } 00952 00956 protected function envCheckExtension() { 00957 // @todo FIXME: Detect this properly 00958 if ( defined( 'MW_INSTALL_PHP5_EXT' ) ) { 00959 $ext = 'php5'; 00960 } else { 00961 $ext = 'php'; 00962 } 00963 $this->setVar( 'wgScriptExtension', ".$ext" ); 00964 return true; 00965 } 00966 00971 protected function envCheckShellLocale() { 00972 $os = php_uname( 's' ); 00973 $supported = array( 'Linux', 'SunOS', 'HP-UX', 'Darwin' ); # Tested these 00974 00975 if ( !in_array( $os, $supported ) ) { 00976 return true; 00977 } 00978 00979 # Get a list of available locales. 00980 $ret = false; 00981 $lines = wfShellExec( '/usr/bin/locale -a', $ret ); 00982 00983 if ( $ret ) { 00984 return true; 00985 } 00986 00987 $lines = wfArrayMap( 'trim', explode( "\n", $lines ) ); 00988 $candidatesByLocale = array(); 00989 $candidatesByLang = array(); 00990 00991 foreach ( $lines as $line ) { 00992 if ( $line === '' ) { 00993 continue; 00994 } 00995 00996 if ( !preg_match( '/^([a-zA-Z]+)(_[a-zA-Z]+|)\.(utf8|UTF-8)(@[a-zA-Z_]*|)$/i', $line, $m ) ) { 00997 continue; 00998 } 00999 01000 list( , $lang, , , ) = $m; 01001 01002 $candidatesByLocale[$m[0]] = $m; 01003 $candidatesByLang[$lang][] = $m; 01004 } 01005 01006 # Try the current value of LANG. 01007 if ( isset( $candidatesByLocale[ getenv( 'LANG' ) ] ) ) { 01008 $this->setVar( 'wgShellLocale', getenv( 'LANG' ) ); 01009 return true; 01010 } 01011 01012 # Try the most common ones. 01013 $commonLocales = array( 'en_US.UTF-8', 'en_US.utf8', 'de_DE.UTF-8', 'de_DE.utf8' ); 01014 foreach ( $commonLocales as $commonLocale ) { 01015 if ( isset( $candidatesByLocale[$commonLocale] ) ) { 01016 $this->setVar( 'wgShellLocale', $commonLocale ); 01017 return true; 01018 } 01019 } 01020 01021 # Is there an available locale in the Wiki's language? 01022 $wikiLang = $this->getVar( 'wgLanguageCode' ); 01023 01024 if ( isset( $candidatesByLang[$wikiLang] ) ) { 01025 $m = reset( $candidatesByLang[$wikiLang] ); 01026 $this->setVar( 'wgShellLocale', $m[0] ); 01027 return true; 01028 } 01029 01030 # Are there any at all? 01031 if ( count( $candidatesByLocale ) ) { 01032 $m = reset( $candidatesByLocale ); 01033 $this->setVar( 'wgShellLocale', $m[0] ); 01034 return true; 01035 } 01036 01037 # Give up. 01038 return true; 01039 } 01040 01045 protected function envCheckUploadsDirectory() { 01046 global $IP; 01047 01048 $dir = $IP . '/images/'; 01049 $url = $this->getVar( 'wgServer' ) . $this->getVar( 'wgScriptPath' ) . '/images/'; 01050 $safe = !$this->dirIsExecutable( $dir, $url ); 01051 01052 if ( !$safe ) { 01053 $this->showMessage( 'config-uploads-not-safe', $dir ); 01054 } 01055 return true; 01056 } 01057 01064 protected function envCheckSuhosinMaxValueLength() { 01065 $maxValueLength = ini_get( 'suhosin.get.max_value_length' ); 01066 if ( $maxValueLength > 0 ) { 01067 if( $maxValueLength < 1024 ) { 01068 # Only warn if the value is below the sane 1024 01069 $this->showMessage( 'config-suhosin-max-value-length', $maxValueLength ); 01070 } 01071 } else { 01072 $maxValueLength = -1; 01073 } 01074 $this->setVar( 'wgResourceLoaderMaxQueryLength', $maxValueLength ); 01075 return true; 01076 } 01077 01083 protected function unicodeChar( $c ) { 01084 $c = hexdec( $c ); 01085 if ( $c <= 0x7F ) { 01086 return chr( $c ); 01087 } elseif ( $c <= 0x7FF ) { 01088 return chr( 0xC0 | $c >> 6 ) . chr( 0x80 | $c & 0x3F ); 01089 } elseif ( $c <= 0xFFFF ) { 01090 return chr( 0xE0 | $c >> 12 ) . chr( 0x80 | $c >> 6 & 0x3F ) 01091 . chr( 0x80 | $c & 0x3F ); 01092 } elseif ( $c <= 0x10FFFF ) { 01093 return chr( 0xF0 | $c >> 18 ) . chr( 0x80 | $c >> 12 & 0x3F ) 01094 . chr( 0x80 | $c >> 6 & 0x3F ) 01095 . chr( 0x80 | $c & 0x3F ); 01096 } else { 01097 return false; 01098 } 01099 } 01100 01104 protected function envCheckLibicu() { 01105 $utf8 = function_exists( 'utf8_normalize' ); 01106 $intl = function_exists( 'normalizer_normalize' ); 01107 01115 $not_normal_c = $this->unicodeChar( "FA6C" ); 01116 $normal_c = $this->unicodeChar( "242EE" ); 01117 01118 $useNormalizer = 'php'; 01119 $needsUpdate = false; 01120 01125 if( $utf8 ) { 01126 $useNormalizer = 'utf8'; 01127 $utf8 = utf8_normalize( $not_normal_c, UtfNormal::UNORM_NFC ); 01128 if ( $utf8 !== $normal_c ) { 01129 $needsUpdate = true; 01130 } 01131 } 01132 if( $intl ) { 01133 $useNormalizer = 'intl'; 01134 $intl = normalizer_normalize( $not_normal_c, Normalizer::FORM_C ); 01135 if ( $intl !== $normal_c ) { 01136 $needsUpdate = true; 01137 } 01138 } 01139 01140 // Uses messages 'config-unicode-using-php', 'config-unicode-using-utf8', 'config-unicode-using-intl' 01141 if( $useNormalizer === 'php' ) { 01142 $this->showMessage( 'config-unicode-pure-php-warning' ); 01143 } else { 01144 $this->showMessage( 'config-unicode-using-' . $useNormalizer ); 01145 if( $needsUpdate ) { 01146 $this->showMessage( 'config-unicode-update-warning' ); 01147 } 01148 } 01149 } 01150 01154 protected function envCheckCtype() { 01155 if ( !function_exists( 'ctype_digit' ) ) { 01156 $this->showError( 'config-ctype' ); 01157 return false; 01158 } 01159 return true; 01160 } 01161 01169 protected static function getPossibleBinPaths() { 01170 return array_merge( 01171 array( '/usr/bin', '/usr/local/bin', '/opt/csw/bin', 01172 '/usr/gnu/bin', '/usr/sfw/bin', '/sw/bin', '/opt/local/bin' ), 01173 explode( PATH_SEPARATOR, getenv( 'PATH' ) ) 01174 ); 01175 } 01176 01194 public static function locateExecutable( $path, $names, $versionInfo = false ) { 01195 if ( !is_array( $names ) ) { 01196 $names = array( $names ); 01197 } 01198 01199 foreach ( $names as $name ) { 01200 $command = $path . DIRECTORY_SEPARATOR . $name; 01201 01202 wfSuppressWarnings(); 01203 $file_exists = file_exists( $command ); 01204 wfRestoreWarnings(); 01205 01206 if ( $file_exists ) { 01207 if ( !$versionInfo ) { 01208 return $command; 01209 } 01210 01211 $file = str_replace( '$1', wfEscapeShellArg( $command ), $versionInfo[0] ); 01212 if ( strstr( wfShellExec( $file ), $versionInfo[1] ) !== false ) { 01213 return $command; 01214 } 01215 } 01216 } 01217 return false; 01218 } 01219 01227 public static function locateExecutableInDefaultPaths( $names, $versionInfo = false ) { 01228 foreach( self::getPossibleBinPaths() as $path ) { 01229 $exe = self::locateExecutable( $path, $names, $versionInfo ); 01230 if( $exe !== false ) { 01231 return $exe; 01232 } 01233 } 01234 return false; 01235 } 01236 01245 public function dirIsExecutable( $dir, $url ) { 01246 $scriptTypes = array( 01247 'php' => array( 01248 "<?php echo 'ex' . 'ec';", 01249 "#!/var/env php5\n<?php echo 'ex' . 'ec';", 01250 ), 01251 ); 01252 01253 // it would be good to check other popular languages here, but it'll be slow. 01254 01255 wfSuppressWarnings(); 01256 01257 foreach ( $scriptTypes as $ext => $contents ) { 01258 foreach ( $contents as $source ) { 01259 $file = 'exectest.' . $ext; 01260 01261 if ( !file_put_contents( $dir . $file, $source ) ) { 01262 break; 01263 } 01264 01265 try { 01266 $text = Http::get( $url . $file, array( 'timeout' => 3 ) ); 01267 } 01268 catch( MWException $e ) { 01269 // Http::get throws with allow_url_fopen = false and no curl extension. 01270 $text = null; 01271 } 01272 unlink( $dir . $file ); 01273 01274 if ( $text == 'exec' ) { 01275 wfRestoreWarnings(); 01276 return $ext; 01277 } 01278 } 01279 } 01280 01281 wfRestoreWarnings(); 01282 01283 return false; 01284 } 01285 01292 public static function apacheModulePresent( $moduleName ) { 01293 if ( function_exists( 'apache_get_modules' ) && in_array( $moduleName, apache_get_modules() ) ) { 01294 return true; 01295 } 01296 // try it the hard way 01297 ob_start(); 01298 phpinfo( INFO_MODULES ); 01299 $info = ob_get_clean(); 01300 return strpos( $info, $moduleName ) !== false; 01301 } 01302 01308 public function setParserLanguage( $lang ) { 01309 $this->parserOptions->setTargetLanguage( $lang ); 01310 $this->parserOptions->setUserLang( $lang ); 01311 } 01312 01318 protected function getDocUrl( $page ) { 01319 return "{$_SERVER['PHP_SELF']}?page=" . urlencode( $page ); 01320 } 01321 01328 public function findExtensions() { 01329 if( $this->getVar( 'IP' ) === null ) { 01330 return false; 01331 } 01332 01333 $exts = array(); 01334 $extDir = $this->getVar( 'IP' ) . '/extensions'; 01335 $dh = opendir( $extDir ); 01336 01337 while ( ( $file = readdir( $dh ) ) !== false ) { 01338 if( !is_dir( "$extDir/$file" ) ) { 01339 continue; 01340 } 01341 if( file_exists( "$extDir/$file/$file.php" ) ) { 01342 $exts[] = $file; 01343 } 01344 } 01345 natcasesort( $exts ); 01346 01347 return $exts; 01348 } 01349 01355 protected function includeExtensions() { 01356 global $IP; 01357 $exts = $this->getVar( '_Extensions' ); 01358 $IP = $this->getVar( 'IP' ); 01359 01368 global $wgAutoloadClasses; 01369 $wgAutoloadClasses = array(); 01370 01371 require( "$IP/includes/DefaultSettings.php" ); 01372 01373 foreach( $exts as $e ) { 01374 require_once( "$IP/extensions/$e/$e.php" ); 01375 } 01376 01377 $hooksWeWant = isset( $wgHooks['LoadExtensionSchemaUpdates'] ) ? 01378 $wgHooks['LoadExtensionSchemaUpdates'] : array(); 01379 01380 // Unset everyone else's hooks. Lord knows what someone might be doing 01381 // in ParserFirstCallInit (see bug 27171) 01382 $GLOBALS['wgHooks'] = array( 'LoadExtensionSchemaUpdates' => $hooksWeWant ); 01383 01384 return Status::newGood(); 01385 } 01386 01399 protected function getInstallSteps( DatabaseInstaller $installer ) { 01400 $coreInstallSteps = array( 01401 array( 'name' => 'database', 'callback' => array( $installer, 'setupDatabase' ) ), 01402 array( 'name' => 'tables', 'callback' => array( $installer, 'createTables' ) ), 01403 array( 'name' => 'interwiki', 'callback' => array( $installer, 'populateInterwikiTable' ) ), 01404 array( 'name' => 'stats', 'callback' => array( $this, 'populateSiteStats' ) ), 01405 array( 'name' => 'keys', 'callback' => array( $this, 'generateKeys' ) ), 01406 array( 'name' => 'sysop', 'callback' => array( $this, 'createSysop' ) ), 01407 array( 'name' => 'mainpage', 'callback' => array( $this, 'createMainpage' ) ), 01408 ); 01409 01410 // Build the array of install steps starting from the core install list, 01411 // then adding any callbacks that wanted to attach after a given step 01412 foreach( $coreInstallSteps as $step ) { 01413 $this->installSteps[] = $step; 01414 if( isset( $this->extraInstallSteps[ $step['name'] ] ) ) { 01415 $this->installSteps = array_merge( 01416 $this->installSteps, 01417 $this->extraInstallSteps[ $step['name'] ] 01418 ); 01419 } 01420 } 01421 01422 // Prepend any steps that want to be at the beginning 01423 if( isset( $this->extraInstallSteps['BEGINNING'] ) ) { 01424 $this->installSteps = array_merge( 01425 $this->extraInstallSteps['BEGINNING'], 01426 $this->installSteps 01427 ); 01428 } 01429 01430 // Extensions should always go first, chance to tie into hooks and such 01431 if( count( $this->getVar( '_Extensions' ) ) ) { 01432 array_unshift( $this->installSteps, 01433 array( 'name' => 'extensions', 'callback' => array( $this, 'includeExtensions' ) ) 01434 ); 01435 $this->installSteps[] = array( 01436 'name' => 'extension-tables', 01437 'callback' => array( $installer, 'createExtensionTables' ) 01438 ); 01439 } 01440 return $this->installSteps; 01441 } 01442 01451 public function performInstallation( $startCB, $endCB ) { 01452 $installResults = array(); 01453 $installer = $this->getDBInstaller(); 01454 $installer->preInstall(); 01455 $steps = $this->getInstallSteps( $installer ); 01456 foreach( $steps as $stepObj ) { 01457 $name = $stepObj['name']; 01458 call_user_func_array( $startCB, array( $name ) ); 01459 01460 // Perform the callback step 01461 $status = call_user_func( $stepObj['callback'], $installer ); 01462 01463 // Output and save the results 01464 call_user_func( $endCB, $name, $status ); 01465 $installResults[$name] = $status; 01466 01467 // If we've hit some sort of fatal, we need to bail. 01468 // Callback already had a chance to do output above. 01469 if( !$status->isOk() ) { 01470 break; 01471 } 01472 } 01473 if( $status->isOk() ) { 01474 $this->setVar( '_InstallDone', true ); 01475 } 01476 return $installResults; 01477 } 01478 01484 public function generateKeys() { 01485 $keys = array( 'wgSecretKey' => 64 ); 01486 if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) { 01487 $keys['wgUpgradeKey'] = 16; 01488 } 01489 return $this->doGenerateKeys( $keys ); 01490 } 01491 01499 protected function doGenerateKeys( $keys ) { 01500 $status = Status::newGood(); 01501 01502 $strong = true; 01503 foreach ( $keys as $name => $length ) { 01504 $secretKey = MWCryptRand::generateHex( $length, true ); 01505 if ( !MWCryptRand::wasStrong() ) { 01506 $strong = false; 01507 } 01508 01509 $this->setVar( $name, $secretKey ); 01510 } 01511 01512 if ( !$strong ) { 01513 $names = array_keys( $keys ); 01514 $names = preg_replace( '/^(.*)$/', '\$$1', $names ); 01515 global $wgLang; 01516 $status->warning( 'config-insecure-keys', $wgLang->listToText( $names ), count( $names ) ); 01517 } 01518 01519 return $status; 01520 } 01521 01527 protected function createSysop() { 01528 $name = $this->getVar( '_AdminName' ); 01529 $user = User::newFromName( $name ); 01530 01531 if ( !$user ) { 01532 // We should've validated this earlier anyway! 01533 return Status::newFatal( 'config-admin-error-user', $name ); 01534 } 01535 01536 if ( $user->idForName() == 0 ) { 01537 $user->addToDatabase(); 01538 01539 try { 01540 $user->setPassword( $this->getVar( '_AdminPassword' ) ); 01541 } catch( PasswordError $pwe ) { 01542 return Status::newFatal( 'config-admin-error-password', $name, $pwe->getMessage() ); 01543 } 01544 01545 $user->addGroup( 'sysop' ); 01546 $user->addGroup( 'bureaucrat' ); 01547 if( $this->getVar( '_AdminEmail' ) ) { 01548 $user->setEmail( $this->getVar( '_AdminEmail' ) ); 01549 } 01550 $user->saveSettings(); 01551 01552 // Update user count 01553 $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 ); 01554 $ssUpdate->doUpdate(); 01555 } 01556 $status = Status::newGood(); 01557 01558 if( $this->getVar( '_Subscribe' ) && $this->getVar( '_AdminEmail' ) ) { 01559 $this->subscribeToMediaWikiAnnounce( $status ); 01560 } 01561 01562 return $status; 01563 } 01564 01568 private function subscribeToMediaWikiAnnounce( Status $s ) { 01569 $params = array( 01570 'email' => $this->getVar( '_AdminEmail' ), 01571 'language' => 'en', 01572 'digest' => 0 01573 ); 01574 01575 // Mailman doesn't support as many languages as we do, so check to make 01576 // sure their selected language is available 01577 $myLang = $this->getVar( '_UserLang' ); 01578 if( in_array( $myLang, $this->mediaWikiAnnounceLanguages ) ) { 01579 $myLang = $myLang == 'pt-br' ? 'pt_BR' : $myLang; // rewrite to Mailman's pt_BR 01580 $params['language'] = $myLang; 01581 } 01582 01583 if( MWHttpRequest::canMakeRequests() ) { 01584 $res = MWHttpRequest::factory( $this->mediaWikiAnnounceUrl, 01585 array( 'method' => 'POST', 'postData' => $params ) )->execute(); 01586 if( !$res->isOK() ) { 01587 $s->warning( 'config-install-subscribe-fail', $res->getMessage() ); 01588 } 01589 } else { 01590 $s->warning( 'config-install-subscribe-notpossible' ); 01591 } 01592 } 01593 01600 protected function createMainpage( DatabaseInstaller $installer ) { 01601 $status = Status::newGood(); 01602 try { 01603 $page = WikiPage::factory( Title::newMainPage() ); 01604 $content = new WikitextContent ( 01605 wfMessage( 'mainpagetext' )->inContentLanguage()->text() . "\n\n" . 01606 wfMessage( 'mainpagedocfooter' )->inContentLanguage()->text() 01607 ); 01608 01609 $page->doEditContent( $content, 01610 '', 01611 EDIT_NEW, 01612 false, 01613 User::newFromName( 'MediaWiki default' ) ); 01614 } catch (MWException $e) { 01615 //using raw, because $wgShowExceptionDetails can not be set yet 01616 $status->fatal( 'config-install-mainpage-failed', $e->getMessage() ); 01617 } 01618 01619 return $status; 01620 } 01621 01625 public static function overrideConfig() { 01626 define( 'MW_NO_SESSION', 1 ); 01627 01628 // Don't access the database 01629 $GLOBALS['wgUseDatabaseMessages'] = false; 01630 // Don't cache langconv tables 01631 $GLOBALS['wgLanguageConverterCacheType'] = CACHE_NONE; 01632 // Debug-friendly 01633 $GLOBALS['wgShowExceptionDetails'] = true; 01634 // Don't break forms 01635 $GLOBALS['wgExternalLinkTarget'] = '_blank'; 01636 01637 // Extended debugging 01638 $GLOBALS['wgShowSQLErrors'] = true; 01639 $GLOBALS['wgShowDBErrorBacktrace'] = true; 01640 01641 // Allow multiple ob_flush() calls 01642 $GLOBALS['wgDisableOutputCompression'] = true; 01643 01644 // Use a sensible cookie prefix (not my_wiki) 01645 $GLOBALS['wgCookiePrefix'] = 'mw_installer'; 01646 01647 // Some of the environment checks make shell requests, remove limits 01648 $GLOBALS['wgMaxShellMemory'] = 0; 01649 } 01650 01658 public function addInstallStep( $callback, $findStep = 'BEGINNING' ) { 01659 $this->extraInstallSteps[$findStep][] = $callback; 01660 } 01661 01666 protected function disableTimeLimit() { 01667 wfSuppressWarnings(); 01668 set_time_limit( 0 ); 01669 wfRestoreWarnings(); 01670 } 01671 }