MediaWiki  master
WebRequest.php
Go to the documentation of this file.
1 <?php
31 
32 // The point of this class is to be a wrapper around super globals
33 // phpcs:disable MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals
34 
42 class WebRequest {
47  protected $data;
48 
55 
59  protected $queryParams;
60 
65  protected $headers = [];
66 
71  const GETHEADER_LIST = 1;
72 
77  private static $reqId;
78 
83  private $response;
84 
89  private $ip;
90 
95  protected $requestTime;
96 
101  protected $protocol;
102 
111  protected $sessionId = null;
112 
114  protected $markedAsSafe = false;
115 
119  public function __construct() {
120  $this->requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
121 
122  // POST overrides GET data
123  // We don't use $_REQUEST here to avoid interference from cookies...
124  $this->data = $_POST + $_GET;
125 
126  $this->queryAndPathParams = $this->queryParams = $_GET;
127  }
128 
145  public static function getPathInfo( $want = 'all' ) {
146  // PATH_INFO is mangled due to https://bugs.php.net/bug.php?id=31892
147  // And also by Apache 2.x, double slashes are converted to single slashes.
148  // So we will use REQUEST_URI if possible.
149  if ( isset( $_SERVER['REQUEST_URI'] ) ) {
150  // Slurp out the path portion to examine...
151  $url = $_SERVER['REQUEST_URI'];
152  if ( !preg_match( '!^https?://!', $url ) ) {
153  $url = 'http://unused' . $url;
154  }
155  AtEase::suppressWarnings();
156  $a = parse_url( $url );
157  AtEase::restoreWarnings();
158  if ( !$a ) {
159  return [];
160  }
161  $path = $a['path'] ?? '';
162 
163  global $wgScript;
164  if ( $path == $wgScript && $want !== 'all' ) {
165  // Script inside a rewrite path?
166  // Abort to keep from breaking...
167  return [];
168  }
169 
170  $router = new PathRouter;
171 
172  // Raw PATH_INFO style
173  $router->add( "$wgScript/$1" );
174 
175  if ( isset( $_SERVER['SCRIPT_NAME'] )
176  && strpos( $_SERVER['SCRIPT_NAME'], '.php' ) !== false
177  ) {
178  // Check for SCRIPT_NAME, we handle index.php explicitly
179  // But we do have some other .php files such as img_auth.php
180  // Don't let root article paths clober the parsing for them
181  $router->add( $_SERVER['SCRIPT_NAME'] . "/$1" );
182  }
183 
184  global $wgArticlePath;
185  if ( $wgArticlePath ) {
186  $router->validateRoute( $wgArticlePath, 'wgArticlePath' );
187  $router->add( $wgArticlePath );
188  }
189 
190  global $wgActionPaths;
191  $articlePaths = PathRouter::getActionPaths( $wgActionPaths, $wgArticlePath );
192  if ( $articlePaths ) {
193  $router->add( $articlePaths, [ 'action' => '$key' ] );
194  }
195 
196  global $wgVariantArticlePath;
197  if ( $wgVariantArticlePath ) {
198  $router->validateRoute( $wgVariantArticlePath, 'wgVariantArticlePath' );
199  $router->add( $wgVariantArticlePath,
200  [ 'variant' => '$2' ],
201  [ '$2' => MediaWikiServices::getInstance()->getContentLanguage()->
202  getVariants() ]
203  );
204  }
205 
206  Hooks::run( 'WebRequestPathInfoRouter', [ $router ] );
207 
208  $matches = $router->parse( $path );
209  } else {
210  global $wgUsePathInfo;
211  $matches = [];
212  if ( $wgUsePathInfo ) {
213  if ( !empty( $_SERVER['ORIG_PATH_INFO'] ) ) {
214  // Mangled PATH_INFO
215  // https://bugs.php.net/bug.php?id=31892
216  // Also reported when ini_get('cgi.fix_pathinfo')==false
217  $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 );
218  } elseif ( !empty( $_SERVER['PATH_INFO'] ) ) {
219  // Regular old PATH_INFO yay
220  $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 );
221  }
222  }
223  }
224 
225  return $matches;
226  }
227 
234  public static function detectServer() {
236 
237  $proto = self::detectProtocol();
238  $stdPort = $proto === 'https' ? 443 : 80;
239 
240  $varNames = [ 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ];
241  $host = 'localhost';
242  $port = $stdPort;
243  foreach ( $varNames as $varName ) {
244  if ( !isset( $_SERVER[$varName] ) ) {
245  continue;
246  }
247 
248  $parts = IP::splitHostAndPort( $_SERVER[$varName] );
249  if ( !$parts ) {
250  // Invalid, do not use
251  continue;
252  }
253 
254  $host = $parts[0];
255  if ( $wgAssumeProxiesUseDefaultProtocolPorts && isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
256  // T72021: Assume that upstream proxy is running on the default
257  // port based on the protocol. We have no reliable way to determine
258  // the actual port in use upstream.
259  $port = $stdPort;
260  } elseif ( $parts[1] === false ) {
261  if ( isset( $_SERVER['SERVER_PORT'] ) ) {
262  $port = $_SERVER['SERVER_PORT'];
263  } // else leave it as $stdPort
264  } else {
265  $port = $parts[1];
266  }
267  break;
268  }
269 
270  return $proto . '://' . IP::combineHostAndPort( $host, $port, $stdPort );
271  }
272 
280  public static function detectProtocol() {
281  if ( ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ||
282  ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
283  $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) ) {
284  return 'https';
285  } else {
286  return 'http';
287  }
288  }
289 
297  public function getElapsedTime() {
298  return microtime( true ) - $this->requestTime;
299  }
300 
309  public static function getRequestId() {
310  // This method is called from various error handlers and should be kept simple.
311 
312  if ( self::$reqId ) {
313  return self::$reqId;
314  }
315 
316  global $wgAllowExternalReqID;
317 
318  self::$reqId = $_SERVER['UNIQUE_ID'] ?? wfRandomString( 24 );
319  if ( $wgAllowExternalReqID ) {
320  $id = RequestContext::getMain()->getRequest()->getHeader( 'X-Request-Id' );
321  if ( $id ) {
322  self::$reqId = $id;
323  }
324  }
325 
326  return self::$reqId;
327  }
328 
336  public static function overrideRequestId( $id ) {
337  self::$reqId = $id;
338  }
339 
344  public function getProtocol() {
345  if ( $this->protocol === null ) {
346  $this->protocol = self::detectProtocol();
347  }
348  return $this->protocol;
349  }
350 
358  public function interpolateTitle() {
359  // T18019: title interpolation on API queries is useless and sometimes harmful
360  if ( defined( 'MW_API' ) ) {
361  return;
362  }
363 
364  $matches = self::getPathInfo( 'title' );
365  foreach ( $matches as $key => $val ) {
366  $this->data[$key] = $this->queryAndPathParams[$key] = $val;
367  }
368  }
369 
380  static function extractTitle( $path, $bases, $key = false ) {
381  foreach ( (array)$bases as $keyValue => $base ) {
382  // Find the part after $wgArticlePath
383  $base = str_replace( '$1', '', $base );
384  $baseLen = strlen( $base );
385  if ( substr( $path, 0, $baseLen ) == $base ) {
386  $raw = substr( $path, $baseLen );
387  if ( $raw !== '' ) {
388  $matches = [ 'title' => rawurldecode( $raw ) ];
389  if ( $key ) {
390  $matches[$key] = $keyValue;
391  }
392  return $matches;
393  }
394  }
395  }
396  return [];
397  }
398 
406  public function normalizeUnicode( $data ) {
407  if ( is_array( $data ) ) {
408  foreach ( $data as $key => $val ) {
409  $data[$key] = $this->normalizeUnicode( $val );
410  }
411  } else {
412  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
413  $data = $contLang ? $contLang->normalize( $data ) :
414  UtfNormal\Validator::cleanUp( $data );
415  }
416  return $data;
417  }
418 
427  private function getGPCVal( $arr, $name, $default ) {
428  # PHP is so nice to not touch input data, except sometimes:
429  # https://www.php.net/variables.external#language.variables.external.dot-in-names
430  # Work around PHP *feature* to avoid *bugs* elsewhere.
431  $name = strtr( $name, '.', '_' );
432 
433  if ( !isset( $arr[$name] ) ) {
434  return $default;
435  }
436 
437  $data = $arr[$name];
438  # Optimisation: Skip UTF-8 normalization and legacy transcoding for simple ASCII strings.
439  $isAsciiStr = ( is_string( $data ) && preg_match( '/[^\x20-\x7E]/', $data ) === 0 );
440  if ( !$isAsciiStr ) {
441  if ( isset( $_GET[$name] ) && is_string( $data ) ) {
442  # Check for alternate/legacy character encoding.
443  $data = MediaWikiServices::getInstance()
444  ->getContentLanguage()
445  ->checkTitleEncoding( $data );
446  }
447  $data = $this->normalizeUnicode( $data );
448  }
449 
450  return $data;
451  }
452 
465  public function getRawVal( $name, $default = null ) {
466  $name = strtr( $name, '.', '_' ); // See comment in self::getGPCVal()
467  if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
468  $val = $this->data[$name];
469  } else {
470  $val = $default;
471  }
472  if ( is_null( $val ) ) {
473  return $val;
474  } else {
475  return (string)$val;
476  }
477  }
478 
489  public function getVal( $name, $default = null ) {
490  $val = $this->getGPCVal( $this->data, $name, $default );
491  if ( is_array( $val ) ) {
492  $val = $default;
493  }
494  if ( is_null( $val ) ) {
495  return $val;
496  } else {
497  return (string)$val;
498  }
499  }
500 
508  public function setVal( $key, $value ) {
509  $ret = $this->data[$key] ?? null;
510  $this->data[$key] = $value;
511  return $ret;
512  }
513 
520  public function unsetVal( $key ) {
521  if ( !isset( $this->data[$key] ) ) {
522  $ret = null;
523  } else {
524  $ret = $this->data[$key];
525  unset( $this->data[$key] );
526  }
527  return $ret;
528  }
529 
539  public function getArray( $name, $default = null ) {
540  $val = $this->getGPCVal( $this->data, $name, $default );
541  if ( is_null( $val ) ) {
542  return null;
543  } else {
544  return (array)$val;
545  }
546  }
547 
558  public function getIntArray( $name, $default = null ) {
559  $val = $this->getArray( $name, $default );
560  if ( is_array( $val ) ) {
561  $val = array_map( 'intval', $val );
562  }
563  return $val;
564  }
565 
575  public function getInt( $name, $default = 0 ) {
576  return intval( $this->getRawVal( $name, $default ) );
577  }
578 
587  public function getIntOrNull( $name ) {
588  $val = $this->getRawVal( $name );
589  return is_numeric( $val )
590  ? intval( $val )
591  : null;
592  }
593 
604  public function getFloat( $name, $default = 0.0 ) {
605  return floatval( $this->getRawVal( $name, $default ) );
606  }
607 
617  public function getBool( $name, $default = false ) {
618  return (bool)$this->getRawVal( $name, $default );
619  }
620 
630  public function getFuzzyBool( $name, $default = false ) {
631  return $this->getBool( $name, $default )
632  && strcasecmp( $this->getRawVal( $name ), 'false' ) !== 0;
633  }
634 
643  public function getCheck( $name ) {
644  # Checkboxes and buttons are only present when clicked
645  # Presence connotes truth, absence false
646  return $this->getRawVal( $name, null ) !== null;
647  }
648 
659  public function getText( $name, $default = '' ) {
660  $val = $this->getVal( $name, $default );
661  return str_replace( "\r\n", "\n", $val );
662  }
663 
671  public function getValues() {
672  $names = func_get_args();
673  if ( count( $names ) == 0 ) {
674  $names = array_keys( $this->data );
675  }
676 
677  $retVal = [];
678  foreach ( $names as $name ) {
679  $value = $this->getGPCVal( $this->data, $name, null );
680  if ( !is_null( $value ) ) {
681  $retVal[$name] = $value;
682  }
683  }
684  return $retVal;
685  }
686 
693  public function getValueNames( $exclude = [] ) {
694  return array_diff( array_keys( $this->getValues() ), $exclude );
695  }
696 
704  public function getQueryValues() {
706  }
707 
717  public function getQueryValuesOnly() {
718  return $this->queryParams;
719  }
720 
729  public function getPostValues() {
730  return $_POST;
731  }
732 
740  public function getRawQueryString() {
741  return $_SERVER['QUERY_STRING'];
742  }
743 
750  public function getRawPostString() {
751  if ( !$this->wasPosted() ) {
752  return '';
753  }
754  return $this->getRawInput();
755  }
756 
764  public function getRawInput() {
765  static $input = null;
766  if ( $input === null ) {
767  $input = file_get_contents( 'php://input' );
768  }
769  return $input;
770  }
771 
777  public function getMethod() {
778  return $_SERVER['REQUEST_METHOD'] ?? 'GET';
779  }
780 
790  public function wasPosted() {
791  return $this->getMethod() == 'POST';
792  }
793 
804  public function getSession() {
805  if ( $this->sessionId !== null ) {
806  $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
807  if ( $session ) {
808  return $session;
809  }
810  }
811 
812  $session = SessionManager::singleton()->getSessionForRequest( $this );
813  $this->sessionId = $session->getSessionId();
814  return $session;
815  }
816 
823  public function setSessionId( SessionId $sessionId ) {
824  $this->sessionId = $sessionId;
825  }
826 
833  public function getSessionId() {
834  return $this->sessionId;
835  }
836 
845  public function getCookie( $key, $prefix = null, $default = null ) {
846  if ( $prefix === null ) {
847  global $wgCookiePrefix;
848  $prefix = $wgCookiePrefix;
849  }
850  return $this->getGPCVal( $_COOKIE, $prefix . $key, $default );
851  }
852 
860  public static function getGlobalRequestURL() {
861  // This method is called on fatal errors; it should not depend on anything complex.
862 
863  if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
864  $base = $_SERVER['REQUEST_URI'];
865  } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] )
866  && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] )
867  ) {
868  // Probably IIS; doesn't set REQUEST_URI
869  $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
870  } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
871  $base = $_SERVER['SCRIPT_NAME'];
872  if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
873  $base .= '?' . $_SERVER['QUERY_STRING'];
874  }
875  } else {
876  // This shouldn't happen!
877  throw new MWException( "Web server doesn't provide either " .
878  "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
879  "of your web server configuration to https://phabricator.wikimedia.org/" );
880  }
881  // User-agents should not send a fragment with the URI, but
882  // if they do, and the web server passes it on to us, we
883  // need to strip it or we get false-positive redirect loops
884  // or weird output URLs
885  $hash = strpos( $base, '#' );
886  if ( $hash !== false ) {
887  $base = substr( $base, 0, $hash );
888  }
889 
890  if ( $base[0] == '/' ) {
891  // More than one slash will look like it is protocol relative
892  return preg_replace( '!^/+!', '/', $base );
893  } else {
894  // We may get paths with a host prepended; strip it.
895  return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base );
896  }
897  }
898 
906  public function getRequestURL() {
907  return self::getGlobalRequestURL();
908  }
909 
920  public function getFullRequestURL() {
921  // Pass an explicit PROTO constant instead of PROTO_CURRENT so that we
922  // do not rely on state from the global $wgRequest object (which it would,
923  // via wfGetServerUrl/wfExpandUrl/$wgRequest->protocol).
924  if ( $this->getProtocol() === 'http' ) {
925  return wfGetServerUrl( PROTO_HTTP ) . $this->getRequestURL();
926  } else {
927  return wfGetServerUrl( PROTO_HTTPS ) . $this->getRequestURL();
928  }
929  }
930 
936  public function appendQueryValue( $key, $value ) {
937  return $this->appendQueryArray( [ $key => $value ] );
938  }
939 
946  public function appendQueryArray( $array ) {
947  $newquery = $this->getQueryValues();
948  unset( $newquery['title'] );
949  $newquery = array_merge( $newquery, $array );
950 
951  return wfArrayToCgi( $newquery );
952  }
953 
963  public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
964  global $wgUser;
965 
966  $limit = $this->getInt( 'limit', 0 );
967  if ( $limit < 0 ) {
968  $limit = 0;
969  }
970  if ( ( $limit == 0 ) && ( $optionname != '' ) ) {
971  $limit = $wgUser->getIntOption( $optionname );
972  }
973  if ( $limit <= 0 ) {
974  $limit = $deflimit;
975  }
976  if ( $limit > 5000 ) {
977  $limit = 5000; # We have *some* limits...
978  }
979 
980  $offset = $this->getInt( 'offset', 0 );
981  if ( $offset < 0 ) {
982  $offset = 0;
983  }
984 
985  return [ $limit, $offset ];
986  }
987 
994  public function getFileTempname( $key ) {
995  $file = new WebRequestUpload( $this, $key );
996  return $file->getTempName();
997  }
998 
1005  public function getUploadError( $key ) {
1006  $file = new WebRequestUpload( $this, $key );
1007  return $file->getError();
1008  }
1009 
1021  public function getFileName( $key ) {
1022  $file = new WebRequestUpload( $this, $key );
1023  return $file->getName();
1024  }
1025 
1032  public function getUpload( $key ) {
1033  return new WebRequestUpload( $this, $key );
1034  }
1035 
1042  public function response() {
1043  /* Lazy initialization of response object for this request */
1044  if ( !is_object( $this->response ) ) {
1045  $class = ( $this instanceof FauxRequest ) ? FauxResponse::class : WebResponse::class;
1046  $this->response = new $class();
1047  }
1048  return $this->response;
1049  }
1050 
1054  protected function initHeaders() {
1055  if ( count( $this->headers ) ) {
1056  return;
1057  }
1058 
1059  $apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false;
1060  if ( $apacheHeaders ) {
1061  foreach ( $apacheHeaders as $tempName => $tempValue ) {
1062  $this->headers[strtoupper( $tempName )] = $tempValue;
1063  }
1064  } else {
1065  foreach ( $_SERVER as $name => $value ) {
1066  if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
1067  $name = str_replace( '_', '-', substr( $name, 5 ) );
1068  $this->headers[$name] = $value;
1069  } elseif ( $name === 'CONTENT_LENGTH' ) {
1070  $this->headers['CONTENT-LENGTH'] = $value;
1071  }
1072  }
1073  }
1074  }
1075 
1081  public function getAllHeaders() {
1082  $this->initHeaders();
1083  return $this->headers;
1084  }
1085 
1098  public function getHeader( $name, $flags = 0 ) {
1099  $this->initHeaders();
1100  $name = strtoupper( $name );
1101  if ( !isset( $this->headers[$name] ) ) {
1102  return false;
1103  }
1104  $value = $this->headers[$name];
1105  if ( $flags & self::GETHEADER_LIST ) {
1106  $value = array_map( 'trim', explode( ',', $value ) );
1107  }
1108  return $value;
1109  }
1110 
1118  public function getSessionData( $key ) {
1119  return $this->getSession()->get( $key );
1120  }
1121 
1129  public function setSessionData( $key, $data ) {
1130  $this->getSession()->set( $key, $data );
1131  }
1132 
1142  public function checkUrlExtension( $extWhitelist = [] ) {
1143  wfDeprecated( __METHOD__, '1.35' );
1144  return true;
1145  }
1146 
1156  public function getAcceptLang() {
1157  // Modified version of code found at
1158  // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1159  $acceptLang = $this->getHeader( 'Accept-Language' );
1160  if ( !$acceptLang ) {
1161  return [];
1162  }
1163 
1164  // Return the language codes in lower case
1165  $acceptLang = strtolower( $acceptLang );
1166 
1167  // Break up string into pieces (languages and q factors)
1168  $lang_parse = null;
1169  preg_match_all(
1170  '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/',
1171  $acceptLang,
1172  $lang_parse
1173  );
1174 
1175  if ( !count( $lang_parse[1] ) ) {
1176  return [];
1177  }
1178 
1179  $langcodes = $lang_parse[1];
1180  $qvalues = $lang_parse[4];
1181  $indices = range( 0, count( $lang_parse[1] ) - 1 );
1182 
1183  // Set default q factor to 1
1184  foreach ( $indices as $index ) {
1185  if ( $qvalues[$index] === '' ) {
1186  $qvalues[$index] = 1;
1187  } elseif ( $qvalues[$index] == 0 ) {
1188  unset( $langcodes[$index], $qvalues[$index], $indices[$index] );
1189  }
1190  }
1191 
1192  // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way
1193  array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes );
1194 
1195  // Create a list like "en" => 0.8
1196  $langs = array_combine( $langcodes, $qvalues );
1197 
1198  return $langs;
1199  }
1200 
1209  protected function getRawIP() {
1210  if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) {
1211  return null;
1212  }
1213 
1214  if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) {
1215  throw new MWException( __METHOD__
1216  . " : Could not determine the remote IP address due to multiple values." );
1217  } else {
1218  $ipchain = $_SERVER['REMOTE_ADDR'];
1219  }
1220 
1221  return IP::canonicalize( $ipchain );
1222  }
1223 
1233  public function getIP() {
1234  global $wgUsePrivateIPs;
1235 
1236  # Return cached result
1237  if ( $this->ip !== null ) {
1238  return $this->ip;
1239  }
1240 
1241  # collect the originating ips
1242  $ip = $this->getRawIP();
1243  if ( !$ip ) {
1244  throw new MWException( 'Unable to determine IP.' );
1245  }
1246 
1247  # Append XFF
1248  $forwardedFor = $this->getHeader( 'X-Forwarded-For' );
1249  if ( $forwardedFor !== false ) {
1250  $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
1251  $isConfigured = $proxyLookup->isConfiguredProxy( $ip );
1252  $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) );
1253  $ipchain = array_reverse( $ipchain );
1254  array_unshift( $ipchain, $ip );
1255 
1256  # Step through XFF list and find the last address in the list which is a
1257  # trusted server. Set $ip to the IP address given by that trusted server,
1258  # unless the address is not sensible (e.g. private). However, prefer private
1259  # IP addresses over proxy servers controlled by this site (more sensible).
1260  # Note that some XFF values might be "unknown" with Squid/Varnish.
1261  foreach ( $ipchain as $i => $curIP ) {
1262  $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) );
1263  if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown'
1264  || !$proxyLookup->isTrustedProxy( $curIP )
1265  ) {
1266  break; // IP is not valid/trusted or does not point to anything
1267  }
1268  if (
1269  IP::isPublic( $ipchain[$i + 1] ) ||
1270  $wgUsePrivateIPs ||
1271  $proxyLookup->isConfiguredProxy( $curIP ) // T50919; treat IP as sane
1272  ) {
1273  // Follow the next IP according to the proxy
1274  $nextIP = IP::canonicalize( $ipchain[$i + 1] );
1275  if ( !$nextIP && $isConfigured ) {
1276  // We have not yet made it past CDN/proxy servers of this site,
1277  // so either they are misconfigured or there is some IP spoofing.
1278  throw new MWException( "Invalid IP given in XFF '$forwardedFor'." );
1279  }
1280  $ip = $nextIP;
1281  // keep traversing the chain
1282  continue;
1283  }
1284  break;
1285  }
1286  }
1287 
1288  # Allow extensions to improve our guess
1289  Hooks::run( 'GetIP', [ &$ip ] );
1290 
1291  if ( !$ip ) {
1292  throw new MWException( "Unable to determine IP." );
1293  }
1294 
1295  wfDebug( "IP: $ip\n" );
1296  $this->ip = $ip;
1297  return $ip;
1298  }
1299 
1305  public function setIP( $ip ) {
1306  $this->ip = $ip;
1307  }
1308 
1321  public function hasSafeMethod() {
1322  if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
1323  return false; // CLI mode
1324  }
1325 
1326  return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
1327  }
1328 
1347  public function isSafeRequest() {
1348  if ( $this->markedAsSafe && $this->wasPosted() ) {
1349  return true; // marked as a "safe" POST
1350  }
1351 
1352  return $this->hasSafeMethod();
1353  }
1354 
1365  public function markAsSafeRequest() {
1366  $this->markedAsSafe = true;
1367  }
1368 }
getRawVal( $name, $default=null)
Fetch a scalar from the input without normalization, or return $default if it&#39;s not set...
Definition: WebRequest.php:465
checkUrlExtension( $extWhitelist=[])
This function formerly did a security check to prevent an XSS vulnerability in IE6, as documented in T30235.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
getPostValues()
Get the values passed via POST.
Definition: WebRequest.php:729
float $requestTime
The timestamp of the start of the request, with microsecond precision.
Definition: WebRequest.php:95
getInt( $name, $default=0)
Fetch an integer value from the input or return $default if not set.
Definition: WebRequest.php:575
getRawPostString()
Return the contents of the POST with no decoding.
Definition: WebRequest.php:750
$wgScript
The URL path to index.php.
static getRequestId()
Get the unique request ID.
Definition: WebRequest.php:309
getHeader( $name, $flags=0)
Get a request header, or false if it isn&#39;t set.
getIP()
Work out the IP address based on various globals For trusted proxies, use the XFF client IP (first of...
setSessionData( $key, $data)
Set session data.
getFullRequestURL()
Return the request URI with the canonical service and hostname, path, and query string.
Definition: WebRequest.php:920
getVal( $name, $default=null)
Fetch a scalar from the input or return $default if it&#39;s not set.
Definition: WebRequest.php:489
$wgCookiePrefix
Cookies generated by MediaWiki have names starting with this prefix.
$wgActionPaths
Definition: img_auth.php:48
setVal( $key, $value)
Set an arbitrary value into our get/post data.
Definition: WebRequest.php:508
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff...
unsetVal( $key)
Unset an arbitrary value from our get/post data.
Definition: WebRequest.php:520
string $protocol
Cached URL protocol.
Definition: WebRequest.php:101
static canonicalize( $addr)
Convert some unusual representations of IPv4 addresses to their canonical dotted quad representation...
Definition: IP.php:674
static combineHostAndPort( $host, $port, $defaultPort=false)
Given a host name and a port, combine them into host/port string like you might find in a URL...
Definition: IP.php:302
$wgAllowExternalReqID
Whether to respect/honour the request ID provided by the incoming request via the X-Request-Id header...
getFileName( $key)
Return the original filename of the uploaded file, as reported by the submitting user agent...
const GETHEADER_LIST
Flag to make WebRequest::getHeader return an array of values.
Definition: WebRequest.php:71
getSession()
Return the session for this request.
Definition: WebRequest.php:804
getFloat( $name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
Definition: WebRequest.php:604
wfGetServerUrl( $proto)
Get the wiki&#39;s "server", i.e.
$wgArticlePath
Definition: img_auth.php:47
initHeaders()
Initialise the header list.
string $ip
Cached client IP address.
Definition: WebRequest.php:89
normalizeUnicode( $data)
Recursively normalizes UTF-8 strings in the given array.
Definition: WebRequest.php:406
getBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:617
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
Definition: WebRequest.php:643
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e.g.
getAllHeaders()
Get an array containing all request headers.
static extractTitle( $path, $bases, $key=false)
URL rewriting function; tries to extract page title and, optionally, one other fixed parameter value ...
Definition: WebRequest.php:380
static detectServer()
Work out an appropriate URL prefix containing scheme and host, based on information detected from $_S...
Definition: WebRequest.php:234
getLimitOffset( $deflimit=50, $optionname='rclimit')
Check for limit and offset parameters on the input, and return sensible defaults if not given...
Definition: WebRequest.php:963
const PROTO_HTTPS
Definition: Defines.php:200
static getMain()
Get the RequestContext object associated with the main request.
getSessionData( $key)
Get data from the session.
static sanitizeIP( $ip)
Convert an IP into a verbose, uppercase, normalized form.
Definition: IP.php:139
getProtocol()
Get the current URL protocol (http or https)
Definition: WebRequest.php:344
hasSafeMethod()
Check if this request uses a "safe" HTTP method.
wfRandomString( $length=32)
Get a random string containing a number of pseudo-random hex characters.
wasPosted()
Returns true if the present request was reached by a POST operation, false otherwise (GET...
Definition: WebRequest.php:790
getRequestURL()
Return the path and query string portion of the request URI.
Definition: WebRequest.php:906
PathRouter class.
Definition: PathRouter.php:73
bool $markedAsSafe
Whether this HTTP request is "safe" (even if it is an HTTP post)
Definition: WebRequest.php:114
getRawIP()
Fetch the raw IP from the request.
getQueryValues()
Get the values passed in the query string and the path router parameters.
Definition: WebRequest.php:704
static getActionPaths(array $actionPaths, $articlePath)
Definition: PathRouter.php:430
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
array $queryAndPathParams
The parameters from $_GET.
Definition: WebRequest.php:54
appendQueryArray( $array)
Appends or replaces value of query variables.
Definition: WebRequest.php:946
array $headers
Lazy-initialized request headers indexed by upper-case header name.
Definition: WebRequest.php:65
$wgUsePathInfo
Whether to support URLs like index.php/Page_title These often break when PHP is set up in CGI mode...
isSafeRequest()
Whether this request should be identified as being "safe".
bool $wgAssumeProxiesUseDefaultProtocolPorts
When the wiki is running behind a proxy and this is set to true, assumes that the proxy exposes the w...
static splitHostAndPort( $both)
Given a host/port string, like one might find in the host part of a URL per RFC 2732, split the hostname part and the port part and return an array with an element for each.
Definition: IP.php:253
getFuzzyBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:630
static detectProtocol()
Detect the protocol from $_SERVER.
Definition: WebRequest.php:280
getGPCVal( $arr, $name, $default)
Fetch a value from the given array or return $default if it&#39;s not set.
Definition: WebRequest.php:427
getSessionId()
Get the session id for this request, if any.
Definition: WebRequest.php:833
getUpload( $key)
Return a WebRequestUpload object corresponding to the key.
const PROTO_HTTP
Definition: Defines.php:199
getValues()
Extracts the given named values into an array.
Definition: WebRequest.php:671
setSessionId(SessionId $sessionId)
Set the session for this request.
Definition: WebRequest.php:823
WebResponse $response
Lazy-init response object.
Definition: WebRequest.php:83
SessionId null $sessionId
Session ID to use for this request.
Definition: WebRequest.php:111
static getGlobalRequestURL()
Return the path and query string portion of the main request URI.
Definition: WebRequest.php:860
array $data
The parameters from $_GET, $_POST and the path router.
Definition: WebRequest.php:47
getText( $name, $default='')
Fetch a text string from the given array or return $default if it&#39;s not set.
Definition: WebRequest.php:659
getValueNames( $exclude=[])
Returns the names of all input values excluding those in $exclude.
Definition: WebRequest.php:693
getMethod()
Get the HTTP method used for this request.
Definition: WebRequest.php:777
static overrideRequestId( $id)
Override the unique request ID.
Definition: WebRequest.php:336
Value object holding the session ID in a manner that can be globally updated.
Definition: SessionId.php:38
markAsSafeRequest()
Mark this request as identified as being nullipotent even if it is a POST request.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
$queryParams
The parameters from $_GET only.
Definition: WebRequest.php:59
getRawInput()
Return the raw request body, with no processing.
Definition: WebRequest.php:764
interpolateTitle()
Check for title, action, and/or variant data in the URL and interpolate it into the GET variables...
Definition: WebRequest.php:358
getAcceptLang()
Parse the Accept-Language header sent by the client into an array.
getArray( $name, $default=null)
Fetch an array from the input or return $default if it&#39;s not set.
Definition: WebRequest.php:539
getQueryValuesOnly()
Get the values passed in the query string only, not including the path router parameters.
Definition: WebRequest.php:717
getIntOrNull( $name)
Fetch an integer value from the input or return null if empty.
Definition: WebRequest.php:587
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
Definition: WebRequest.php:845
static string $reqId
The unique request ID.
Definition: WebRequest.php:77
getUploadError( $key)
Return the upload error or 0.
static getPathInfo( $want='all')
Extract relevant query arguments from the http request uri&#39;s path to be merged with the normal php pr...
Definition: WebRequest.php:145
getIntArray( $name, $default=null)
Fetch an array of integers, or return $default if it&#39;s not set.
Definition: WebRequest.php:558
getElapsedTime()
Get the number of seconds to have elapsed since request start, in fractional seconds, with microsecond resolution.
Definition: WebRequest.php:297
add( $path, $params=[], $options=[])
Add a new path pattern to the path router.
Definition: PathRouter.php:158
$wgVariantArticlePath
Like $wgArticlePath, but on multi-variant wikis, this provides a path format that describes which par...
Object to access the $_FILES array.
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
getRawQueryString()
Return the contents of the Query with no decoding.
Definition: WebRequest.php:740
getFileTempname( $key)
Return the path to the temporary file where PHP has stored the upload.
Definition: WebRequest.php:994
static isPublic( $ip)
Determine if an IP address really is an IP address, and if it is public, i.e.
Definition: IP.php:375
$wgUsePrivateIPs
Should forwarded Private IPs be accepted?
$matches
appendQueryValue( $key, $value)
Definition: WebRequest.php:936