From c1f9b1f7b1b77776192048005dcc66dcf3df2bfb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 27 Dec 2014 15:41:37 +0100 Subject: Update to MediaWiki 1.24.1 --- includes/WebRequest.php | 479 ++++++++++++++++++++++-------------------------- 1 file changed, 219 insertions(+), 260 deletions(-) (limited to 'includes/WebRequest.php') diff --git a/includes/WebRequest.php b/includes/WebRequest.php index b17cb9ec..b187c4ac 100644 --- a/includes/WebRequest.php +++ b/includes/WebRequest.php @@ -3,7 +3,7 @@ * Deal with importing all those nasty globals and things * * Copyright © 2003 Brion Vibber - * http://www.mediawiki.org/ + * https://www.mediawiki.org/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,8 +25,8 @@ /** * The WebRequest class encapsulates getting at data passed in the - * URL or via a POSTed form, handling remove of "magic quotes" slashes, - * stripping illegal input characters and normalizing Unicode sequences. + * URL or via a POSTed form stripping illegal input characters and + * normalizing Unicode sequences. * * Usually this is used via a global singleton, $wgRequest. You should * not create a second WebRequest object; make a FauxRequest object if @@ -46,15 +46,20 @@ class WebRequest { /** * Cached client IP address - * @var String + * @var string */ private $ip; + /** + * Cached URL protocol + * @var string + */ + protected $protocol; + public function __construct() { - /// @todo FIXME: This preemptive de-quoting can interfere with other web libraries - /// and increases our memory footprint. It would be cleaner to do on - /// demand; but currently we have no wrapper for $_SERVER etc. - $this->checkMagicQuotes(); + if ( function_exists( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ) { + throw new MWException( "MediaWiki does not function when magic quotes are enabled." ); + } // POST overrides GET data // We don't use $_REQUEST here to avoid interference from cookies... @@ -74,7 +79,7 @@ class WebRequest { * will return an empty array if it determines that the URL is * inside a rewrite path. * - * @return Array: Any query arguments found in path matches. + * @return array Any query arguments found in path matches. */ public static function getPathInfo( $want = 'all' ) { global $wgUsePathInfo; @@ -107,8 +112,8 @@ class WebRequest { $router->add( "$wgScript/$1" ); if ( isset( $_SERVER['SCRIPT_NAME'] ) - && preg_match( '/\.php5?/', $_SERVER['SCRIPT_NAME'] ) ) - { + && preg_match( '/\.php5?/', $_SERVER['SCRIPT_NAME'] ) + ) { # Check for SCRIPT_NAME, we handle index.php explicitly # But we do have some other .php files such as img_auth.php # Don't let root article paths clober the parsing for them @@ -160,7 +165,8 @@ class WebRequest { * @return string */ public static function detectServer() { - list( $proto, $stdPort ) = self::detectProtocolAndStdPort(); + $proto = self::detectProtocol(); + $stdPort = $proto === 'https' ? 443 : 80; $varNames = array( 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ); $host = 'localhost'; @@ -175,7 +181,12 @@ class WebRequest { continue; } $host = $parts[0]; - if ( $parts[1] === false ) { + if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) { + // Bug 70021: Assume that upstream proxy is running on the default + // port based on the protocol. We have no reliable way to determine + // the actual port in use upstream. + $port = $stdPort; + } elseif ( $parts[1] === false ) { if ( isset( $_SERVER['SERVER_PORT'] ) ) { $port = $_SERVER['SERVER_PORT']; } // else leave it as $stdPort @@ -189,25 +200,31 @@ class WebRequest { } /** + * Detect the protocol from $_SERVER. + * This is for use prior to Setup.php, when no WebRequest object is available. + * At other times, use the non-static function getProtocol(). + * * @return array */ - public static function detectProtocolAndStdPort() { + public static function detectProtocol() { if ( ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) || ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) ) { - $arr = array( 'https', 443 ); + return 'https'; } else { - $arr = array( 'http', 80 ); + return 'http'; } - return $arr; } /** + * Get the current URL protocol (http or https) * @return string */ - public static function detectProtocol() { - list( $proto, ) = self::detectProtocolAndStdPort(); - return $proto; + public function getProtocol() { + if ( $this->protocol === null ) { + $this->protocol = self::detectProtocol(); + } + return $this->protocol; } /** @@ -233,11 +250,11 @@ class WebRequest { * URL rewriting function; tries to extract page title and, * optionally, one other fixed parameter value from a URL path. * - * @param string $path the URL path given from the client - * @param array $bases one or more URLs, optionally with $1 at the end - * @param string $key if provided, the matching key in $bases will be - * passed on as the value of this URL parameter - * @return array of URL variables to interpolate; empty if no match + * @param string $path The URL path given from the client + * @param array $bases One or more URLs, optionally with $1 at the end + * @param string $key If provided, the matching key in $bases will be + * passed on as the value of this URL parameter + * @return array Array of URL variables to interpolate; empty if no match */ static function extractTitle( $path, $bases, $key = false ) { foreach ( (array)$bases as $keyValue => $base ) { @@ -258,56 +275,11 @@ class WebRequest { return array(); } - /** - * Recursively strips slashes from the given array; - * used for undoing the evil that is magic_quotes_gpc. - * - * @param array $arr will be modified - * @param bool $topLevel Specifies if the array passed is from the top - * level of the source. In PHP5 magic_quotes only escapes the first level - * of keys that belong to an array. - * @return array the original array - * @see http://www.php.net/manual/en/function.get-magic-quotes-gpc.php#49612 - */ - private function &fix_magic_quotes( &$arr, $topLevel = true ) { - $clean = array(); - foreach ( $arr as $key => $val ) { - if ( is_array( $val ) ) { - $cleanKey = $topLevel ? stripslashes( $key ) : $key; - $clean[$cleanKey] = $this->fix_magic_quotes( $arr[$key], false ); - } else { - $cleanKey = stripslashes( $key ); - $clean[$cleanKey] = stripslashes( $val ); - } - } - $arr = $clean; - return $arr; - } - - /** - * If magic_quotes_gpc option is on, run the global arrays - * through fix_magic_quotes to strip out the stupid slashes. - * WARNING: This should only be done once! Running a second - * time could damage the values. - */ - private function checkMagicQuotes() { - $mustFixQuotes = function_exists( 'get_magic_quotes_gpc' ) - && get_magic_quotes_gpc(); - if ( $mustFixQuotes ) { - $this->fix_magic_quotes( $_COOKIE ); - $this->fix_magic_quotes( $_ENV ); - $this->fix_magic_quotes( $_GET ); - $this->fix_magic_quotes( $_POST ); - $this->fix_magic_quotes( $_REQUEST ); - $this->fix_magic_quotes( $_SERVER ); - } - } - /** * Recursively normalizes UTF-8 strings in the given array. * - * @param $data string|array - * @return array|string cleaned-up version of the given + * @param string|array $data + * @return array|string Cleaned-up version of the given * @private */ function normalizeUnicode( $data ) { @@ -325,9 +297,9 @@ class WebRequest { /** * Fetch a value from the given array or return $default if it's not set. * - * @param $arr Array - * @param $name String - * @param $default Mixed + * @param array $arr + * @param string $name + * @param mixed $default * @return mixed */ private function getGPCVal( $arr, $name, $default ) { @@ -357,9 +329,9 @@ class WebRequest { * non-freeform text inputs (e.g. predefined internal text keys * selected by a drop-down menu). For freeform input, see getText(). * - * @param $name String - * @param string $default optional default (or NULL) - * @return String + * @param string $name + * @param string $default Optional default (or null) + * @return string */ public function getVal( $name, $default = null ) { $val = $this->getGPCVal( $this->data, $name, $default ); @@ -376,9 +348,9 @@ class WebRequest { /** * Set an arbitrary value into our get/post data. * - * @param string $key key name to use - * @param $value Mixed: value to set - * @return Mixed: old value if one was present, null otherwise + * @param string $key Key name to use + * @param mixed $value Value to set + * @return mixed Old value if one was present, null otherwise */ public function setVal( $key, $value ) { $ret = isset( $this->data[$key] ) ? $this->data[$key] : null; @@ -389,8 +361,8 @@ class WebRequest { /** * Unset an arbitrary value from our get/post data. * - * @param string $key key name to use - * @return Mixed: old value if one was present, null otherwise + * @param string $key Key name to use + * @return mixed Old value if one was present, null otherwise */ public function unsetVal( $key ) { if ( !isset( $this->data[$key] ) ) { @@ -405,11 +377,11 @@ class WebRequest { /** * Fetch an array from the input or return $default if it's not set. * If source was scalar, will return an array with a single element. - * If no source and no default, returns NULL. + * If no source and no default, returns null. * - * @param $name String - * @param array $default optional default (or NULL) - * @return Array + * @param string $name + * @param array $default Optional default (or null) + * @return array */ public function getArray( $name, $default = null ) { $val = $this->getGPCVal( $this->data, $name, $default ); @@ -423,12 +395,12 @@ class WebRequest { /** * Fetch an array of integers, or return $default if it's not set. * If source was scalar, will return an array with a single element. - * If no source and no default, returns NULL. + * If no source and no default, returns null. * If an array is returned, contents are guaranteed to be integers. * - * @param $name String - * @param array $default option default (or NULL) - * @return Array of ints + * @param string $name + * @param array $default Option default (or null) + * @return array Array of ints */ public function getIntArray( $name, $default = null ) { $val = $this->getArray( $name, $default ); @@ -443,9 +415,9 @@ class WebRequest { * Guaranteed to return an integer; non-numeric input will typically * return 0. * - * @param $name String - * @param $default Integer - * @return Integer + * @param string $name + * @param int $default + * @return int */ public function getInt( $name, $default = 0 ) { return intval( $this->getVal( $name, $default ) ); @@ -456,8 +428,8 @@ class WebRequest { * Guaranteed to return an integer or null; non-numeric input will * typically return null. * - * @param $name String - * @return Integer + * @param string $name + * @return int|null */ public function getIntOrNull( $name ) { $val = $this->getVal( $name ); @@ -466,14 +438,28 @@ class WebRequest { : null; } + /** + * Fetch a floating point value from the input or return $default if not set. + * Guaranteed to return a float; non-numeric input will typically + * return 0. + * + * @since 1.23 + * @param string $name + * @param float $default + * @return float + */ + public function getFloat( $name, $default = 0.0 ) { + return floatval( $this->getVal( $name, $default ) ); + } + /** * Fetch a boolean value from the input or return $default if not set. * Guaranteed to return true or false, with normal PHP semantics for * boolean interpretation of strings. * - * @param $name String - * @param $default Boolean - * @return Boolean + * @param string $name + * @param bool $default + * @return bool */ public function getBool( $name, $default = false ) { return (bool)$this->getVal( $name, $default ); @@ -484,9 +470,9 @@ class WebRequest { * Unlike getBool, the string "false" will result in boolean false, which is * useful when interpreting information sent from JavaScript. * - * @param $name String - * @param $default Boolean - * @return Boolean + * @param string $name + * @param bool $default + * @return bool */ public function getFuzzyBool( $name, $default = false ) { return $this->getBool( $name, $default ) && strcasecmp( $this->getVal( $name ), 'false' ) !== 0; @@ -497,8 +483,8 @@ class WebRequest { * value is (even "0"). Return false if the named value is not set. * Example use is checking for the presence of check boxes in forms. * - * @param $name String - * @return Boolean + * @param string $name + * @return bool */ public function getCheck( $name ) { # Checkboxes and buttons are only present when clicked @@ -514,9 +500,9 @@ class WebRequest { * user-supplied freeform text input (for which input transformations may * be required - e.g. Esperanto x-coding). * - * @param $name String - * @param string $default optional - * @return String + * @param string $name + * @param string $default Optional + * @return string */ public function getText( $name, $default = '' ) { global $wgContLang; @@ -551,7 +537,7 @@ class WebRequest { /** * Returns the names of all input values excluding those in $exclude. * - * @param $exclude Array + * @param array $exclude * @return array */ public function getValueNames( $exclude = array() ) { @@ -562,7 +548,7 @@ class WebRequest { * Get the values passed in the query string. * No transformation is performed on the values. * - * @return Array + * @return array */ public function getQueryValues() { return $_GET; @@ -572,7 +558,7 @@ class WebRequest { * Return the contents of the Query with no decoding. Use when you need to * know exactly what was sent, e.g. for an OAuth signature over the elements. * - * @return String + * @return string */ public function getRawQueryString() { return $_SERVER['QUERY_STRING']; @@ -582,7 +568,7 @@ class WebRequest { * Return the contents of the POST with no decoding. Use when you need to * know exactly what was sent, e.g. for an OAuth signature over the elements. * - * @return String + * @return string */ public function getRawPostString() { if ( !$this->wasPosted() ) { @@ -596,11 +582,11 @@ class WebRequest { * disallow reading the stream more than once. As stated in the php docs, this * does not work with enctype="multipart/form-data". * - * @return String + * @return string */ public function getRawInput() { - static $input = false; - if ( $input === false ) { + static $input = null; + if ( $input === null ) { $input = file_get_contents( 'php://input' ); } return $input; @@ -609,7 +595,7 @@ class WebRequest { /** * Get the HTTP method used for this request. * - * @return String + * @return string */ public function getMethod() { return isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : 'GET'; @@ -622,7 +608,7 @@ class WebRequest { * Note that values retrieved by the object may come from the * GET URL etc even on a POST request. * - * @return Boolean + * @return bool */ public function wasPosted() { return $this->getMethod() == 'POST'; @@ -637,7 +623,7 @@ class WebRequest { * during the current request (in which case the cookie will * be sent back to the client at the end of the script run). * - * @return Boolean + * @return bool */ public function checkSessionCookie() { return isset( $_COOKIE[session_name()] ); @@ -646,10 +632,10 @@ class WebRequest { /** * Get a cookie from the $_COOKIE jar * - * @param string $key the name of the cookie - * @param string $prefix a prefix to use for the cookie name, if not $wgCookiePrefix - * @param $default Mixed: what to return if the value isn't found - * @return Mixed: cookie value or $default if the cookie not set + * @param string $key The name of the cookie + * @param string $prefix A prefix to use for the cookie name, if not $wgCookiePrefix + * @param mixed $default What to return if the value isn't found + * @return mixed Cookie value or $default if the cookie not set */ public function getCookie( $key, $prefix = null, $default = null ) { if ( $prefix === null ) { @@ -664,12 +650,14 @@ class WebRequest { * This will be suitable for use as a relative link in HTML output. * * @throws MWException - * @return String + * @return string */ public function getRequestURL() { if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) { $base = $_SERVER['REQUEST_URI']; - } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) { + } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) + && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] ) + ) { // Probably IIS; doesn't set REQUEST_URI $base = $_SERVER['HTTP_X_ORIGINAL_URL']; } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) { @@ -709,7 +697,7 @@ class WebRequest { * If $wgServer is protocol-relative, this will return a fully * qualified URL with the protocol that was used for this request. * - * @return String + * @return string */ public function getFullRequestURL() { return wfExpandUrl( $this->getRequestURL(), PROTO_CURRENT ); @@ -717,30 +705,19 @@ class WebRequest { /** * Take an arbitrary query and rewrite the present URL to include it - * @param string $query query string fragment; do not include initial '?' + * @param string $query Query string fragment; do not include initial '?' * - * @return String + * @return string */ public function appendQuery( $query ) { return $this->appendQueryArray( wfCgiToArray( $query ) ); } /** - * HTML-safe version of appendQuery(). - * @deprecated: Deprecated in 1.20, warnings in 1.21, remove in 1.22. - * - * @param string $query query string fragment; do not include initial '?' - * @return String - */ - public function escapeAppendQuery( $query ) { - return htmlspecialchars( $this->appendQuery( $query ) ); - } - - /** - * @param $key - * @param $value - * @param $onlyquery bool - * @return String + * @param string $key + * @param string $value + * @param bool $onlyquery + * @return string */ public function appendQueryValue( $key, $value, $onlyquery = false ) { return $this->appendQueryArray( array( $key => $value ), $onlyquery ); @@ -749,10 +726,9 @@ class WebRequest { /** * Appends or replaces value of query variables. * - * @param array $array of values to replace/add to query - * @param bool $onlyquery whether to only return the query string and not - * the complete URL - * @return String + * @param array $array Array of values to replace/add to query + * @param bool $onlyquery Whether to only return the query string and not the complete URL + * @return string */ public function appendQueryArray( $array, $onlyquery = false ) { global $wgTitle; @@ -768,9 +744,9 @@ class WebRequest { * defaults if not given. The limit must be positive and is capped at 5000. * Offset must be positive but is not capped. * - * @param $deflimit Integer: limit to use if no input and the user hasn't set the option. - * @param string $optionname to specify an option other than rclimit to pull from. - * @return array first element is limit, second is offset + * @param int $deflimit Limit to use if no input and the user hasn't set the option. + * @param string $optionname To specify an option other than rclimit to pull from. + * @return array First element is limit, second is offset */ public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) { global $wgUser; @@ -800,32 +776,19 @@ class WebRequest { /** * Return the path to the temporary file where PHP has stored the upload. * - * @param $key String: - * @return string or NULL if no such file. + * @param string $key + * @return string|null String or null if no such file. */ public function getFileTempname( $key ) { $file = new WebRequestUpload( $this, $key ); return $file->getTempName(); } - /** - * Return the size of the upload, or 0. - * - * @deprecated since 1.17 - * @param $key String: - * @return integer - */ - public function getFileSize( $key ) { - wfDeprecated( __METHOD__, '1.17' ); - $file = new WebRequestUpload( $this, $key ); - return $file->getSize(); - } - /** * Return the upload error or 0 * - * @param $key String: - * @return integer + * @param string $key + * @return int */ public function getUploadError( $key ) { $file = new WebRequestUpload( $this, $key ); @@ -840,8 +803,8 @@ class WebRequest { * * Other than this the name is not verified for being a safe filename. * - * @param $key String: - * @return string or NULL if no such file. + * @param string $key + * @return string|null String or null if no such file. */ public function getFileName( $key ) { $file = new WebRequestUpload( $this, $key ); @@ -851,7 +814,7 @@ class WebRequest { /** * Return a WebRequestUpload object corresponding to the key * - * @param $key string + * @param string $key * @return WebRequestUpload */ public function getUpload( $key ) { @@ -901,7 +864,7 @@ class WebRequest { /** * Get an array containing all request headers * - * @return Array mapping header name to its value + * @return array Mapping header name to its value */ public function getAllHeaders() { $this->initHeaders(); @@ -910,7 +873,7 @@ class WebRequest { /** * Get a request header, or false if it isn't set - * @param string $name case-insensitive header name + * @param string $name Case-insensitive header name * * @return string|bool False on failure */ @@ -927,8 +890,8 @@ class WebRequest { /** * Get data from $_SESSION * - * @param string $key name of key in $_SESSION - * @return Mixed + * @param string $key Name of key in $_SESSION + * @return mixed */ public function getSessionData( $key ) { if ( !isset( $_SESSION[$key] ) ) { @@ -940,8 +903,8 @@ class WebRequest { /** * Set session data * - * @param string $key name of key in $_SESSION - * @param $data Mixed + * @param string $key Name of key in $_SESSION + * @param mixed $data */ public function setSessionData( $key, $data ) { $_SESSION[$key] = $data; @@ -953,7 +916,7 @@ class WebRequest { * message or redirect to a safer URL. Returns true if the URL is OK, and * false if an error message has been shown and the request should be aborted. * - * @param $extWhitelist array + * @param array $extWhitelist * @throws HttpError * @return bool */ @@ -979,7 +942,7 @@ class WebRequest { * Attempt to redirect to a URL with a QUERY_STRING that's not dangerous in * IE 6. Returns true if it was successful, false otherwise. * - * @param $url string + * @param string $url * @return bool */ protected function doSecurityRedirect( $url ) { @@ -997,9 +960,9 @@ class WebRequest { We can't serve non-HTML content from the URL you have requested, because Internet Explorer would interpret it as an incorrect and potentially dangerous content type.

