MediaWiki
REL1_23
|
00001 <?php 00038 class WebRequest { 00039 protected $data, $headers = array(); 00040 00045 private $response; 00046 00051 private $ip; 00052 00057 protected $protocol; 00058 00059 public function __construct() { 00063 $this->checkMagicQuotes(); 00064 00065 // POST overrides GET data 00066 // We don't use $_REQUEST here to avoid interference from cookies... 00067 $this->data = $_POST + $_GET; 00068 } 00069 00085 public static function getPathInfo( $want = 'all' ) { 00086 global $wgUsePathInfo; 00087 // PATH_INFO is mangled due to http://bugs.php.net/bug.php?id=31892 00088 // And also by Apache 2.x, double slashes are converted to single slashes. 00089 // So we will use REQUEST_URI if possible. 00090 $matches = array(); 00091 if ( !empty( $_SERVER['REQUEST_URI'] ) ) { 00092 // Slurp out the path portion to examine... 00093 $url = $_SERVER['REQUEST_URI']; 00094 if ( !preg_match( '!^https?://!', $url ) ) { 00095 $url = 'http://unused' . $url; 00096 } 00097 wfSuppressWarnings(); 00098 $a = parse_url( $url ); 00099 wfRestoreWarnings(); 00100 if ( $a ) { 00101 $path = isset( $a['path'] ) ? $a['path'] : ''; 00102 00103 global $wgScript; 00104 if ( $path == $wgScript && $want !== 'all' ) { 00105 // Script inside a rewrite path? 00106 // Abort to keep from breaking... 00107 return $matches; 00108 } 00109 00110 $router = new PathRouter; 00111 00112 // Raw PATH_INFO style 00113 $router->add( "$wgScript/$1" ); 00114 00115 if ( isset( $_SERVER['SCRIPT_NAME'] ) 00116 && preg_match( '/\.php5?/', $_SERVER['SCRIPT_NAME'] ) 00117 ) { 00118 # Check for SCRIPT_NAME, we handle index.php explicitly 00119 # But we do have some other .php files such as img_auth.php 00120 # Don't let root article paths clober the parsing for them 00121 $router->add( $_SERVER['SCRIPT_NAME'] . "/$1" ); 00122 } 00123 00124 global $wgArticlePath; 00125 if ( $wgArticlePath ) { 00126 $router->add( $wgArticlePath ); 00127 } 00128 00129 global $wgActionPaths; 00130 if ( $wgActionPaths ) { 00131 $router->add( $wgActionPaths, array( 'action' => '$key' ) ); 00132 } 00133 00134 global $wgVariantArticlePath, $wgContLang; 00135 if ( $wgVariantArticlePath ) { 00136 $router->add( $wgVariantArticlePath, 00137 array( 'variant' => '$2' ), 00138 array( '$2' => $wgContLang->getVariants() ) 00139 ); 00140 } 00141 00142 wfRunHooks( 'WebRequestPathInfoRouter', array( $router ) ); 00143 00144 $matches = $router->parse( $path ); 00145 } 00146 } elseif ( $wgUsePathInfo ) { 00147 if ( isset( $_SERVER['ORIG_PATH_INFO'] ) && $_SERVER['ORIG_PATH_INFO'] != '' ) { 00148 // Mangled PATH_INFO 00149 // http://bugs.php.net/bug.php?id=31892 00150 // Also reported when ini_get('cgi.fix_pathinfo')==false 00151 $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 ); 00152 00153 } elseif ( isset( $_SERVER['PATH_INFO'] ) && $_SERVER['PATH_INFO'] != '' ) { 00154 // Regular old PATH_INFO yay 00155 $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 ); 00156 } 00157 } 00158 00159 return $matches; 00160 } 00161 00168 public static function detectServer() { 00169 $proto = self::detectProtocol(); 00170 $stdPort = $proto === 'https' ? 443 : 80; 00171 00172 $varNames = array( 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ); 00173 $host = 'localhost'; 00174 $port = $stdPort; 00175 foreach ( $varNames as $varName ) { 00176 if ( !isset( $_SERVER[$varName] ) ) { 00177 continue; 00178 } 00179 $parts = IP::splitHostAndPort( $_SERVER[$varName] ); 00180 if ( !$parts ) { 00181 // Invalid, do not use 00182 continue; 00183 } 00184 $host = $parts[0]; 00185 if ( $parts[1] === false ) { 00186 if ( isset( $_SERVER['SERVER_PORT'] ) ) { 00187 $port = $_SERVER['SERVER_PORT']; 00188 } // else leave it as $stdPort 00189 } else { 00190 $port = $parts[1]; 00191 } 00192 break; 00193 } 00194 00195 return $proto . '://' . IP::combineHostAndPort( $host, $port, $stdPort ); 00196 } 00197 00205 public static function detectProtocol() { 00206 if ( ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) || 00207 ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 00208 $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) ) { 00209 return 'https'; 00210 } else { 00211 return 'http'; 00212 } 00213 } 00214 00219 public function getProtocol() { 00220 if ( $this->protocol === null ) { 00221 $this->protocol = self::detectProtocol(); 00222 } 00223 return $this->protocol; 00224 } 00225 00233 public function interpolateTitle() { 00234 // bug 16019: title interpolation on API queries is useless and sometimes harmful 00235 if ( defined( 'MW_API' ) ) { 00236 return; 00237 } 00238 00239 $matches = self::getPathInfo( 'title' ); 00240 foreach ( $matches as $key => $val ) { 00241 $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val; 00242 } 00243 } 00244 00255 static function extractTitle( $path, $bases, $key = false ) { 00256 foreach ( (array)$bases as $keyValue => $base ) { 00257 // Find the part after $wgArticlePath 00258 $base = str_replace( '$1', '', $base ); 00259 $baseLen = strlen( $base ); 00260 if ( substr( $path, 0, $baseLen ) == $base ) { 00261 $raw = substr( $path, $baseLen ); 00262 if ( $raw !== '' ) { 00263 $matches = array( 'title' => rawurldecode( $raw ) ); 00264 if ( $key ) { 00265 $matches[$key] = $keyValue; 00266 } 00267 return $matches; 00268 } 00269 } 00270 } 00271 return array(); 00272 } 00273 00285 private function &fix_magic_quotes( &$arr, $topLevel = true ) { 00286 $clean = array(); 00287 foreach ( $arr as $key => $val ) { 00288 if ( is_array( $val ) ) { 00289 $cleanKey = $topLevel ? stripslashes( $key ) : $key; 00290 $clean[$cleanKey] = $this->fix_magic_quotes( $arr[$key], false ); 00291 } else { 00292 $cleanKey = stripslashes( $key ); 00293 $clean[$cleanKey] = stripslashes( $val ); 00294 } 00295 } 00296 $arr = $clean; 00297 return $arr; 00298 } 00299 00306 private function checkMagicQuotes() { 00307 $mustFixQuotes = function_exists( 'get_magic_quotes_gpc' ) 00308 && get_magic_quotes_gpc(); 00309 if ( $mustFixQuotes ) { 00310 $this->fix_magic_quotes( $_COOKIE ); 00311 $this->fix_magic_quotes( $_ENV ); 00312 $this->fix_magic_quotes( $_GET ); 00313 $this->fix_magic_quotes( $_POST ); 00314 $this->fix_magic_quotes( $_REQUEST ); 00315 $this->fix_magic_quotes( $_SERVER ); 00316 } 00317 } 00318 00326 function normalizeUnicode( $data ) { 00327 if ( is_array( $data ) ) { 00328 foreach ( $data as $key => $val ) { 00329 $data[$key] = $this->normalizeUnicode( $val ); 00330 } 00331 } else { 00332 global $wgContLang; 00333 $data = isset( $wgContLang ) ? $wgContLang->normalize( $data ) : UtfNormal::cleanUp( $data ); 00334 } 00335 return $data; 00336 } 00337 00346 private function getGPCVal( $arr, $name, $default ) { 00347 # PHP is so nice to not touch input data, except sometimes: 00348 # http://us2.php.net/variables.external#language.variables.external.dot-in-names 00349 # Work around PHP *feature* to avoid *bugs* elsewhere. 00350 $name = strtr( $name, '.', '_' ); 00351 if ( isset( $arr[$name] ) ) { 00352 global $wgContLang; 00353 $data = $arr[$name]; 00354 if ( isset( $_GET[$name] ) && !is_array( $data ) ) { 00355 # Check for alternate/legacy character encoding. 00356 if ( isset( $wgContLang ) ) { 00357 $data = $wgContLang->checkTitleEncoding( $data ); 00358 } 00359 } 00360 $data = $this->normalizeUnicode( $data ); 00361 return $data; 00362 } else { 00363 return $default; 00364 } 00365 } 00366 00377 public function getVal( $name, $default = null ) { 00378 $val = $this->getGPCVal( $this->data, $name, $default ); 00379 if ( is_array( $val ) ) { 00380 $val = $default; 00381 } 00382 if ( is_null( $val ) ) { 00383 return $val; 00384 } else { 00385 return (string)$val; 00386 } 00387 } 00388 00396 public function setVal( $key, $value ) { 00397 $ret = isset( $this->data[$key] ) ? $this->data[$key] : null; 00398 $this->data[$key] = $value; 00399 return $ret; 00400 } 00401 00408 public function unsetVal( $key ) { 00409 if ( !isset( $this->data[$key] ) ) { 00410 $ret = null; 00411 } else { 00412 $ret = $this->data[$key]; 00413 unset( $this->data[$key] ); 00414 } 00415 return $ret; 00416 } 00417 00427 public function getArray( $name, $default = null ) { 00428 $val = $this->getGPCVal( $this->data, $name, $default ); 00429 if ( is_null( $val ) ) { 00430 return null; 00431 } else { 00432 return (array)$val; 00433 } 00434 } 00435 00446 public function getIntArray( $name, $default = null ) { 00447 $val = $this->getArray( $name, $default ); 00448 if ( is_array( $val ) ) { 00449 $val = array_map( 'intval', $val ); 00450 } 00451 return $val; 00452 } 00453 00463 public function getInt( $name, $default = 0 ) { 00464 return intval( $this->getVal( $name, $default ) ); 00465 } 00466 00475 public function getIntOrNull( $name ) { 00476 $val = $this->getVal( $name ); 00477 return is_numeric( $val ) 00478 ? intval( $val ) 00479 : null; 00480 } 00481 00492 public function getFloat( $name, $default = 0 ) { 00493 return floatval( $this->getVal( $name, $default ) ); 00494 } 00495 00505 public function getBool( $name, $default = false ) { 00506 return (bool)$this->getVal( $name, $default ); 00507 } 00508 00518 public function getFuzzyBool( $name, $default = false ) { 00519 return $this->getBool( $name, $default ) && strcasecmp( $this->getVal( $name ), 'false' ) !== 0; 00520 } 00521 00530 public function getCheck( $name ) { 00531 # Checkboxes and buttons are only present when clicked 00532 # Presence connotes truth, absence false 00533 return $this->getVal( $name, null ) !== null; 00534 } 00535 00548 public function getText( $name, $default = '' ) { 00549 global $wgContLang; 00550 $val = $this->getVal( $name, $default ); 00551 return str_replace( "\r\n", "\n", 00552 $wgContLang->recodeInput( $val ) ); 00553 } 00554 00562 public function getValues() { 00563 $names = func_get_args(); 00564 if ( count( $names ) == 0 ) { 00565 $names = array_keys( $this->data ); 00566 } 00567 00568 $retVal = array(); 00569 foreach ( $names as $name ) { 00570 $value = $this->getGPCVal( $this->data, $name, null ); 00571 if ( !is_null( $value ) ) { 00572 $retVal[$name] = $value; 00573 } 00574 } 00575 return $retVal; 00576 } 00577 00584 public function getValueNames( $exclude = array() ) { 00585 return array_diff( array_keys( $this->getValues() ), $exclude ); 00586 } 00587 00594 public function getQueryValues() { 00595 return $_GET; 00596 } 00597 00604 public function getRawQueryString() { 00605 return $_SERVER['QUERY_STRING']; 00606 } 00607 00614 public function getRawPostString() { 00615 if ( !$this->wasPosted() ) { 00616 return ''; 00617 } 00618 return $this->getRawInput(); 00619 } 00620 00628 public function getRawInput() { 00629 static $input = false; 00630 if ( $input === false ) { 00631 $input = file_get_contents( 'php://input' ); 00632 } 00633 return $input; 00634 } 00635 00641 public function getMethod() { 00642 return isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : 'GET'; 00643 } 00644 00654 public function wasPosted() { 00655 return $this->getMethod() == 'POST'; 00656 } 00657 00669 public function checkSessionCookie() { 00670 return isset( $_COOKIE[session_name()] ); 00671 } 00672 00681 public function getCookie( $key, $prefix = null, $default = null ) { 00682 if ( $prefix === null ) { 00683 global $wgCookiePrefix; 00684 $prefix = $wgCookiePrefix; 00685 } 00686 return $this->getGPCVal( $_COOKIE, $prefix . $key, $default ); 00687 } 00688 00696 public function getRequestURL() { 00697 if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) { 00698 $base = $_SERVER['REQUEST_URI']; 00699 } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] ) ) { 00700 // Probably IIS; doesn't set REQUEST_URI 00701 $base = $_SERVER['HTTP_X_ORIGINAL_URL']; 00702 } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) { 00703 $base = $_SERVER['SCRIPT_NAME']; 00704 if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) { 00705 $base .= '?' . $_SERVER['QUERY_STRING']; 00706 } 00707 } else { 00708 // This shouldn't happen! 00709 throw new MWException( "Web server doesn't provide either " . 00710 "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " . 00711 "of your web server configuration to http://bugzilla.wikimedia.org/" ); 00712 } 00713 // User-agents should not send a fragment with the URI, but 00714 // if they do, and the web server passes it on to us, we 00715 // need to strip it or we get false-positive redirect loops 00716 // or weird output URLs 00717 $hash = strpos( $base, '#' ); 00718 if ( $hash !== false ) { 00719 $base = substr( $base, 0, $hash ); 00720 } 00721 00722 if ( $base[0] == '/' ) { 00723 // More than one slash will look like it is protocol relative 00724 return preg_replace( '!^/+!', '/', $base ); 00725 } else { 00726 // We may get paths with a host prepended; strip it. 00727 return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base ); 00728 } 00729 } 00730 00741 public function getFullRequestURL() { 00742 return wfExpandUrl( $this->getRequestURL(), PROTO_CURRENT ); 00743 } 00744 00751 public function appendQuery( $query ) { 00752 return $this->appendQueryArray( wfCgiToArray( $query ) ); 00753 } 00754 00762 public function escapeAppendQuery( $query ) { 00763 return htmlspecialchars( $this->appendQuery( $query ) ); 00764 } 00765 00772 public function appendQueryValue( $key, $value, $onlyquery = false ) { 00773 return $this->appendQueryArray( array( $key => $value ), $onlyquery ); 00774 } 00775 00784 public function appendQueryArray( $array, $onlyquery = false ) { 00785 global $wgTitle; 00786 $newquery = $this->getQueryValues(); 00787 unset( $newquery['title'] ); 00788 $newquery = array_merge( $newquery, $array ); 00789 $query = wfArrayToCgi( $newquery ); 00790 return $onlyquery ? $query : $wgTitle->getLocalURL( $query ); 00791 } 00792 00802 public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) { 00803 global $wgUser; 00804 00805 $limit = $this->getInt( 'limit', 0 ); 00806 if ( $limit < 0 ) { 00807 $limit = 0; 00808 } 00809 if ( ( $limit == 0 ) && ( $optionname != '' ) ) { 00810 $limit = $wgUser->getIntOption( $optionname ); 00811 } 00812 if ( $limit <= 0 ) { 00813 $limit = $deflimit; 00814 } 00815 if ( $limit > 5000 ) { 00816 $limit = 5000; # We have *some* limits... 00817 } 00818 00819 $offset = $this->getInt( 'offset', 0 ); 00820 if ( $offset < 0 ) { 00821 $offset = 0; 00822 } 00823 00824 return array( $limit, $offset ); 00825 } 00826 00833 public function getFileTempname( $key ) { 00834 $file = new WebRequestUpload( $this, $key ); 00835 return $file->getTempName(); 00836 } 00837 00844 public function getUploadError( $key ) { 00845 $file = new WebRequestUpload( $this, $key ); 00846 return $file->getError(); 00847 } 00848 00860 public function getFileName( $key ) { 00861 $file = new WebRequestUpload( $this, $key ); 00862 return $file->getName(); 00863 } 00864 00871 public function getUpload( $key ) { 00872 return new WebRequestUpload( $this, $key ); 00873 } 00874 00881 public function response() { 00882 /* Lazy initialization of response object for this request */ 00883 if ( !is_object( $this->response ) ) { 00884 $class = ( $this instanceof FauxRequest ) ? 'FauxResponse' : 'WebResponse'; 00885 $this->response = new $class(); 00886 } 00887 return $this->response; 00888 } 00889 00893 private function initHeaders() { 00894 if ( count( $this->headers ) ) { 00895 return; 00896 } 00897 00898 $apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false; 00899 if ( $apacheHeaders ) { 00900 foreach ( $apacheHeaders as $tempName => $tempValue ) { 00901 $this->headers[strtoupper( $tempName )] = $tempValue; 00902 } 00903 } else { 00904 foreach ( $_SERVER as $name => $value ) { 00905 if ( substr( $name, 0, 5 ) === 'HTTP_' ) { 00906 $name = str_replace( '_', '-', substr( $name, 5 ) ); 00907 $this->headers[$name] = $value; 00908 } elseif ( $name === 'CONTENT_LENGTH' ) { 00909 $this->headers['CONTENT-LENGTH'] = $value; 00910 } 00911 } 00912 } 00913 } 00914 00920 public function getAllHeaders() { 00921 $this->initHeaders(); 00922 return $this->headers; 00923 } 00924 00931 public function getHeader( $name ) { 00932 $this->initHeaders(); 00933 $name = strtoupper( $name ); 00934 if ( isset( $this->headers[$name] ) ) { 00935 return $this->headers[$name]; 00936 } else { 00937 return false; 00938 } 00939 } 00940 00947 public function getSessionData( $key ) { 00948 if ( !isset( $_SESSION[$key] ) ) { 00949 return null; 00950 } 00951 return $_SESSION[$key]; 00952 } 00953 00960 public function setSessionData( $key, $data ) { 00961 $_SESSION[$key] = $data; 00962 } 00963 00974 public function checkUrlExtension( $extWhitelist = array() ) { 00975 global $wgScriptExtension; 00976 $extWhitelist[] = ltrim( $wgScriptExtension, '.' ); 00977 if ( IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ) ) { 00978 if ( !$this->wasPosted() ) { 00979 $newUrl = IEUrlExtension::fixUrlForIE6( 00980 $this->getFullRequestURL(), $extWhitelist ); 00981 if ( $newUrl !== false ) { 00982 $this->doSecurityRedirect( $newUrl ); 00983 return false; 00984 } 00985 } 00986 throw new HttpError( 403, 00987 'Invalid file extension found in the path info or query string.' ); 00988 } 00989 return true; 00990 } 00991 00999 protected function doSecurityRedirect( $url ) { 01000 header( 'Location: ' . $url ); 01001 header( 'Content-Type: text/html' ); 01002 $encUrl = htmlspecialchars( $url ); 01003 echo <<<HTML 01004 <html> 01005 <head> 01006 <title>Security redirect</title> 01007 </head> 01008 <body> 01009 <h1>Security redirect</h1> 01010 <p> 01011 We can't serve non-HTML content from the URL you have requested, because 01012 Internet Explorer would interpret it as an incorrect and potentially dangerous 01013 content type.</p> 01014 <p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the URL you have requested, except that 01015 "&*" is appended. This prevents Internet Explorer from seeing a bogus file 01016 extension. 01017 </p> 01018 </body> 01019 </html> 01020 HTML; 01021 echo "\n"; 01022 return true; 01023 } 01024 01033 public function getAcceptLang() { 01034 // Modified version of code found at http://www.thefutureoftheweb.com/blog/use-accept-language-header 01035 $acceptLang = $this->getHeader( 'Accept-Language' ); 01036 if ( !$acceptLang ) { 01037 return array(); 01038 } 01039 01040 // Return the language codes in lower case 01041 $acceptLang = strtolower( $acceptLang ); 01042 01043 // Break up string into pieces (languages and q factors) 01044 $lang_parse = null; 01045 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})?)?)?/', 01046 $acceptLang, $lang_parse ); 01047 01048 if ( !count( $lang_parse[1] ) ) { 01049 return array(); 01050 } 01051 01052 $langcodes = $lang_parse[1]; 01053 $qvalues = $lang_parse[4]; 01054 $indices = range( 0, count( $lang_parse[1] ) - 1 ); 01055 01056 // Set default q factor to 1 01057 foreach ( $indices as $index ) { 01058 if ( $qvalues[$index] === '' ) { 01059 $qvalues[$index] = 1; 01060 } elseif ( $qvalues[$index] == 0 ) { 01061 unset( $langcodes[$index], $qvalues[$index], $indices[$index] ); 01062 } 01063 } 01064 01065 // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way 01066 array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes ); 01067 01068 // Create a list like "en" => 0.8 01069 $langs = array_combine( $langcodes, $qvalues ); 01070 01071 return $langs; 01072 } 01073 01082 protected function getRawIP() { 01083 if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) { 01084 return null; 01085 } 01086 01087 if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) { 01088 throw new MWException( __METHOD__ . " : Could not determine the remote IP address due to multiple values." ); 01089 } else { 01090 $ipchain = $_SERVER['REMOTE_ADDR']; 01091 } 01092 01093 return IP::canonicalize( $ipchain ); 01094 } 01095 01105 public function getIP() { 01106 global $wgUsePrivateIPs; 01107 01108 # Return cached result 01109 if ( $this->ip !== null ) { 01110 return $this->ip; 01111 } 01112 01113 # collect the originating ips 01114 $ip = $this->getRawIP(); 01115 01116 # Append XFF 01117 $forwardedFor = $this->getHeader( 'X-Forwarded-For' ); 01118 if ( $forwardedFor !== false ) { 01119 $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) ); 01120 $ipchain = array_reverse( $ipchain ); 01121 if ( $ip ) { 01122 array_unshift( $ipchain, $ip ); 01123 } 01124 01125 # Step through XFF list and find the last address in the list which is a 01126 # trusted server. Set $ip to the IP address given by that trusted server, 01127 # unless the address is not sensible (e.g. private). However, prefer private 01128 # IP addresses over proxy servers controlled by this site (more sensible). 01129 foreach ( $ipchain as $i => $curIP ) { 01130 // ignore 'unknown' value from Squid when 'forwarded_for off' and try next 01131 if ( $curIP === 'unknown' ) { 01132 continue; 01133 } 01134 $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) ); 01135 if ( wfIsTrustedProxy( $curIP ) && isset( $ipchain[$i + 1] ) ) { 01136 if ( wfIsConfiguredProxy( $curIP ) || // bug 48919; treat IP as sane 01137 IP::isPublic( $ipchain[$i + 1] ) || 01138 $wgUsePrivateIPs 01139 ) { 01140 $nextIP = IP::canonicalize( $ipchain[$i + 1] ); 01141 if ( !$nextIP && wfIsConfiguredProxy( $ip ) ) { 01142 // We have not yet made it past CDN/proxy servers of this site, 01143 // so either they are misconfigured or there is some IP spoofing. 01144 throw new MWException( "Invalid IP given in XFF '$forwardedFor'." ); 01145 } 01146 $ip = $nextIP; 01147 continue; 01148 } 01149 } 01150 break; 01151 } 01152 } 01153 01154 # Allow extensions to improve our guess 01155 wfRunHooks( 'GetIP', array( &$ip ) ); 01156 01157 if ( !$ip ) { 01158 throw new MWException( "Unable to determine IP." ); 01159 } 01160 01161 wfDebug( "IP: $ip\n" ); 01162 $this->ip = $ip; 01163 return $ip; 01164 } 01165 01171 public function setIP( $ip ) { 01172 $this->ip = $ip; 01173 } 01174 } 01175 01179 class WebRequestUpload { 01180 protected $request; 01181 protected $doesExist; 01182 protected $fileInfo; 01183 01190 public function __construct( $request, $key ) { 01191 $this->request = $request; 01192 $this->doesExist = isset( $_FILES[$key] ); 01193 if ( $this->doesExist ) { 01194 $this->fileInfo = $_FILES[$key]; 01195 } 01196 } 01197 01203 public function exists() { 01204 return $this->doesExist; 01205 } 01206 01212 public function getName() { 01213 if ( !$this->exists() ) { 01214 return null; 01215 } 01216 01217 global $wgContLang; 01218 $name = $this->fileInfo['name']; 01219 01220 # Safari sends filenames in HTML-encoded Unicode form D... 01221 # Horrid and evil! Let's try to make some kind of sense of it. 01222 $name = Sanitizer::decodeCharReferences( $name ); 01223 $name = $wgContLang->normalize( $name ); 01224 wfDebug( __METHOD__ . ": {$this->fileInfo['name']} normalized to '$name'\n" ); 01225 return $name; 01226 } 01227 01233 public function getSize() { 01234 if ( !$this->exists() ) { 01235 return 0; 01236 } 01237 01238 return $this->fileInfo['size']; 01239 } 01240 01246 public function getTempName() { 01247 if ( !$this->exists() ) { 01248 return null; 01249 } 01250 01251 return $this->fileInfo['tmp_name']; 01252 } 01253 01260 public function getError() { 01261 if ( !$this->exists() ) { 01262 return 0; # UPLOAD_ERR_OK 01263 } 01264 01265 return $this->fileInfo['error']; 01266 } 01267 01274 public function isIniSizeOverflow() { 01275 if ( $this->getError() == UPLOAD_ERR_INI_SIZE ) { 01276 # PHP indicated that upload_max_filesize is exceeded 01277 return true; 01278 } 01279 01280 $contentLength = $this->request->getHeader( 'CONTENT_LENGTH' ); 01281 if ( $contentLength > wfShorthandToInteger( ini_get( 'post_max_size' ) ) ) { 01282 # post_max_size is exceeded 01283 return true; 01284 } 01285 01286 return false; 01287 } 01288 } 01289 01295 class FauxRequest extends WebRequest { 01296 private $wasPosted = false; 01297 private $session = array(); 01298 01307 public function __construct( $data = array(), $wasPosted = false, $session = null, $protocol = 'http' ) { 01308 if ( is_array( $data ) ) { 01309 $this->data = $data; 01310 } else { 01311 throw new MWException( "FauxRequest() got bogus data" ); 01312 } 01313 $this->wasPosted = $wasPosted; 01314 if ( $session ) { 01315 $this->session = $session; 01316 } 01317 $this->protocol = $protocol; 01318 } 01319 01324 private function notImplemented( $method ) { 01325 throw new MWException( "{$method}() not implemented" ); 01326 } 01327 01333 public function getText( $name, $default = '' ) { 01334 # Override; don't recode since we're using internal data 01335 return (string)$this->getVal( $name, $default ); 01336 } 01337 01341 public function getValues() { 01342 return $this->data; 01343 } 01344 01348 public function getQueryValues() { 01349 if ( $this->wasPosted ) { 01350 return array(); 01351 } else { 01352 return $this->data; 01353 } 01354 } 01355 01356 public function getMethod() { 01357 return $this->wasPosted ? 'POST' : 'GET'; 01358 } 01359 01363 public function wasPosted() { 01364 return $this->wasPosted; 01365 } 01366 01367 public function getCookie( $key, $prefix = null, $default = null ) { 01368 return $default; 01369 } 01370 01371 public function checkSessionCookie() { 01372 return false; 01373 } 01374 01375 public function getRequestURL() { 01376 $this->notImplemented( __METHOD__ ); 01377 } 01378 01379 public function getProtocol() { 01380 return $this->protocol; 01381 } 01382 01387 public function getHeader( $name ) { 01388 $name = strtoupper( $name ); 01389 return isset( $this->headers[$name] ) ? $this->headers[$name] : false; 01390 } 01391 01396 public function setHeader( $name, $val ) { 01397 $name = strtoupper( $name ); 01398 $this->headers[$name] = $val; 01399 } 01400 01405 public function getSessionData( $key ) { 01406 if ( isset( $this->session[$key] ) ) { 01407 return $this->session[$key]; 01408 } 01409 return null; 01410 } 01411 01416 public function setSessionData( $key, $data ) { 01417 $this->session[$key] = $data; 01418 } 01419 01423 public function getSessionArray() { 01424 return $this->session; 01425 } 01426 01431 public function getRawQueryString() { 01432 return ''; 01433 } 01434 01439 public function getRawPostString() { 01440 return ''; 01441 } 01442 01447 public function getRawInput() { 01448 return ''; 01449 } 01450 01455 public function checkUrlExtension( $extWhitelist = array() ) { 01456 return true; 01457 } 01458 01462 protected function getRawIP() { 01463 return '127.0.0.1'; 01464 } 01465 } 01466 01475 class DerivativeRequest extends FauxRequest { 01476 private $base; 01477 01484 public function __construct( WebRequest $base, $data, $wasPosted = false ) { 01485 $this->base = $base; 01486 parent::__construct( $data, $wasPosted ); 01487 } 01488 01489 public function getCookie( $key, $prefix = null, $default = null ) { 01490 return $this->base->getCookie( $key, $prefix, $default ); 01491 } 01492 01493 public function checkSessionCookie() { 01494 return $this->base->checkSessionCookie(); 01495 } 01496 01497 public function getHeader( $name ) { 01498 return $this->base->getHeader( $name ); 01499 } 01500 01501 public function getAllHeaders() { 01502 return $this->base->getAllHeaders(); 01503 } 01504 01505 public function getSessionData( $key ) { 01506 return $this->base->getSessionData( $key ); 01507 } 01508 01509 public function setSessionData( $key, $data ) { 01510 $this->base->setSessionData( $key, $data ); 01511 } 01512 01513 public function getAcceptLang() { 01514 return $this->base->getAcceptLang(); 01515 } 01516 01517 public function getIP() { 01518 return $this->base->getIP(); 01519 } 01520 01521 public function getProtocol() { 01522 return $this->base->getProtocol(); 01523 } 01524 }