Index: trunk/phase3/tests/phpunit/includes/installer/InstallerTest.php |
— | — | @@ -0,0 +1,102 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class Installer_TestHelper extends Installer { |
| 5 | + function showMessage( $msg ) {} |
| 6 | + function showError( $msg ) {} |
| 7 | + function showStatusMessage( Status $status ) {} |
| 8 | + |
| 9 | + function __construct() { |
| 10 | + $this->settings = array(); |
| 11 | + } |
| 12 | + |
| 13 | +} |
| 14 | + |
| 15 | +class InstallerTest extends MediaWikiTestCase { |
| 16 | + /** |
| 17 | + * @dataProvider provideEnvCheckServer |
| 18 | + */ |
| 19 | + function testEnvCheckServer( $expected, $input, $description ) { |
| 20 | + $installer = new Installer_TestHelper; |
| 21 | + $oldServer = $_SERVER; |
| 22 | + $_SERVER = $input; |
| 23 | + $rm = new ReflectionMethod( 'Installer_TestHelper', 'envCheckServer' ); |
| 24 | + $rm->setAccessible( true ); |
| 25 | + $rm->invoke( $installer ); |
| 26 | + $_SERVER = $oldServer; |
| 27 | + $this->assertEquals( $expected, $installer->getVar( 'wgServer' ), $description ); |
| 28 | + } |
| 29 | + |
| 30 | + function provideEnvCheckServer() { |
| 31 | + return array( |
| 32 | + array( |
| 33 | + 'http://x', |
| 34 | + array( |
| 35 | + 'HTTP_HOST' => 'x' |
| 36 | + ), |
| 37 | + 'Host header' |
| 38 | + ), |
| 39 | + array( |
| 40 | + 'https://x', |
| 41 | + array( |
| 42 | + 'HTTP_HOST' => 'x', |
| 43 | + 'HTTPS' => 'on', |
| 44 | + ), |
| 45 | + 'Host header with secure' |
| 46 | + ), |
| 47 | + array( |
| 48 | + 'http://x', |
| 49 | + array( |
| 50 | + 'HTTP_HOST' => 'x', |
| 51 | + 'SERVER_PORT' => 80, |
| 52 | + ), |
| 53 | + 'Default SERVER_PORT', |
| 54 | + ), |
| 55 | + array( |
| 56 | + 'http://x', |
| 57 | + array( |
| 58 | + 'HTTP_HOST' => 'x', |
| 59 | + 'HTTPS' => 'off', |
| 60 | + ), |
| 61 | + 'Secure off' |
| 62 | + ), |
| 63 | + array( |
| 64 | + 'http://y', |
| 65 | + array( |
| 66 | + 'SERVER_NAME' => 'y', |
| 67 | + ), |
| 68 | + 'Server name' |
| 69 | + ), |
| 70 | + array( |
| 71 | + 'http://x', |
| 72 | + array( |
| 73 | + 'HTTP_HOST' => 'x', |
| 74 | + 'SERVER_NAME' => 'y', |
| 75 | + ), |
| 76 | + 'Host server name precedence' |
| 77 | + ), |
| 78 | + array( |
| 79 | + 'http://[::1]:81', |
| 80 | + array( |
| 81 | + 'HTTP_HOST' => '[::1]', |
| 82 | + 'SERVER_NAME' => '::1', |
| 83 | + 'SERVER_PORT' => '81', |
| 84 | + ), |
| 85 | + 'Apache bug 26005' |
| 86 | + ), |
| 87 | + array( |
| 88 | + 'http://localhost', |
| 89 | + array( |
| 90 | + 'SERVER_NAME' => '[2001' |
| 91 | + ), |
| 92 | + 'Kind of like lighttpd per commit message in MW r83847', |
| 93 | + ), |
| 94 | + array( |
| 95 | + 'http://[2a01:e35:2eb4:1::2]:777', |
| 96 | + array( |
| 97 | + 'SERVER_NAME' => '[2a01:e35:2eb4:1::2]:777' |
| 98 | + ), |
| 99 | + 'Possible lighttpd environment per bug 14977 comment 13', |
| 100 | + ), |
| 101 | + ); |
| 102 | + } |
| 103 | +} |
Property changes on: trunk/phase3/tests/phpunit/includes/installer/InstallerTest.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 104 | + native |
Index: trunk/phase3/tests/phpunit/includes/IPTest.php |
— | — | @@ -426,4 +426,56 @@ |
427 | 427 | array( false, '2001:0DB8:F::', '2001:DB8::/96' ), |
428 | 428 | ); |
429 | 429 | } |
| 430 | + |
| 431 | + /** |
| 432 | + * Test for IP::splitHostAndPort(). |
| 433 | + * @dataProvider provideSplitHostAndPort |
| 434 | + */ |
| 435 | + function testSplitHostAndPort( $expected, $input, $description ) { |
| 436 | + $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description ); |
| 437 | + } |
| 438 | + |
| 439 | + /** |
| 440 | + * Provider for IP::splitHostAndPort() |
| 441 | + */ |
| 442 | + function provideSplitHostAndPort() { |
| 443 | + return array( |
| 444 | + array( false, '[', 'Unclosed square bracket' ), |
| 445 | + array( false, '[::', 'Unclosed square bracket 2' ), |
| 446 | + array( array( '::', false ), '::', 'Bare IPv6 0' ), |
| 447 | + array( array( '::1', false ), '::1', 'Bare IPv6 1' ), |
| 448 | + array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ), |
| 449 | + array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ), |
| 450 | + array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ), |
| 451 | + array( false, '::x', 'Double colon but no IPv6' ), |
| 452 | + array( array( 'x', 80 ), 'x:80', 'Hostname and port' ), |
| 453 | + array( false, 'x:x', 'Hostname and invalid port' ), |
| 454 | + array( array( 'x', false ), 'x', 'Plain hostname' ) |
| 455 | + ); |
| 456 | + } |
| 457 | + |
| 458 | + /** |
| 459 | + * Test for IP::combineHostAndPort() |
| 460 | + * @dataProvider provideCombineHostAndPort |
| 461 | + */ |
| 462 | + function testCombineHostAndPort( $expected, $input, $description ) { |
| 463 | + list( $host, $port, $defaultPort ) = $input; |
| 464 | + $this->assertEquals( |
| 465 | + $expected, |
| 466 | + IP::combineHostAndPort( $host, $port, $defaultPort ), |
| 467 | + $description ); |
| 468 | + } |
| 469 | + |
| 470 | + /** |
| 471 | + * Provider for IP::combineHostAndPort() |
| 472 | + */ |
| 473 | + function provideCombineHostAndPort() { |
| 474 | + return array( |
| 475 | + array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ), |
| 476 | + array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ), |
| 477 | + array( 'x', array( 'x', 2, 2 ), 'Normal default port' ), |
| 478 | + array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ), |
| 479 | + ); |
| 480 | + } |
| 481 | + |
430 | 482 | } |
Index: trunk/phase3/includes/installer/Installer.php |
— | — | @@ -99,6 +99,7 @@ |
100 | 100 | 'envCheckCache', |
101 | 101 | 'envCheckDiff3', |
102 | 102 | 'envCheckGraphics', |
| 103 | + 'envCheckServer', |
103 | 104 | 'envCheckPath', |
104 | 105 | 'envCheckExtension', |
105 | 106 | 'envCheckShellLocale', |
— | — | @@ -131,6 +132,8 @@ |
132 | 133 | 'wgDiff3', |
133 | 134 | 'wgImageMagickConvertCommand', |
134 | 135 | 'IP', |
| 136 | + 'wgServer', |
| 137 | + 'wgProto', |
135 | 138 | 'wgScriptPath', |
136 | 139 | 'wgScriptExtension', |
137 | 140 | 'wgMetaNamespace', |
— | — | @@ -838,6 +841,47 @@ |
839 | 842 | } |
840 | 843 | |
841 | 844 | /** |
| 845 | + * Environment check for the server hostname. |
| 846 | + */ |
| 847 | + protected function envCheckServer() { |
| 848 | + if ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on') { |
| 849 | + $proto = 'https'; |
| 850 | + $stdPort = 443; |
| 851 | + } else { |
| 852 | + $proto = 'http'; |
| 853 | + $stdPort = 80; |
| 854 | + } |
| 855 | + |
| 856 | + $varNames = array( 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ); |
| 857 | + $host = 'localhost'; |
| 858 | + $port = $stdPort; |
| 859 | + foreach ( $varNames as $varName ) { |
| 860 | + if ( !isset( $_SERVER[$varName] ) ) { |
| 861 | + continue; |
| 862 | + } |
| 863 | + $parts = IP::splitHostAndPort( $_SERVER[$varName] ); |
| 864 | + if ( !$parts ) { |
| 865 | + // Invalid, do not use |
| 866 | + continue; |
| 867 | + } |
| 868 | + $host = $parts[0]; |
| 869 | + if ( $parts[1] === false ) { |
| 870 | + if ( isset( $_SERVER['SERVER_PORT'] ) ) { |
| 871 | + $port = $_SERVER['SERVER_PORT']; |
| 872 | + } // else leave it as $stdPort |
| 873 | + } else { |
| 874 | + $port = $parts[1]; |
| 875 | + } |
| 876 | + break; |
| 877 | + } |
| 878 | + |
| 879 | + $server = $proto . '://' . IP::combineHostAndPort( $host, $port, $stdPort ); |
| 880 | + $this->showMessage( 'config-using-server', $server ); |
| 881 | + $this->setVar( 'wgServer', $server ); |
| 882 | + $this->setVar( 'wgProto', $proto ); |
| 883 | + } |
| 884 | + |
| 885 | + /** |
842 | 886 | * Environment check for setting $IP and $wgScriptPath. |
843 | 887 | */ |
844 | 888 | protected function envCheckPath() { |
— | — | @@ -955,10 +999,10 @@ |
956 | 1000 | * TODO: document |
957 | 1001 | */ |
958 | 1002 | protected function envCheckUploadsDirectory() { |
959 | | - global $IP, $wgServer; |
| 1003 | + global $IP; |
960 | 1004 | |
961 | 1005 | $dir = $IP . '/images/'; |
962 | | - $url = $wgServer . $this->getVar( 'wgScriptPath' ) . '/images/'; |
| 1006 | + $url = $this->getVar( 'wgServer' ) . $this->getVar( 'wgScriptPath' ) . '/images/'; |
963 | 1007 | $safe = !$this->dirIsExecutable( $dir, $url ); |
964 | 1008 | |
965 | 1009 | if ( $safe ) { |
Index: trunk/phase3/includes/installer/Installer.i18n.php |
— | — | @@ -147,6 +147,7 @@ |
148 | 148 | Image thumbnailing will be disabled.', |
149 | 149 | 'config-no-uri' => "'''Error:''' Could not determine the current URI. |
150 | 150 | Installation aborted.", |
| 151 | + 'config-using-server' => 'Using server name "<nowiki>$1</nowiki>".', |
151 | 152 | 'config-uploads-not-safe' => "'''Warning:''' Your default directory for uploads <code>$1</code> is vulnerable to arbitrary scripts execution. |
152 | 153 | 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.", |
153 | 154 | '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. |
Index: trunk/phase3/includes/installer/LocalSettingsGenerator.php |
— | — | @@ -39,7 +39,7 @@ |
40 | 40 | |
41 | 41 | $confItems = array_merge( |
42 | 42 | array( |
43 | | - 'wgScriptPath', 'wgScriptExtension', |
| 43 | + 'wgServer', 'wgProto', 'wgScriptPath', 'wgScriptExtension', |
44 | 44 | 'wgPasswordSender', 'wgImageMagickConvertCommand', 'wgShellLocale', |
45 | 45 | 'wgLanguageCode', 'wgEnableEmail', 'wgEnableUserEmail', 'wgDiff3', |
46 | 46 | 'wgEnotifUserTalk', 'wgEnotifWatchlist', 'wgEmailAuthentication', |
— | — | @@ -249,6 +249,12 @@ |
250 | 250 | \$wgScriptPath = \"{$this->values['wgScriptPath']}\"; |
251 | 251 | \$wgScriptExtension = \"{$this->values['wgScriptExtension']}\"; |
252 | 252 | |
| 253 | +## The server name to use in fully-qualified URLs |
| 254 | +\$wgServer = \"{$this->values['wgServer']}\"; |
| 255 | + |
| 256 | +## The URL protocol, may be http or https |
| 257 | +\$wgProto = \"{$this->values['wgProto']}\"; |
| 258 | + |
253 | 259 | ## The relative URL path to the skins directory |
254 | 260 | \$wgStylePath = \"\$wgScriptPath/skins\"; |
255 | 261 | |
Index: trunk/phase3/includes/installer/WebInstallerPage.php |
— | — | @@ -531,7 +531,7 @@ |
532 | 532 | $this->addHTML( |
533 | 533 | $this->parent->getInfoBox( |
534 | 534 | wfMsgNoTrans( $msg, |
535 | | - $GLOBALS['wgServer'] . |
| 535 | + $this->getVar( 'wgServer' ) . |
536 | 536 | $this->getVar( 'wgScriptPath' ) . '/index' . |
537 | 537 | $this->getVar( 'wgScriptExtension' ) |
538 | 538 | ), 'tick-32.png' |
— | — | @@ -934,8 +934,8 @@ |
935 | 935 | * @return string |
936 | 936 | */ |
937 | 937 | public function getCCPartnerUrl() { |
938 | | - global $wgServer; |
939 | | - $exitUrl = $wgServer . $this->parent->getUrl( array( |
| 938 | + $server = $this->getVar( 'wgServer' ); |
| 939 | + $exitUrl = $server . $this->parent->getUrl( array( |
940 | 940 | 'page' => 'Options', |
941 | 941 | 'SubmitCC' => 'indeed', |
942 | 942 | 'config__LicenseCode' => 'cc', |
— | — | @@ -943,7 +943,7 @@ |
944 | 944 | 'config_wgRightsText' => '[license_name]', |
945 | 945 | 'config_wgRightsIcon' => '[license_button]', |
946 | 946 | ) ); |
947 | | - $styleUrl = $wgServer . dirname( dirname( $this->parent->getUrl() ) ) . |
| 947 | + $styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) . |
948 | 948 | '/skins/common/config-cc.css'; |
949 | 949 | $iframeUrl = 'http://creativecommons.org/license/?' . |
950 | 950 | wfArrayToCGI( array( |
— | — | @@ -1147,7 +1147,7 @@ |
1148 | 1148 | public function execute() { |
1149 | 1149 | // Pop up a dialog box, to make it difficult for the user to forget |
1150 | 1150 | // to download the file |
1151 | | - $lsUrl = $GLOBALS['wgServer'] . $this->parent->getURL( array( 'localsettings' => 1 ) ); |
| 1151 | + $lsUrl = $this->getVar( 'wgServer' ) . $this->parent->getURL( array( 'localsettings' => 1 ) ); |
1152 | 1152 | if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false ) { |
1153 | 1153 | // JS appears the only method that works consistently with IE7+ |
1154 | 1154 | $this->addHtml( "\n<script type=\"" . $GLOBALS['wgJsMimeType'] . '">jQuery( document ).ready( function() { document.location=' |
— | — | @@ -1162,7 +1162,7 @@ |
1163 | 1163 | $this->parent->getInfoBox( |
1164 | 1164 | wfMsgNoTrans( 'config-install-done', |
1165 | 1165 | $lsUrl, |
1166 | | - $GLOBALS['wgServer'] . |
| 1166 | + $this->getVar( 'wgServer' ) . |
1167 | 1167 | $this->getVar( 'wgScriptPath' ) . '/index' . |
1168 | 1168 | $this->getVar( 'wgScriptExtension' ), |
1169 | 1169 | '<downloadlink/>' |
Index: trunk/phase3/includes/IP.php |
— | — | @@ -186,6 +186,76 @@ |
187 | 187 | } |
188 | 188 | |
189 | 189 | /** |
| 190 | + * Given a host/port string, like one might find in the host part of a URL |
| 191 | + * per RFC 2732, split the hostname part and the port part and return an |
| 192 | + * array with an element for each. If there is no port part, the array will |
| 193 | + * have false in place of the port. If the string was invalid in some way, |
| 194 | + * false is returned. |
| 195 | + * |
| 196 | + * This was easy with IPv4 and was generally done in an ad-hoc way, but |
| 197 | + * with IPv6 it's somewhat more complicated due to the need to parse the |
| 198 | + * square brackets and colons. |
| 199 | + * |
| 200 | + * A bare IPv6 address is accepted despite the lack of square brackets. |
| 201 | + * |
| 202 | + * @param $both The string with the host and port |
| 203 | + * @return array |
| 204 | + */ |
| 205 | + public static function splitHostAndPort( $both ) { |
| 206 | + if ( substr( $both, 0, 1 ) === '[' ) { |
| 207 | + if ( preg_match( '/^\[(' . RE_IPV6_ADD . ')\](?::(?P<port>\d+))?$/', $both, $m ) ) { |
| 208 | + if ( isset( $m['port'] ) ) { |
| 209 | + return array( $m[1], intval( $m['port'] ) ); |
| 210 | + } else { |
| 211 | + return array( $m[1], false ); |
| 212 | + } |
| 213 | + } else { |
| 214 | + // Square bracket found but no IPv6 |
| 215 | + return false; |
| 216 | + } |
| 217 | + } |
| 218 | + $numColons = substr_count( $both, ':' ); |
| 219 | + if ( $numColons >= 2 ) { |
| 220 | + // Is it a bare IPv6 address? |
| 221 | + if ( preg_match( '/^' . RE_IPV6_ADD . '$/', $both ) ) { |
| 222 | + return array( $both, false ); |
| 223 | + } else { |
| 224 | + // Not valid IPv6, but too many colons for anything else |
| 225 | + return false; |
| 226 | + } |
| 227 | + } |
| 228 | + if ( $numColons >= 1 ) { |
| 229 | + // Host:port? |
| 230 | + $bits = explode( ':', $both ); |
| 231 | + if ( preg_match( '/^\d+/', $bits[1] ) ) { |
| 232 | + return array( $bits[0], intval( $bits[1] ) ); |
| 233 | + } else { |
| 234 | + // Not a valid port |
| 235 | + return false; |
| 236 | + } |
| 237 | + } |
| 238 | + // Plain hostname |
| 239 | + return array( $both, false ); |
| 240 | + } |
| 241 | + |
| 242 | + /** |
| 243 | + * Given a host name and a port, combine them into host/port string like |
| 244 | + * you might find in a URL. If the host contains a colon, wrap it in square |
| 245 | + * brackets like in RFC 2732. If the port matches the default port, omit |
| 246 | + * the port specification |
| 247 | + */ |
| 248 | + public static function combineHostAndPort( $host, $port, $defaultPort = false ) { |
| 249 | + if ( strpos( $host, ':' ) !== false ) { |
| 250 | + $host = "[$host]"; |
| 251 | + } |
| 252 | + if ( $defaultPort !== false && $port == $defaultPort ) { |
| 253 | + return $host; |
| 254 | + } else { |
| 255 | + return "$host:$port"; |
| 256 | + } |
| 257 | + } |
| 258 | + |
| 259 | + /** |
190 | 260 | * Given an unsigned integer, returns an IPv6 address in octet notation |
191 | 261 | * |
192 | 262 | * @param $ip_int String: IP address. |