-

Instead, please use this URL, which is the same as the URL you have requested, except that -"&*" is appended. This prevents Internet Explorer from seeing a bogus file -extension. +

Instead, please use this URL, which is the same as the +URL you have requested, except that "&*" is appended. This prevents Internet +Explorer from seeing a bogus file extension.

@@ -1008,45 +971,18 @@ HTML; return true; } - /** - * Returns true if the PATH_INFO ends with an extension other than a script - * extension. This could confuse IE for scripts that send arbitrary data which - * is not HTML but may be detected as such. - * - * Various past attempts to use the URL to make this check have generally - * run up against the fact that CGI does not provide a standard method to - * determine the URL. PATH_INFO may be mangled (e.g. if cgi.fix_pathinfo=0), - * but only by prefixing it with the script name and maybe some other stuff, - * the extension is not mangled. So this should be a reasonably portable - * way to perform this security check. - * - * Also checks for anything that looks like a file extension at the end of - * QUERY_STRING, since IE 6 and earlier will use this to get the file type - * if there was no dot before the question mark (bug 28235). - * - * @deprecated Use checkUrlExtension(). - * - * @param $extWhitelist array - * - * @return bool - */ - public function isPathInfoBad( $extWhitelist = array() ) { - wfDeprecated( __METHOD__, '1.17' ); - global $wgScriptExtension; - $extWhitelist[] = ltrim( $wgScriptExtension, '.' ); - return IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ); - } - /** * Parse the Accept-Language header sent by the client into an array - * @return array array( languageCode => q-value ) sorted by q-value in descending order then - * appearing time in the header in ascending order. + * + * @return array Array( languageCode => q-value ) sorted by q-value in + * descending order then appearing time in the header in ascending order. * May contain the "language" '*', which applies to languages other than those explicitly listed. * This is aligned with rfc2616 section 14.4 * Preference for earlier languages appears in rfc3282 as an extension to HTTP/1.1. */ public function getAcceptLang() { - // Modified version of code found at http://www.thefutureoftheweb.com/blog/use-accept-language-header + // Modified version of code found at + // http://www.thefutureoftheweb.com/blog/use-accept-language-header $acceptLang = $this->getHeader( 'Accept-Language' ); if ( !$acceptLang ) { return array(); @@ -1057,8 +993,11 @@ HTML; // Break up string into pieces (languages and q factors) $lang_parse = null; - preg_match_all( '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/', - $acceptLang, $lang_parse ); + preg_match_all( + '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/', + $acceptLang, + $lang_parse + ); if ( !count( $lang_parse[1] ) ) { return array(); @@ -1092,7 +1031,7 @@ HTML; * @since 1.19 * * @throws MWException - * @return String + * @return string */ protected function getRawIP() { if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) { @@ -1100,7 +1039,8 @@ HTML; } if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) { - throw new MWException( __METHOD__ . " : Could not determine the remote IP address due to multiple values." ); + throw new MWException( __METHOD__ + . " : Could not determine the remote IP address due to multiple values." ); } else { $ipchain = $_SERVER['REMOTE_ADDR']; } @@ -1127,36 +1067,45 @@ HTML; # collect the originating ips $ip = $this->getRawIP(); + if ( !$ip ) { + throw new MWException( 'Unable to determine IP.' ); + } # Append XFF $forwardedFor = $this->getHeader( 'X-Forwarded-For' ); if ( $forwardedFor !== false ) { + $isConfigured = IP::isConfiguredProxy( $ip ); $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) ); $ipchain = array_reverse( $ipchain ); - if ( $ip ) { - array_unshift( $ipchain, $ip ); - } + array_unshift( $ipchain, $ip ); # Step through XFF list and find the last address in the list which is a # trusted server. Set $ip to the IP address given by that trusted server, # unless the address is not sensible (e.g. private). However, prefer private # IP addresses over proxy servers controlled by this site (more sensible). + # Note that some XFF values might be "unknown" with Squid/Varnish. foreach ( $ipchain as $i => $curIP ) { $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) ); - if ( wfIsTrustedProxy( $curIP ) && isset( $ipchain[$i + 1] ) ) { - if ( wfIsConfiguredProxy( $curIP ) || // bug 48919; treat IP as sane - IP::isPublic( $ipchain[$i + 1] ) || - $wgUsePrivateIPs - ) { - $nextIP = IP::canonicalize( $ipchain[$i + 1] ); - if ( !$nextIP && wfIsConfiguredProxy( $ip ) ) { - // We have not yet made it past CDN/proxy servers of this site, - // so either they are misconfigured or there is some IP spoofing. - throw new MWException( "Invalid IP given in XFF '$forwardedFor'." ); - } - $ip = $nextIP; - continue; + if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown' + || !IP::isTrustedProxy( $curIP ) + ) { + break; // IP is not valid/trusted or does not point to anything + } + if ( + IP::isPublic( $ipchain[$i + 1] ) || + $wgUsePrivateIPs || + IP::isConfiguredProxy( $curIP ) // bug 48919; treat IP as sane + ) { + // Follow the next IP according to the proxy + $nextIP = IP::canonicalize( $ipchain[$i + 1] ); + if ( !$nextIP && $isConfigured ) { + // We have not yet made it past CDN/proxy servers of this site, + // so either they are misconfigured or there is some IP spoofing. + throw new MWException( "Invalid IP given in XFF '$forwardedFor'." ); } + $ip = $nextIP; + // keep traversing the chain + continue; } break; } @@ -1195,7 +1144,7 @@ class WebRequestUpload { /** * Constructor. Should only be called by WebRequest * - * @param $request WebRequest The associated request + * @param WebRequest $request The associated request * @param string $key Key in $_FILES array (name of form field) */ public function __construct( $request, $key ) { @@ -1218,7 +1167,7 @@ class WebRequestUpload { /** * Return the original filename of the uploaded file * - * @return mixed Filename or null if non-existent + * @return string|null Filename or null if non-existent */ public function getName() { if ( !$this->exists() ) { @@ -1252,7 +1201,7 @@ class WebRequestUpload { /** * Return the path to the temporary file * - * @return mixed Path or null if non-existent + * @return string|null Path or null if non-existent */ public function getTempName() { if ( !$this->exists() ) { @@ -1308,13 +1257,16 @@ class FauxRequest extends WebRequest { private $session = array(); /** - * @param array $data of *non*-urlencoded key => value pairs, the + * @param array $data Array of *non*-urlencoded key => value pairs, the * fake GET/POST values - * @param bool $wasPosted whether to treat the data as POST - * @param $session Mixed: session array or null + * @param bool $wasPosted Whether to treat the data as POST + * @param array|null $session Session array or null + * @param string $protocol 'http' or 'https' * @throws MWException */ - public function __construct( $data = array(), $wasPosted = false, $session = null ) { + public function __construct( $data = array(), $wasPosted = false, + $session = null, $protocol = 'http' + ) { if ( is_array( $data ) ) { $this->data = $data; } else { @@ -1324,10 +1276,11 @@ class FauxRequest extends WebRequest { if ( $session ) { $this->session = $session; } + $this->protocol = $protocol; } /** - * @param $method string + * @param string $method * @throws MWException */ private function notImplemented( $method ) { @@ -1335,8 +1288,8 @@ class FauxRequest extends WebRequest { } /** - * @param $name string - * @param $default string + * @param string $name + * @param string $default * @return string */ public function getText( $name, $default = '' ) { @@ -1345,7 +1298,7 @@ class FauxRequest extends WebRequest { } /** - * @return Array + * @return array */ public function getValues() { return $this->data; @@ -1385,6 +1338,10 @@ class FauxRequest extends WebRequest { $this->notImplemented( __METHOD__ ); } + public function getProtocol() { + return $this->protocol; + } + /** * @param string $name The name of the header to get (case insensitive). * @return bool|string @@ -1395,8 +1352,8 @@ class FauxRequest extends WebRequest { } /** - * @param $name string - * @param $val string + * @param string $name + * @param string $val */ public function setHeader( $name, $val ) { $name = strtoupper( $name ); @@ -1404,8 +1361,8 @@ class FauxRequest extends WebRequest { } /** - * @param $key - * @return mixed + * @param string $key + * @return array|null */ public function getSessionData( $key ) { if ( isset( $this->session[$key] ) ) { @@ -1415,31 +1372,23 @@ class FauxRequest extends WebRequest { } /** - * @param $key - * @param $data + * @param string $key + * @param array $data */ public function setSessionData( $key, $data ) { $this->session[$key] = $data; } /** - * @return array|Mixed|null + * @return array|mixed|null */ public function getSessionArray() { return $this->session; } - /** - * @param array $extWhitelist - * @return bool - */ - public function isPathInfoBad( $extWhitelist = array() ) { - return false; - } - /** * FauxRequests shouldn't depend on raw request data (but that could be implemented here) - * @return String + * @return string */ public function getRawQueryString() { return ''; @@ -1447,7 +1396,7 @@ class FauxRequest extends WebRequest { /** * FauxRequests shouldn't depend on raw request data (but that could be implemented here) - * @return String + * @return string */ public function getRawPostString() { return ''; @@ -1455,7 +1404,7 @@ class FauxRequest extends WebRequest { /** * FauxRequests shouldn't depend on raw request data (but that could be implemented here) - * @return String + * @return string */ public function getRawInput() { return ''; @@ -1488,6 +1437,12 @@ class FauxRequest extends WebRequest { class DerivativeRequest extends FauxRequest { private $base; + /** + * @param WebRequest $base + * @param array $data Array of *non*-urlencoded key => value pairs, the + * fake GET/POST values + * @param bool $wasPosted Whether to treat the data as POST + */ public function __construct( WebRequest $base, $data, $wasPosted = false ) { $this->base = $base; parent::__construct( $data, $wasPosted ); @@ -1524,4 +1479,8 @@ class DerivativeRequest extends FauxRequest { public function getIP() { return $this->base->getIP(); } + + public function getProtocol() { + return $this->base->getProtocol(); + } } -- cgit v1.2.3-54-g00ecf