From 6a2123260b38625e97df3124d11ccec8a2e7f100 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Wed, 15 Jun 2011 07:35:47 +0000 Subject: [PATCH] * (bug 28798) Set $wgServer in the default LocalSettings.php * (bug 14977) When detecting $wgServer, treat IPv6 addresses in $_SERVER['SERVER_NAME'] etc. in a sensible way. * Tests for the new functions in IP.php and Installer.php --- includes/IP.php | 70 ++++++++++++ includes/installer/Installer.i18n.php | 1 + includes/installer/Installer.php | 48 ++++++++- includes/installer/LocalSettingsGenerator.php | 8 +- includes/installer/WebInstallerPage.php | 12 +-- tests/phpunit/includes/IPTest.php | 52 +++++++++ .../includes/installer/InstallerTest.php | 102 ++++++++++++++++++ 7 files changed, 284 insertions(+), 9 deletions(-) create mode 100644 tests/phpunit/includes/installer/InstallerTest.php diff --git a/includes/IP.php b/includes/IP.php index 9b55ad22fc..055fe85c05 100644 --- a/includes/IP.php +++ b/includes/IP.php @@ -185,6 +185,76 @@ class IP { return $ip; } + /** + * Given a host/port string, like one might find in the host part of a URL + * per RFC 2732, split the hostname part and the port part and return an + * array with an element for each. If there is no port part, the array will + * have false in place of the port. If the string was invalid in some way, + * false is returned. + * + * This was easy with IPv4 and was generally done in an ad-hoc way, but + * with IPv6 it's somewhat more complicated due to the need to parse the + * square brackets and colons. + * + * A bare IPv6 address is accepted despite the lack of square brackets. + * + * @param $both The string with the host and port + * @return array + */ + public static function splitHostAndPort( $both ) { + if ( substr( $both, 0, 1 ) === '[' ) { + if ( preg_match( '/^\[(' . RE_IPV6_ADD . ')\](?::(?P\d+))?$/', $both, $m ) ) { + if ( isset( $m['port'] ) ) { + return array( $m[1], intval( $m['port'] ) ); + } else { + return array( $m[1], false ); + } + } else { + // Square bracket found but no IPv6 + return false; + } + } + $numColons = substr_count( $both, ':' ); + if ( $numColons >= 2 ) { + // Is it a bare IPv6 address? + if ( preg_match( '/^' . RE_IPV6_ADD . '$/', $both ) ) { + return array( $both, false ); + } else { + // Not valid IPv6, but too many colons for anything else + return false; + } + } + if ( $numColons >= 1 ) { + // Host:port? + $bits = explode( ':', $both ); + if ( preg_match( '/^\d+/', $bits[1] ) ) { + return array( $bits[0], intval( $bits[1] ) ); + } else { + // Not a valid port + return false; + } + } + // Plain hostname + return array( $both, false ); + } + + /** + * Given a host name and a port, combine them into host/port string like + * you might find in a URL. If the host contains a colon, wrap it in square + * brackets like in RFC 2732. If the port matches the default port, omit + * the port specification + */ + public static function combineHostAndPort( $host, $port, $defaultPort = false ) { + if ( strpos( $host, ':' ) !== false ) { + $host = "[$host]"; + } + if ( $defaultPort !== false && $port == $defaultPort ) { + return $host; + } else { + return "$host:$port"; + } + } + /** * Given an unsigned integer, returns an IPv6 address in octet notation * diff --git a/includes/installer/Installer.i18n.php b/includes/installer/Installer.i18n.php index b2850bb42f..009d0931d0 100644 --- a/includes/installer/Installer.i18n.php +++ b/includes/installer/Installer.i18n.php @@ -147,6 +147,7 @@ Image thumbnailing will be enabled if you enable uploads.', Image thumbnailing will be disabled.', 'config-no-uri' => "'''Error:''' Could not determine the current URI. Installation aborted.", + 'config-using-server' => 'Using server name "$1".', 'config-uploads-not-safe' => "'''Warning:''' Your default directory for uploads $1 is vulnerable to arbitrary scripts execution. Although MediaWiki checks all uploaded files for security threats, it is highly recommended to [http://www.mediawiki.org/wiki/Manual:Security#Upload_security close this security vulnerability] before enabling uploads.", 'config-brokenlibxml' => 'Your system has a combination of PHP and libxml2 versions which is buggy and can cause hidden data corruption in MediaWiki and other web applications. diff --git a/includes/installer/Installer.php b/includes/installer/Installer.php index 55de557373..2bee742db3 100644 --- a/includes/installer/Installer.php +++ b/includes/installer/Installer.php @@ -99,6 +99,7 @@ abstract class Installer { 'envCheckCache', 'envCheckDiff3', 'envCheckGraphics', + 'envCheckServer', 'envCheckPath', 'envCheckExtension', 'envCheckShellLocale', @@ -131,6 +132,8 @@ abstract class Installer { 'wgDiff3', 'wgImageMagickConvertCommand', 'IP', + 'wgServer', + 'wgProto', 'wgScriptPath', 'wgScriptExtension', 'wgMetaNamespace', @@ -837,6 +840,47 @@ abstract class Installer { } } + /** + * Environment check for the server hostname. + */ + protected function envCheckServer() { + if ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on') { + $proto = 'https'; + $stdPort = 443; + } else { + $proto = 'http'; + $stdPort = 80; + } + + $varNames = array( 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ); + $host = 'localhost'; + $port = $stdPort; + foreach ( $varNames as $varName ) { + if ( !isset( $_SERVER[$varName] ) ) { + continue; + } + $parts = IP::splitHostAndPort( $_SERVER[$varName] ); + if ( !$parts ) { + // Invalid, do not use + continue; + } + $host = $parts[0]; + if ( $parts[1] === false ) { + if ( isset( $_SERVER['SERVER_PORT'] ) ) { + $port = $_SERVER['SERVER_PORT']; + } // else leave it as $stdPort + } else { + $port = $parts[1]; + } + break; + } + + $server = $proto . '://' . IP::combineHostAndPort( $host, $port, $stdPort ); + $this->showMessage( 'config-using-server', $server ); + $this->setVar( 'wgServer', $server ); + $this->setVar( 'wgProto', $proto ); + } + /** * Environment check for setting $IP and $wgScriptPath. */ @@ -955,10 +999,10 @@ abstract class Installer { * TODO: document */ protected function envCheckUploadsDirectory() { - global $IP, $wgServer; + global $IP; $dir = $IP . '/images/'; - $url = $wgServer . $this->getVar( 'wgScriptPath' ) . '/images/'; + $url = $this->getVar( 'wgServer' ) . $this->getVar( 'wgScriptPath' ) . '/images/'; $safe = !$this->dirIsExecutable( $dir, $url ); if ( $safe ) { diff --git a/includes/installer/LocalSettingsGenerator.php b/includes/installer/LocalSettingsGenerator.php index a08a6970e8..76056748df 100644 --- a/includes/installer/LocalSettingsGenerator.php +++ b/includes/installer/LocalSettingsGenerator.php @@ -39,7 +39,7 @@ class LocalSettingsGenerator { $confItems = array_merge( array( - 'wgScriptPath', 'wgScriptExtension', + 'wgServer', 'wgProto', 'wgScriptPath', 'wgScriptExtension', 'wgPasswordSender', 'wgImageMagickConvertCommand', 'wgShellLocale', 'wgLanguageCode', 'wgEnableEmail', 'wgEnableUserEmail', 'wgDiff3', 'wgEnotifUserTalk', 'wgEnotifWatchlist', 'wgEmailAuthentication', @@ -249,6 +249,12 @@ if ( !defined( 'MEDIAWIKI' ) ) { \$wgScriptPath = \"{$this->values['wgScriptPath']}\"; \$wgScriptExtension = \"{$this->values['wgScriptExtension']}\"; +## The server name to use in fully-qualified URLs +\$wgServer = \"{$this->values['wgServer']}\"; + +## The URL protocol, may be http or https +\$wgProto = \"{$this->values['wgProto']}\"; + ## The relative URL path to the skins directory \$wgStylePath = \"\$wgScriptPath/skins\"; diff --git a/includes/installer/WebInstallerPage.php b/includes/installer/WebInstallerPage.php index e56db83b92..9b303c8e2b 100644 --- a/includes/installer/WebInstallerPage.php +++ b/includes/installer/WebInstallerPage.php @@ -531,7 +531,7 @@ class WebInstaller_Upgrade extends WebInstallerPage { $this->addHTML( $this->parent->getInfoBox( wfMsgNoTrans( $msg, - $GLOBALS['wgServer'] . + $this->getVar( 'wgServer' ) . $this->getVar( 'wgScriptPath' ) . '/index' . $this->getVar( 'wgScriptExtension' ) ), 'tick-32.png' @@ -934,8 +934,8 @@ class WebInstaller_Options extends WebInstallerPage { * @return string */ public function getCCPartnerUrl() { - global $wgServer; - $exitUrl = $wgServer . $this->parent->getUrl( array( + $server = $this->getVar( 'wgServer' ); + $exitUrl = $server . $this->parent->getUrl( array( 'page' => 'Options', 'SubmitCC' => 'indeed', 'config__LicenseCode' => 'cc', @@ -943,7 +943,7 @@ class WebInstaller_Options extends WebInstallerPage { 'config_wgRightsText' => '[license_name]', 'config_wgRightsIcon' => '[license_button]', ) ); - $styleUrl = $wgServer . dirname( dirname( $this->parent->getUrl() ) ) . + $styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) . '/skins/common/config-cc.css'; $iframeUrl = 'http://creativecommons.org/license/?' . wfArrayToCGI( array( @@ -1147,7 +1147,7 @@ class WebInstaller_Complete extends WebInstallerPage { public function execute() { // Pop up a dialog box, to make it difficult for the user to forget // to download the file - $lsUrl = $GLOBALS['wgServer'] . $this->parent->getURL( array( 'localsettings' => 1 ) ); + $lsUrl = $this->getVar( 'wgServer' ) . $this->parent->getURL( array( 'localsettings' => 1 ) ); if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false ) { // JS appears the only method that works consistently with IE7+ $this->addHtml( "\n