MediaWiki
REL1_24
|
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() { 00060 if ( function_exists( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ) { 00061 throw new MWException( "MediaWiki does not function when magic quotes are enabled." ); 00062 } 00063 00064 // POST overrides GET data 00065 // We don't use $_REQUEST here to avoid interference from cookies... 00066 $this->data = $_POST + $_GET; 00067 } 00068 00084 public static function getPathInfo( $want = 'all' ) { 00085 global $wgUsePathInfo; 00086 // PATH_INFO is mangled due to http://bugs.php.net/bug.php?id=31892 00087 // And also by Apache 2.x, double slashes are converted to single slashes. 00088 // So we will use REQUEST_URI if possible. 00089 $matches = array(); 00090 if ( !empty( $_SERVER['REQUEST_URI'] ) ) { 00091 // Slurp out the path portion to examine... 00092 $url = $_SERVER['REQUEST_URI']; 00093 if ( !preg_match( '!^https?://!', $url ) ) { 00094 $url = 'http://unused' . $url; 00095 } 00096 wfSuppressWarnings(); 00097 $a = parse_url( $url ); 00098 wfRestoreWarnings(); 00099 if ( $a ) { 00100 $path = isset( $a['path'] ) ? $a['path'] : ''; 00101 00102 global $wgScript; 00103 if ( $path == $wgScript && $want !== 'all' ) { 00104 // Script inside a rewrite path? 00105 // Abort to keep from breaking... 00106 return $matches; 00107 } 00108 00109 $router = new PathRouter; 00110 00111 // Raw PATH_INFO style 00112 $router->add( "$wgScript/$1" ); 00113 00114 if ( isset( $_SERVER['SCRIPT_NAME'] ) 00115 && preg_match( '/\.php5?/', $_SERVER['SCRIPT_NAME'] ) 00116 ) { 00117 # Check for SCRIPT_NAME, we handle index.php explicitly 00118 # But we do have some other .php files such as img_auth.php 00119 # Don't let root article paths clober the parsing for them 00120 $router->add( $_SERVER['SCRIPT_NAME'] . "/$1" ); 00121 } 00122 00123 global $wgArticlePath; 00124 if ( $wgArticlePath ) { 00125 $router->add( $wgArticlePath ); 00126 } 00127 00128 global $wgActionPaths; 00129 if ( $wgActionPaths ) { 00130 $router->add( $wgActionPaths, array( 'action' => '$key' ) ); 00131 } 00132 00133 global $wgVariantArticlePath, $wgContLang; 00134 if ( $wgVariantArticlePath ) { 00135 $router->add( $wgVariantArticlePath, 00136 array( 'variant' => '$2' ), 00137 array( '$2' => $wgContLang->getVariants() ) 00138 ); 00139 } 00140 00141 wfRunHooks( 'WebRequestPathInfoRouter', array( $router ) ); 00142 00143 $matches = $router->parse( $path ); 00144 } 00145 } elseif ( $wgUsePathInfo ) { 00146 if ( isset( $_SERVER['ORIG_PATH_INFO'] ) && $_SERVER['ORIG_PATH_INFO'] != '' ) { 00147 // Mangled PATH_INFO 00148 // http://bugs.php.net/bug.php?id=31892 00149 // Also reported when ini_get('cgi.fix_pathinfo')==false 00150 $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 ); 00151 00152 } elseif ( isset( $_SERVER['PATH_INFO'] ) && $_SERVER['PATH_INFO'] != '' ) { 00153 // Regular old PATH_INFO yay 00154 $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 ); 00155 } 00156 } 00157 00158 return $matches; 00159 } 00160 00167 public static function detectServer() { 00168 $proto = self::detectProtocol(); 00169 $stdPort = $proto === 'https' ? 443 : 80; 00170 00171 $varNames = array( 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ); 00172 $host = 'localhost'; 00173 $port = $stdPort; 00174 foreach ( $varNames as $varName ) { 00175 if ( !isset( $_SERVER[$varName] ) ) { 00176 continue; 00177 } 00178 $parts = IP::splitHostAndPort( $_SERVER[$varName] ); 00179 if ( !$parts ) { 00180 // Invalid, do not use 00181 continue; 00182 } 00183 $host = $parts[0]; 00184 if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) { 00185 // Bug 70021: Assume that upstream proxy is running on the default 00186 // port based on the protocol. We have no reliable way to determine 00187 // the actual port in use upstream. 00188 $port = $stdPort; 00189 } elseif ( $parts[1] === false ) { 00190 if ( isset( $_SERVER['SERVER_PORT'] ) ) { 00191 $port = $_SERVER['SERVER_PORT']; 00192 } // else leave it as $stdPort 00193 } else { 00194 $port = $parts[1]; 00195 } 00196 break; 00197 } 00198 00199 return $proto . '://' . IP::combineHostAndPort( $host, $port, $stdPort ); 00200 } 00201 00209 public static function detectProtocol() { 00210 if ( ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) || 00211 ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 00212 $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) ) { 00213 return 'https'; 00214 } else { 00215 return 'http'; 00216 } 00217 } 00218 00223 public function getProtocol() { 00224 if ( $this->protocol === null ) { 00225 $this->protocol = self::detectProtocol(); 00226 } 00227 return $this->protocol; 00228 } 00229 00237 public function interpolateTitle() { 00238 // bug 16019: title interpolation on API queries is useless and sometimes harmful 00239 if ( defined( 'MW_API' ) ) { 00240 return; 00241 } 00242 00243 $matches = self::getPathInfo( 'title' ); 00244 foreach ( $matches as $key => $val ) { 00245 $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val; 00246 } 00247 } 00248 00259 static function extractTitle( $path, $bases, $key = false ) { 00260 foreach ( (array)$bases as $keyValue => $base ) { 00261 // Find the part after $wgArticlePath 00262 $base = str_replace( '$1', '', $base ); 00263 $baseLen = strlen( $base ); 00264 if ( substr( $path, 0, $baseLen ) == $base ) { 00265 $raw = substr( $path, $baseLen ); 00266 if ( $raw !== '' ) { 00267 $matches = array( 'title' => rawurldecode( $raw ) ); 00268 if ( $key ) { 00269 $matches[$key] = $keyValue; 00270 } 00271 return $matches; 00272 } 00273 } 00274 } 00275 return array(); 00276 } 00277 00285 function normalizeUnicode( $data ) { 00286 if ( is_array( $data ) ) { 00287 foreach ( $data as $key => $val ) { 00288 $data[$key] = $this->normalizeUnicode( $val ); 00289 } 00290 } else { 00291 global $wgContLang; 00292 $data = isset( $wgContLang ) ? $wgContLang->normalize( $data ) : UtfNormal::cleanUp( $data ); 00293 } 00294 return $data; 00295 } 00296 00305 private function getGPCVal( $arr, $name, $default ) { 00306 # PHP is so nice to not touch input data, except sometimes: 00307 # http://us2.php.net/variables.external#language.variables.external.dot-in-names 00308 # Work around PHP *feature* to avoid *bugs* elsewhere. 00309 $name = strtr( $name, '.', '_' ); 00310 if ( isset( $arr[$name] ) ) { 00311 global $wgContLang; 00312 $data = $arr[$name]; 00313 if ( isset( $_GET[$name] ) && !is_array( $data ) ) { 00314 # Check for alternate/legacy character encoding. 00315 if ( isset( $wgContLang ) ) { 00316 $data = $wgContLang->checkTitleEncoding( $data ); 00317 } 00318 } 00319 $data = $this->normalizeUnicode( $data ); 00320 return $data; 00321 } else { 00322 return $default; 00323 } 00324 } 00325 00336 public function getVal( $name, $default = null ) { 00337 $val = $this->getGPCVal( $this->data, $name, $default ); 00338 if ( is_array( $val ) ) { 00339 $val = $default; 00340 } 00341 if ( is_null( $val ) ) { 00342 return $val; 00343 } else { 00344 return (string)$val; 00345 } 00346 } 00347 00355 public function setVal( $key, $value ) { 00356 $ret = isset( $this->data[$key] ) ? $this->data[$key] : null; 00357 $this->data[$key] = $value; 00358 return $ret; 00359 } 00360 00367 public function unsetVal( $key ) { 00368 if ( !isset( $this->data[$key] ) ) { 00369 $ret = null; 00370 } else { 00371 $ret = $this->data[$key]; 00372 unset( $this->data[$key] ); 00373 } 00374 return $ret; 00375 } 00376 00386 public function getArray( $name, $default = null ) { 00387 $val = $this->getGPCVal( $this->data, $name, $default ); 00388 if ( is_null( $val ) ) { 00389 return null; 00390 } else { 00391 return (array)$val; 00392 } 00393 } 00394 00405 public function getIntArray( $name, $default = null ) { 00406 $val = $this->getArray( $name, $default ); 00407 if ( is_array( $val ) ) { 00408 $val = array_map( 'intval', $val ); 00409 } 00410 return $val; 00411 } 00412 00422 public function getInt( $name, $default = 0 ) { 00423 return intval( $this->getVal( $name, $default ) ); 00424 } 00425 00434 public function getIntOrNull( $name ) { 00435 $val = $this->getVal( $name ); 00436 return is_numeric( $val ) 00437 ? intval( $val ) 00438 : null; 00439 } 00440 00451 public function getFloat( $name, $default = 0.0 ) { 00452 return floatval( $this->getVal( $name, $default ) ); 00453 } 00454 00464 public function getBool( $name, $default = false ) { 00465 return (bool)$this->getVal( $name, $default ); 00466 } 00467 00477 public function getFuzzyBool( $name, $default = false ) { 00478 return $this->getBool( $name, $default ) && strcasecmp( $this->getVal( $name ), 'false' ) !== 0; 00479 } 00480 00489 public function getCheck( $name ) { 00490 # Checkboxes and buttons are only present when clicked 00491 # Presence connotes truth, absence false 00492 return $this->getVal( $name, null ) !== null; 00493 } 00494 00507 public function getText( $name, $default = '' ) { 00508 global $wgContLang; 00509 $val = $this->getVal( $name, $default ); 00510 return str_replace( "\r\n", "\n", 00511 $wgContLang->recodeInput( $val ) ); 00512 } 00513 00521 public function getValues() { 00522 $names = func_get_args(); 00523 if ( count( $names ) == 0 ) { 00524 $names = array_keys( $this->data ); 00525 } 00526 00527 $retVal = array(); 00528 foreach ( $names as $name ) { 00529 $value = $this->getGPCVal( $this->data, $name, null ); 00530 if ( !is_null( $value ) ) { 00531 $retVal[$name] = $value; 00532 } 00533 } 00534 return $retVal; 00535 } 00536 00543 public function getValueNames( $exclude = array() ) { 00544 return array_diff( array_keys( $this->getValues() ), $exclude ); 00545 } 00546 00553 public function getQueryValues() { 00554 return $_GET; 00555 } 00556 00563 public function getRawQueryString() { 00564 return $_SERVER['QUERY_STRING']; 00565 } 00566 00573 public function getRawPostString() { 00574 if ( !$this->wasPosted() ) { 00575 return ''; 00576 } 00577 return $this->getRawInput(); 00578 } 00579 00587 public function getRawInput() { 00588 static $input = null; 00589 if ( $input === null ) { 00590 $input = file_get_contents( 'php://input' ); 00591 } 00592 return $input; 00593 } 00594 00600 public function getMethod() { 00601 return isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : 'GET'; 00602 } 00603 00613 public function wasPosted() { 00614 return $this->getMethod() == 'POST'; 00615 } 00616 00628 public function checkSessionCookie() { 00629 return isset( $_COOKIE[session_name()] ); 00630 } 00631 00640 public function getCookie( $key, $prefix = null, $default = null ) { 00641 if ( $prefix === null ) { 00642 global $wgCookiePrefix; 00643 $prefix = $wgCookiePrefix; 00644 } 00645 return $this->getGPCVal( $_COOKIE, $prefix . $key, $default ); 00646 } 00647 00655 public function getRequestURL() { 00656 if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) { 00657 $base = $_SERVER['REQUEST_URI']; 00658 } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] ) 00659 && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] ) 00660 ) { 00661 // Probably IIS; doesn't set REQUEST_URI 00662 $base = $_SERVER['HTTP_X_ORIGINAL_URL']; 00663 } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) { 00664 $base = $_SERVER['SCRIPT_NAME']; 00665 if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) { 00666 $base .= '?' . $_SERVER['QUERY_STRING']; 00667 } 00668 } else { 00669 // This shouldn't happen! 00670 throw new MWException( "Web server doesn't provide either " . 00671 "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " . 00672 "of your web server configuration to http://bugzilla.wikimedia.org/" ); 00673 } 00674 // User-agents should not send a fragment with the URI, but 00675 // if they do, and the web server passes it on to us, we 00676 // need to strip it or we get false-positive redirect loops 00677 // or weird output URLs 00678 $hash = strpos( $base, '#' ); 00679 if ( $hash !== false ) { 00680 $base = substr( $base, 0, $hash ); 00681 } 00682 00683 if ( $base[0] == '/' ) { 00684 // More than one slash will look like it is protocol relative 00685 return preg_replace( '!^/+!', '/', $base ); 00686 } else { 00687 // We may get paths with a host prepended; strip it. 00688 return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base ); 00689 } 00690 } 00691 00702 public function getFullRequestURL() { 00703 return wfExpandUrl( $this->getRequestURL(), PROTO_CURRENT ); 00704 } 00705 00712 public function appendQuery( $query ) { 00713 return $this->appendQueryArray( wfCgiToArray( $query ) ); 00714 } 00715 00722 public function appendQueryValue( $key, $value, $onlyquery = false ) { 00723 return $this->appendQueryArray( array( $key => $value ), $onlyquery ); 00724 } 00725 00733 public function appendQueryArray( $array, $onlyquery = false ) { 00734 global $wgTitle; 00735 $newquery = $this->getQueryValues(); 00736 unset( $newquery['title'] ); 00737 $newquery = array_merge( $newquery, $array ); 00738 $query = wfArrayToCgi( $newquery ); 00739 return $onlyquery ? $query : $wgTitle->getLocalURL( $query ); 00740 } 00741 00751 public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) { 00752 global $wgUser; 00753 00754 $limit = $this->getInt( 'limit', 0 ); 00755 if ( $limit < 0 ) { 00756 $limit = 0; 00757 } 00758 if ( ( $limit == 0 ) && ( $optionname != '' ) ) { 00759 $limit = $wgUser->getIntOption( $optionname ); 00760 } 00761 if ( $limit <= 0 ) { 00762 $limit = $deflimit; 00763 } 00764 if ( $limit > 5000 ) { 00765 $limit = 5000; # We have *some* limits... 00766 } 00767 00768 $offset = $this->getInt( 'offset', 0 ); 00769 if ( $offset < 0 ) { 00770 $offset = 0; 00771 } 00772 00773 return array( $limit, $offset ); 00774 } 00775 00782 public function getFileTempname( $key ) { 00783 $file = new WebRequestUpload( $this, $key ); 00784 return $file->getTempName(); 00785 } 00786 00793 public function getUploadError( $key ) { 00794 $file = new WebRequestUpload( $this, $key ); 00795 return $file->getError(); 00796 } 00797 00809 public function getFileName( $key ) { 00810 $file = new WebRequestUpload( $this, $key ); 00811 return $file->getName(); 00812 } 00813 00820 public function getUpload( $key ) { 00821 return new WebRequestUpload( $this, $key ); 00822 } 00823 00830 public function response() { 00831 /* Lazy initialization of response object for this request */ 00832 if ( !is_object( $this->response ) ) { 00833 $class = ( $this instanceof FauxRequest ) ? 'FauxResponse' : 'WebResponse'; 00834 $this->response = new $class(); 00835 } 00836 return $this->response; 00837 } 00838 00842 private function initHeaders() { 00843 if ( count( $this->headers ) ) { 00844 return; 00845 } 00846 00847 $apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false; 00848 if ( $apacheHeaders ) { 00849 foreach ( $apacheHeaders as $tempName => $tempValue ) { 00850 $this->headers[strtoupper( $tempName )] = $tempValue; 00851 } 00852 } else { 00853 foreach ( $_SERVER as $name => $value ) { 00854 if ( substr( $name, 0, 5 ) === 'HTTP_' ) { 00855 $name = str_replace( '_', '-', substr( $name, 5 ) ); 00856 $this->headers[$name] = $value; 00857 } elseif ( $name === 'CONTENT_LENGTH' ) { 00858 $this->headers['CONTENT-LENGTH'] = $value; 00859 } 00860 } 00861 } 00862 } 00863 00869 public function getAllHeaders() { 00870 $this->initHeaders(); 00871 return $this->headers; 00872 } 00873 00880 public function getHeader( $name ) { 00881 $this->initHeaders(); 00882 $name = strtoupper( $name ); 00883 if ( isset( $this->headers[$name] ) ) { 00884 return $this->headers[$name]; 00885 } else { 00886 return false; 00887 } 00888 } 00889 00896 public function getSessionData( $key ) { 00897 if ( !isset( $_SESSION[$key] ) ) { 00898 return null; 00899 } 00900 return $_SESSION[$key]; 00901 } 00902 00909 public function setSessionData( $key, $data ) { 00910 $_SESSION[$key] = $data; 00911 } 00912 00923 public function checkUrlExtension( $extWhitelist = array() ) { 00924 global $wgScriptExtension; 00925 $extWhitelist[] = ltrim( $wgScriptExtension, '.' ); 00926 if ( IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ) ) { 00927 if ( !$this->wasPosted() ) { 00928 $newUrl = IEUrlExtension::fixUrlForIE6( 00929 $this->getFullRequestURL(), $extWhitelist ); 00930 if ( $newUrl !== false ) { 00931 $this->doSecurityRedirect( $newUrl ); 00932 return false; 00933 } 00934 } 00935 throw new HttpError( 403, 00936 'Invalid file extension found in the path info or query string.' ); 00937 } 00938 return true; 00939 } 00940 00948 protected function doSecurityRedirect( $url ) { 00949 header( 'Location: ' . $url ); 00950 header( 'Content-Type: text/html' ); 00951 $encUrl = htmlspecialchars( $url ); 00952 echo <<<HTML 00953 <html> 00954 <head> 00955 <title>Security redirect</title> 00956 </head> 00957 <body> 00958 <h1>Security redirect</h1> 00959 <p> 00960 We can't serve non-HTML content from the URL you have requested, because 00961 Internet Explorer would interpret it as an incorrect and potentially dangerous 00962 content type.</p> 00963 <p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the 00964 URL you have requested, except that "&*" is appended. This prevents Internet 00965 Explorer from seeing a bogus file extension. 00966 </p> 00967 </body> 00968 </html> 00969 HTML; 00970 echo "\n"; 00971 return true; 00972 } 00973 00983 public function getAcceptLang() { 00984 // Modified version of code found at 00985 // http://www.thefutureoftheweb.com/blog/use-accept-language-header 00986 $acceptLang = $this->getHeader( 'Accept-Language' ); 00987 if ( !$acceptLang ) { 00988 return array(); 00989 } 00990 00991 // Return the language codes in lower case 00992 $acceptLang = strtolower( $acceptLang ); 00993 00994 // Break up string into pieces (languages and q factors) 00995 $lang_parse = null; 00996 preg_match_all( 00997 '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/', 00998 $acceptLang, 00999 $lang_parse 01000 ); 01001 01002 if ( !count( $lang_parse[1] ) ) { 01003 return array(); 01004 } 01005 01006 $langcodes = $lang_parse[1]; 01007 $qvalues = $lang_parse[4]; 01008 $indices = range( 0, count( $lang_parse[1] ) - 1 ); 01009 01010 // Set default q factor to 1 01011 foreach ( $indices as $index ) { 01012 if ( $qvalues[$index] === '' ) { 01013 $qvalues[$index] = 1; 01014 } elseif ( $qvalues[$index] == 0 ) { 01015 unset( $langcodes[$index], $qvalues[$index], $indices[$index] ); 01016 } 01017 } 01018 01019 // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way 01020 array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes ); 01021 01022 // Create a list like "en" => 0.8 01023 $langs = array_combine( $langcodes, $qvalues ); 01024 01025 return $langs; 01026 } 01027 01036 protected function getRawIP() { 01037 if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) { 01038 return null; 01039 } 01040 01041 if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) { 01042 throw new MWException( __METHOD__ 01043 . " : Could not determine the remote IP address due to multiple values." ); 01044 } else { 01045 $ipchain = $_SERVER['REMOTE_ADDR']; 01046 } 01047 01048 return IP::canonicalize( $ipchain ); 01049 } 01050 01060 public function getIP() { 01061 global $wgUsePrivateIPs; 01062 01063 # Return cached result 01064 if ( $this->ip !== null ) { 01065 return $this->ip; 01066 } 01067 01068 # collect the originating ips 01069 $ip = $this->getRawIP(); 01070 if ( !$ip ) { 01071 throw new MWException( 'Unable to determine IP.' ); 01072 } 01073 01074 # Append XFF 01075 $forwardedFor = $this->getHeader( 'X-Forwarded-For' ); 01076 if ( $forwardedFor !== false ) { 01077 $isConfigured = IP::isConfiguredProxy( $ip ); 01078 $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) ); 01079 $ipchain = array_reverse( $ipchain ); 01080 array_unshift( $ipchain, $ip ); 01081 01082 # Step through XFF list and find the last address in the list which is a 01083 # trusted server. Set $ip to the IP address given by that trusted server, 01084 # unless the address is not sensible (e.g. private). However, prefer private 01085 # IP addresses over proxy servers controlled by this site (more sensible). 01086 # Note that some XFF values might be "unknown" with Squid/Varnish. 01087 foreach ( $ipchain as $i => $curIP ) { 01088 $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) ); 01089 if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown' 01090 || !IP::isTrustedProxy( $curIP ) 01091 ) { 01092 break; // IP is not valid/trusted or does not point to anything 01093 } 01094 if ( 01095 IP::isPublic( $ipchain[$i + 1] ) || 01096 $wgUsePrivateIPs || 01097 IP::isConfiguredProxy( $curIP ) // bug 48919; treat IP as sane 01098 ) { 01099 // Follow the next IP according to the proxy 01100 $nextIP = IP::canonicalize( $ipchain[$i + 1] ); 01101 if ( !$nextIP && $isConfigured ) { 01102 // We have not yet made it past CDN/proxy servers of this site, 01103 // so either they are misconfigured or there is some IP spoofing. 01104 throw new MWException( "Invalid IP given in XFF '$forwardedFor'." ); 01105 } 01106 $ip = $nextIP; 01107 // keep traversing the chain 01108 continue; 01109 } 01110 break; 01111 } 01112 } 01113 01114 # Allow extensions to improve our guess 01115 wfRunHooks( 'GetIP', array( &$ip ) ); 01116 01117 if ( !$ip ) { 01118 throw new MWException( "Unable to determine IP." ); 01119 } 01120 01121 wfDebug( "IP: $ip\n" ); 01122 $this->ip = $ip; 01123 return $ip; 01124 } 01125 01131 public function setIP( $ip ) { 01132 $this->ip = $ip; 01133 } 01134 } 01135 01139 class WebRequestUpload { 01140 protected $request; 01141 protected $doesExist; 01142 protected $fileInfo; 01143 01150 public function __construct( $request, $key ) { 01151 $this->request = $request; 01152 $this->doesExist = isset( $_FILES[$key] ); 01153 if ( $this->doesExist ) { 01154 $this->fileInfo = $_FILES[$key]; 01155 } 01156 } 01157 01163 public function exists() { 01164 return $this->doesExist; 01165 } 01166 01172 public function getName() { 01173 if ( !$this->exists() ) { 01174 return null; 01175 } 01176 01177 global $wgContLang; 01178 $name = $this->fileInfo['name']; 01179 01180 # Safari sends filenames in HTML-encoded Unicode form D... 01181 # Horrid and evil! Let's try to make some kind of sense of it. 01182 $name = Sanitizer::decodeCharReferences( $name ); 01183 $name = $wgContLang->normalize( $name ); 01184 wfDebug( __METHOD__ . ": {$this->fileInfo['name']} normalized to '$name'\n" ); 01185 return $name; 01186 } 01187 01193 public function getSize() { 01194 if ( !$this->exists() ) { 01195 return 0; 01196 } 01197 01198 return $this->fileInfo['size']; 01199 } 01200 01206 public function getTempName() { 01207 if ( !$this->exists() ) { 01208 return null; 01209 } 01210 01211 return $this->fileInfo['tmp_name']; 01212 } 01213 01220 public function getError() { 01221 if ( !$this->exists() ) { 01222 return 0; # UPLOAD_ERR_OK 01223 } 01224 01225 return $this->fileInfo['error']; 01226 } 01227 01234 public function isIniSizeOverflow() { 01235 if ( $this->getError() == UPLOAD_ERR_INI_SIZE ) { 01236 # PHP indicated that upload_max_filesize is exceeded 01237 return true; 01238 } 01239 01240 $contentLength = $this->request->getHeader( 'CONTENT_LENGTH' ); 01241 if ( $contentLength > wfShorthandToInteger( ini_get( 'post_max_size' ) ) ) { 01242 # post_max_size is exceeded 01243 return true; 01244 } 01245 01246 return false; 01247 } 01248 } 01249 01255 class FauxRequest extends WebRequest { 01256 private $wasPosted = false; 01257 private $session = array(); 01258 01267 public function __construct( $data = array(), $wasPosted = false, 01268 $session = null, $protocol = 'http' 01269 ) { 01270 if ( is_array( $data ) ) { 01271 $this->data = $data; 01272 } else { 01273 throw new MWException( "FauxRequest() got bogus data" ); 01274 } 01275 $this->wasPosted = $wasPosted; 01276 if ( $session ) { 01277 $this->session = $session; 01278 } 01279 $this->protocol = $protocol; 01280 } 01281 01286 private function notImplemented( $method ) { 01287 throw new MWException( "{$method}() not implemented" ); 01288 } 01289 01295 public function getText( $name, $default = '' ) { 01296 # Override; don't recode since we're using internal data 01297 return (string)$this->getVal( $name, $default ); 01298 } 01299 01303 public function getValues() { 01304 return $this->data; 01305 } 01306 01310 public function getQueryValues() { 01311 if ( $this->wasPosted ) { 01312 return array(); 01313 } else { 01314 return $this->data; 01315 } 01316 } 01317 01318 public function getMethod() { 01319 return $this->wasPosted ? 'POST' : 'GET'; 01320 } 01321 01325 public function wasPosted() { 01326 return $this->wasPosted; 01327 } 01328 01329 public function getCookie( $key, $prefix = null, $default = null ) { 01330 return $default; 01331 } 01332 01333 public function checkSessionCookie() { 01334 return false; 01335 } 01336 01337 public function getRequestURL() { 01338 $this->notImplemented( __METHOD__ ); 01339 } 01340 01341 public function getProtocol() { 01342 return $this->protocol; 01343 } 01344 01349 public function getHeader( $name ) { 01350 $name = strtoupper( $name ); 01351 return isset( $this->headers[$name] ) ? $this->headers[$name] : false; 01352 } 01353 01358 public function setHeader( $name, $val ) { 01359 $name = strtoupper( $name ); 01360 $this->headers[$name] = $val; 01361 } 01362 01367 public function getSessionData( $key ) { 01368 if ( isset( $this->session[$key] ) ) { 01369 return $this->session[$key]; 01370 } 01371 return null; 01372 } 01373 01378 public function setSessionData( $key, $data ) { 01379 $this->session[$key] = $data; 01380 } 01381 01385 public function getSessionArray() { 01386 return $this->session; 01387 } 01388 01393 public function getRawQueryString() { 01394 return ''; 01395 } 01396 01401 public function getRawPostString() { 01402 return ''; 01403 } 01404 01409 public function getRawInput() { 01410 return ''; 01411 } 01412 01417 public function checkUrlExtension( $extWhitelist = array() ) { 01418 return true; 01419 } 01420 01424 protected function getRawIP() { 01425 return '127.0.0.1'; 01426 } 01427 } 01428 01437 class DerivativeRequest extends FauxRequest { 01438 private $base; 01439 01446 public function __construct( WebRequest $base, $data, $wasPosted = false ) { 01447 $this->base = $base; 01448 parent::__construct( $data, $wasPosted ); 01449 } 01450 01451 public function getCookie( $key, $prefix = null, $default = null ) { 01452 return $this->base->getCookie( $key, $prefix, $default ); 01453 } 01454 01455 public function checkSessionCookie() { 01456 return $this->base->checkSessionCookie(); 01457 } 01458 01459 public function getHeader( $name ) { 01460 return $this->base->getHeader( $name ); 01461 } 01462 01463 public function getAllHeaders() { 01464 return $this->base->getAllHeaders(); 01465 } 01466 01467 public function getSessionData( $key ) { 01468 return $this->base->getSessionData( $key ); 01469 } 01470 01471 public function setSessionData( $key, $data ) { 01472 $this->base->setSessionData( $key, $data ); 01473 } 01474 01475 public function getAcceptLang() { 01476 return $this->base->getAcceptLang(); 01477 } 01478 01479 public function getIP() { 01480 return $this->base->getIP(); 01481 } 01482 01483 public function getProtocol() { 01484 return $this->base->getProtocol(); 01485 } 01486 }