MediaWiki  master
WebRequest.php
Go to the documentation of this file.
1 <?php
30 use Wikimedia\AtEase\AtEase;
31 use Wikimedia\IPUtils;
32 
33 // The point of this class is to be a wrapper around super globals
34 // phpcs:disable MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals
35 
43 class WebRequest {
48  protected $data;
49 
56 
60  protected $queryParams;
61 
66  protected $headers = [];
67 
72  public const GETHEADER_LIST = 1;
73 
78  private static $reqId;
79 
84  private $response;
85 
90  private $ip;
91 
96  protected $requestTime;
97 
102  protected $protocol;
103 
112  protected $sessionId = null;
113 
115  protected $markedAsSafe = false;
116 
120  public function __construct() {
121  $this->requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
122 
123  // POST overrides GET data
124  // We don't use $_REQUEST here to avoid interference from cookies...
125  $this->data = $_POST + $_GET;
126 
127  $this->queryAndPathParams = $this->queryParams = $_GET;
128  }
129 
149  public static function getPathInfo( $want = 'all' ) {
150  // PATH_INFO is mangled due to https://bugs.php.net/bug.php?id=31892
151  // And also by Apache 2.x, double slashes are converted to single slashes.
152  // So we will use REQUEST_URI if possible.
153  if ( isset( $_SERVER['REQUEST_URI'] ) ) {
154  // Slurp out the path portion to examine...
155  $url = $_SERVER['REQUEST_URI'];
156  if ( !preg_match( '!^https?://!', $url ) ) {
157  $url = 'http://unused' . $url;
158  }
159  AtEase::suppressWarnings();
160  $a = parse_url( $url );
161  AtEase::restoreWarnings();
162  if ( !$a ) {
163  return [];
164  }
165  $path = $a['path'] ?? '';
166 
167  global $wgScript;
168  if ( $path == $wgScript && $want !== 'all' ) {
169  // Script inside a rewrite path?
170  // Abort to keep from breaking...
171  return [];
172  }
173 
174  $router = new PathRouter;
175 
176  // Raw PATH_INFO style
177  $router->add( "$wgScript/$1" );
178 
179  global $wgArticlePath;
180  if ( $wgArticlePath ) {
181  $router->validateRoute( $wgArticlePath, 'wgArticlePath' );
182  $router->add( $wgArticlePath );
183  }
184 
185  global $wgActionPaths;
187  if ( $articlePaths ) {
188  $router->add( $articlePaths, [ 'action' => '$key' ] );
189  }
190 
191  global $wgVariantArticlePath;
192  if ( $wgVariantArticlePath ) {
193  $router->validateRoute( $wgVariantArticlePath, 'wgVariantArticlePath' );
194  $router->add( $wgVariantArticlePath,
195  [ 'variant' => '$2' ],
196  [ '$2' => MediaWikiServices::getInstance()->getContentLanguage()->
197  getVariants() ]
198  );
199  }
200 
201  Hooks::runner()->onWebRequestPathInfoRouter( $router );
202 
203  $matches = $router->parse( $path );
204  } else {
205  global $wgUsePathInfo;
206  $matches = [];
207  if ( $wgUsePathInfo ) {
208  if ( !empty( $_SERVER['ORIG_PATH_INFO'] ) ) {
209  // Mangled PATH_INFO
210  // https://bugs.php.net/bug.php?id=31892
211  // Also reported when ini_get('cgi.fix_pathinfo')==false
212  $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 );
213  } elseif ( !empty( $_SERVER['PATH_INFO'] ) ) {
214  // Regular old PATH_INFO yay
215  $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 );
216  }
217  }
218  }
219 
220  return $matches;
221  }
222 
234  public static function getRequestPathSuffix( $basePath ) {
235  $basePath = rtrim( $basePath, '/' ) . '/';
236  $requestUrl = self::getGlobalRequestURL();
237  $qpos = strpos( $requestUrl, '?' );
238  if ( $qpos !== false ) {
239  $requestPath = substr( $requestUrl, 0, $qpos );
240  } else {
241  $requestPath = $requestUrl;
242  }
243  if ( substr( $requestPath, 0, strlen( $basePath ) ) !== $basePath ) {
244  return false;
245  }
246  return rawurldecode( substr( $requestPath, strlen( $basePath ) ) );
247  }
248 
255  public static function detectServer() {
257 
258  $proto = self::detectProtocol();
259  $stdPort = $proto === 'https' ? 443 : 80;
260 
261  $varNames = [ 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ];
262  $host = 'localhost';
263  $port = $stdPort;
264  foreach ( $varNames as $varName ) {
265  if ( !isset( $_SERVER[$varName] ) ) {
266  continue;
267  }
268 
269  $parts = IPUtils::splitHostAndPort( $_SERVER[$varName] );
270  if ( !$parts ) {
271  // Invalid, do not use
272  continue;
273  }
274 
275  $host = $parts[0];
276  if ( $wgAssumeProxiesUseDefaultProtocolPorts && isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
277  // T72021: Assume that upstream proxy is running on the default
278  // port based on the protocol. We have no reliable way to determine
279  // the actual port in use upstream.
280  $port = $stdPort;
281  } elseif ( $parts[1] === false ) {
282  if ( isset( $_SERVER['SERVER_PORT'] ) ) {
283  $port = $_SERVER['SERVER_PORT'];
284  } // else leave it as $stdPort
285  } else {
286  $port = $parts[1];
287  }
288  break;
289  }
290 
291  return $proto . '://' . IPUtils::combineHostAndPort( $host, $port, $stdPort );
292  }
293 
301  public static function detectProtocol() {
302  if ( ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ||
303  ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
304  $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) ) {
305  return 'https';
306  } else {
307  return 'http';
308  }
309  }
310 
318  public function getElapsedTime() {
319  return microtime( true ) - $this->requestTime;
320  }
321 
330  public static function getRequestId() {
331  // This method is called from various error handlers and should be kept simple.
332 
333  if ( !self::$reqId ) {
334  global $wgAllowExternalReqID;
336  ? RequestContext::getMain()->getRequest()->getHeader( 'X-Request-Id' )
337  : null;
338  if ( !$id ) {
339  $id = $_SERVER['UNIQUE_ID'] ?? wfRandomString( 24 );
340  }
341  self::$reqId = $id;
342  }
343 
344  return self::$reqId;
345  }
346 
354  public static function overrideRequestId( $id ) {
355  self::$reqId = $id;
356  }
357 
362  public function getProtocol() {
363  if ( $this->protocol === null ) {
364  $this->protocol = self::detectProtocol();
365  }
366  return $this->protocol;
367  }
368 
376  public function interpolateTitle() {
377  // T18019: title interpolation on API queries is useless and sometimes harmful
378  if ( defined( 'MW_API' ) ) {
379  return;
380  }
381 
382  $matches = self::getPathInfo( 'title' );
383  foreach ( $matches as $key => $val ) {
384  $this->data[$key] = $this->queryAndPathParams[$key] = $val;
385  }
386  }
387 
398  public static function extractTitle( $path, $bases, $key = false ) {
399  foreach ( (array)$bases as $keyValue => $base ) {
400  // Find the part after $wgArticlePath
401  $base = str_replace( '$1', '', $base );
402  $baseLen = strlen( $base );
403  if ( substr( $path, 0, $baseLen ) == $base ) {
404  $raw = substr( $path, $baseLen );
405  if ( $raw !== '' ) {
406  $matches = [ 'title' => rawurldecode( $raw ) ];
407  if ( $key ) {
408  $matches[$key] = $keyValue;
409  }
410  return $matches;
411  }
412  }
413  }
414  return [];
415  }
416 
424  public function normalizeUnicode( $data ) {
425  if ( is_array( $data ) ) {
426  foreach ( $data as $key => $val ) {
427  $data[$key] = $this->normalizeUnicode( $val );
428  }
429  } else {
430  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
431  $data = $contLang ? $contLang->normalize( $data ) :
432  UtfNormal\Validator::cleanUp( $data );
433  }
434  return $data;
435  }
436 
445  private function getGPCVal( $arr, $name, $default ) {
446  # PHP is so nice to not touch input data, except sometimes:
447  # https://www.php.net/variables.external#language.variables.external.dot-in-names
448  # Work around PHP *feature* to avoid *bugs* elsewhere.
449  $name = strtr( $name, '.', '_' );
450 
451  if ( !isset( $arr[$name] ) ) {
452  return $default;
453  }
454 
455  $data = $arr[$name];
456  # Optimisation: Skip UTF-8 normalization and legacy transcoding for simple ASCII strings.
457  $isAsciiStr = ( is_string( $data ) && preg_match( '/[^\x20-\x7E]/', $data ) === 0 );
458  if ( !$isAsciiStr ) {
459  if ( isset( $_GET[$name] ) && is_string( $data ) ) {
460  # Check for alternate/legacy character encoding.
461  $data = MediaWikiServices::getInstance()
462  ->getContentLanguage()
463  ->checkTitleEncoding( $data );
464  }
465  $data = $this->normalizeUnicode( $data );
466  }
467 
468  return $data;
469  }
470 
483  public function getRawVal( $name, $default = null ) {
484  $name = strtr( $name, '.', '_' ); // See comment in self::getGPCVal()
485  if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
486  $val = $this->data[$name];
487  } else {
488  $val = $default;
489  }
490  if ( $val === null ) {
491  return $val;
492  } else {
493  return (string)$val;
494  }
495  }
496 
507  public function getVal( $name, $default = null ) {
508  $val = $this->getGPCVal( $this->data, $name, $default );
509  if ( is_array( $val ) ) {
510  $val = $default;
511  }
512  if ( $val === null ) {
513  return $val;
514  } else {
515  return (string)$val;
516  }
517  }
518 
526  public function setVal( $key, $value ) {
527  $ret = $this->data[$key] ?? null;
528  $this->data[$key] = $value;
529  return $ret;
530  }
531 
538  public function unsetVal( $key ) {
539  if ( !isset( $this->data[$key] ) ) {
540  $ret = null;
541  } else {
542  $ret = $this->data[$key];
543  unset( $this->data[$key] );
544  }
545  return $ret;
546  }
547 
557  public function getArray( $name, $default = null ) {
558  $val = $this->getGPCVal( $this->data, $name, $default );
559  if ( $val === null ) {
560  return null;
561  } else {
562  return (array)$val;
563  }
564  }
565 
576  public function getIntArray( $name, $default = null ) {
577  $val = $this->getArray( $name, $default );
578  if ( is_array( $val ) ) {
579  $val = array_map( 'intval', $val );
580  }
581  return $val;
582  }
583 
593  public function getInt( $name, $default = 0 ) {
594  return intval( $this->getRawVal( $name, $default ) );
595  }
596 
605  public function getIntOrNull( $name ) {
606  $val = $this->getRawVal( $name );
607  return is_numeric( $val )
608  ? intval( $val )
609  : null;
610  }
611 
622  public function getFloat( $name, $default = 0.0 ) {
623  return floatval( $this->getRawVal( $name, $default ) );
624  }
625 
635  public function getBool( $name, $default = false ) {
636  return (bool)$this->getRawVal( $name, $default );
637  }
638 
648  public function getFuzzyBool( $name, $default = false ) {
649  return $this->getBool( $name, $default )
650  && strcasecmp( $this->getRawVal( $name ), 'false' ) !== 0;
651  }
652 
661  public function getCheck( $name ) {
662  # Checkboxes and buttons are only present when clicked
663  # Presence connotes truth, absence false
664  return $this->getRawVal( $name, null ) !== null;
665  }
666 
677  public function getText( $name, $default = '' ) {
678  $val = $this->getVal( $name, $default );
679  return str_replace( "\r\n", "\n", $val );
680  }
681 
689  public function getValues() {
690  $names = func_get_args();
691  if ( count( $names ) == 0 ) {
692  $names = array_keys( $this->data );
693  }
694 
695  $retVal = [];
696  foreach ( $names as $name ) {
697  $value = $this->getGPCVal( $this->data, $name, null );
698  if ( $value !== null ) {
699  $retVal[$name] = $value;
700  }
701  }
702  return $retVal;
703  }
704 
711  public function getValueNames( $exclude = [] ) {
712  return array_diff( array_keys( $this->getValues() ), $exclude );
713  }
714 
722  public function getQueryValues() {
724  }
725 
735  public function getQueryValuesOnly() {
736  return $this->queryParams;
737  }
738 
747  public function getPostValues() {
748  return $_POST;
749  }
750 
758  public function getRawQueryString() {
759  return $_SERVER['QUERY_STRING'];
760  }
761 
768  public function getRawPostString() {
769  if ( !$this->wasPosted() ) {
770  return '';
771  }
772  return $this->getRawInput();
773  }
774 
782  public function getRawInput() {
783  static $input = null;
784  if ( $input === null ) {
785  $input = file_get_contents( 'php://input' );
786  }
787  return $input;
788  }
789 
795  public function getMethod() {
796  return $_SERVER['REQUEST_METHOD'] ?? 'GET';
797  }
798 
808  public function wasPosted() {
809  return $this->getMethod() == 'POST';
810  }
811 
822  public function getSession() {
823  if ( $this->sessionId !== null ) {
824  $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
825  if ( $session ) {
826  return $session;
827  }
828  }
829 
830  $session = SessionManager::singleton()->getSessionForRequest( $this );
831  $this->sessionId = $session->getSessionId();
832  return $session;
833  }
834 
841  public function setSessionId( SessionId $sessionId ) {
842  $this->sessionId = $sessionId;
843  }
844 
851  public function getSessionId() {
852  return $this->sessionId;
853  }
854 
863  public function getCookie( $key, $prefix = null, $default = null ) {
864  if ( $prefix === null ) {
865  global $wgCookiePrefix;
866  $prefix = $wgCookiePrefix;
867  }
868  return $this->getGPCVal( $_COOKIE, $prefix . $key, $default );
869  }
870 
878  public static function getGlobalRequestURL() {
879  // This method is called on fatal errors; it should not depend on anything complex.
880 
881  if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
882  $base = $_SERVER['REQUEST_URI'];
883  } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] )
884  && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] )
885  ) {
886  // Probably IIS; doesn't set REQUEST_URI
887  $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
888  } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
889  $base = $_SERVER['SCRIPT_NAME'];
890  if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
891  $base .= '?' . $_SERVER['QUERY_STRING'];
892  }
893  } else {
894  // This shouldn't happen!
895  throw new MWException( "Web server doesn't provide either " .
896  "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
897  "of your web server configuration to https://phabricator.wikimedia.org/" );
898  }
899  // User-agents should not send a fragment with the URI, but
900  // if they do, and the web server passes it on to us, we
901  // need to strip it or we get false-positive redirect loops
902  // or weird output URLs
903  $hash = strpos( $base, '#' );
904  if ( $hash !== false ) {
905  $base = substr( $base, 0, $hash );
906  }
907 
908  if ( $base[0] == '/' ) {
909  // More than one slash will look like it is protocol relative
910  return preg_replace( '!^/+!', '/', $base );
911  } else {
912  // We may get paths with a host prepended; strip it.
913  return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base );
914  }
915  }
916 
924  public function getRequestURL() {
925  return self::getGlobalRequestURL();
926  }
927 
938  public function getFullRequestURL() {
939  // Pass an explicit PROTO constant instead of PROTO_CURRENT so that we
940  // do not rely on state from the global $wgRequest object (which it would,
941  // via wfGetServerUrl/wfExpandUrl/$wgRequest->protocol).
942  if ( $this->getProtocol() === 'http' ) {
943  return wfGetServerUrl( PROTO_HTTP ) . $this->getRequestURL();
944  } else {
945  return wfGetServerUrl( PROTO_HTTPS ) . $this->getRequestURL();
946  }
947  }
948 
954  public function appendQueryValue( $key, $value ) {
955  return $this->appendQueryArray( [ $key => $value ] );
956  }
957 
964  public function appendQueryArray( $array ) {
965  $newquery = $this->getQueryValues();
966  unset( $newquery['title'] );
967  $newquery = array_merge( $newquery, $array );
968 
969  return wfArrayToCgi( $newquery );
970  }
971 
981  public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
982  wfDeprecated( __METHOD__, '1.35' );
983 
984  global $wgUser;
985  return $this->getLimitOffsetForUser( $wgUser, $deflimit, $optionname );
986  }
987 
998  public function getLimitOffsetForUser( User $user, $deflimit = 50, $optionname = 'rclimit' ) {
999  $limit = $this->getInt( 'limit', 0 );
1000  if ( $limit < 0 ) {
1001  $limit = 0;
1002  }
1003  if ( ( $limit == 0 ) && ( $optionname != '' ) ) {
1004  $limit = $user->getIntOption( $optionname );
1005  }
1006  if ( $limit <= 0 ) {
1007  $limit = $deflimit;
1008  }
1009  if ( $limit > 5000 ) {
1010  $limit = 5000; # We have *some* limits...
1011  }
1012 
1013  $offset = $this->getInt( 'offset', 0 );
1014  if ( $offset < 0 ) {
1015  $offset = 0;
1016  }
1017 
1018  return [ $limit, $offset ];
1019  }
1020 
1027  public function getFileTempname( $key ) {
1028  $file = new WebRequestUpload( $this, $key );
1029  return $file->getTempName();
1030  }
1031 
1038  public function getUploadError( $key ) {
1039  $file = new WebRequestUpload( $this, $key );
1040  return $file->getError();
1041  }
1042 
1054  public function getFileName( $key ) {
1055  $file = new WebRequestUpload( $this, $key );
1056  return $file->getName();
1057  }
1058 
1065  public function getUpload( $key ) {
1066  return new WebRequestUpload( $this, $key );
1067  }
1068 
1075  public function response() {
1076  /* Lazy initialization of response object for this request */
1077  if ( !is_object( $this->response ) ) {
1078  $class = ( $this instanceof FauxRequest ) ? FauxResponse::class : WebResponse::class;
1079  $this->response = new $class();
1080  }
1081  return $this->response;
1082  }
1083 
1087  protected function initHeaders() {
1088  if ( count( $this->headers ) ) {
1089  return;
1090  }
1091 
1092  $this->headers = array_change_key_case( getallheaders(), CASE_UPPER );
1093  }
1094 
1100  public function getAllHeaders() {
1101  $this->initHeaders();
1102  return $this->headers;
1103  }
1104 
1117  public function getHeader( $name, $flags = 0 ) {
1118  $this->initHeaders();
1119  $name = strtoupper( $name );
1120  if ( !isset( $this->headers[$name] ) ) {
1121  return false;
1122  }
1123  $value = $this->headers[$name];
1124  if ( $flags & self::GETHEADER_LIST ) {
1125  $value = array_map( 'trim', explode( ',', $value ) );
1126  }
1127  return $value;
1128  }
1129 
1137  public function getSessionData( $key ) {
1138  return $this->getSession()->get( $key );
1139  }
1140 
1148  public function setSessionData( $key, $data ) {
1149  $this->getSession()->set( $key, $data );
1150  }
1151 
1161  public function checkUrlExtension( $extWhitelist = [] ) {
1162  wfDeprecated( __METHOD__, '1.35' );
1163  return true;
1164  }
1165 
1175  public function getAcceptLang() {
1176  // Modified version of code found at
1177  // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1178  $acceptLang = $this->getHeader( 'Accept-Language' );
1179  if ( !$acceptLang ) {
1180  return [];
1181  }
1182 
1183  // Return the language codes in lower case
1184  $acceptLang = strtolower( $acceptLang );
1185 
1186  // Break up string into pieces (languages and q factors)
1187  $lang_parse = null;
1188  preg_match_all(
1189  '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/',
1190  $acceptLang,
1191  $lang_parse
1192  );
1193 
1194  if ( !count( $lang_parse[1] ) ) {
1195  return [];
1196  }
1197 
1198  $langcodes = $lang_parse[1];
1199  $qvalues = $lang_parse[4];
1200  $indices = range( 0, count( $lang_parse[1] ) - 1 );
1201 
1202  // Set default q factor to 1
1203  foreach ( $indices as $index ) {
1204  if ( $qvalues[$index] === '' ) {
1205  $qvalues[$index] = 1;
1206  } elseif ( $qvalues[$index] == 0 ) {
1207  unset( $langcodes[$index], $qvalues[$index], $indices[$index] );
1208  }
1209  }
1210 
1211  // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way
1212  array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes );
1213 
1214  // Create a list like "en" => 0.8
1215  $langs = array_combine( $langcodes, $qvalues );
1216 
1217  return $langs;
1218  }
1219 
1228  protected function getRawIP() {
1229  if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) {
1230  return null;
1231  }
1232 
1233  if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) {
1234  throw new MWException( __METHOD__
1235  . " : Could not determine the remote IP address due to multiple values." );
1236  } else {
1237  $ipchain = $_SERVER['REMOTE_ADDR'];
1238  }
1239 
1240  return IPUtils::canonicalize( $ipchain );
1241  }
1242 
1252  public function getIP() {
1253  global $wgUsePrivateIPs;
1254 
1255  # Return cached result
1256  if ( $this->ip !== null ) {
1257  return $this->ip;
1258  }
1259 
1260  # collect the originating ips
1261  $ip = $this->getRawIP();
1262  if ( !$ip ) {
1263  throw new MWException( 'Unable to determine IP.' );
1264  }
1265 
1266  # Append XFF
1267  $forwardedFor = $this->getHeader( 'X-Forwarded-For' );
1268  if ( $forwardedFor !== false ) {
1269  $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
1270  $isConfigured = $proxyLookup->isConfiguredProxy( $ip );
1271  $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) );
1272  $ipchain = array_reverse( $ipchain );
1273  array_unshift( $ipchain, $ip );
1274 
1275  # Step through XFF list and find the last address in the list which is a
1276  # trusted server. Set $ip to the IP address given by that trusted server,
1277  # unless the address is not sensible (e.g. private). However, prefer private
1278  # IP addresses over proxy servers controlled by this site (more sensible).
1279  # Note that some XFF values might be "unknown" with Squid/Varnish.
1280  foreach ( $ipchain as $i => $curIP ) {
1281  $curIP = IPUtils::sanitizeIP( IPUtils::canonicalize( $curIP ) );
1282  if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown'
1283  || !$proxyLookup->isTrustedProxy( $curIP )
1284  ) {
1285  break; // IP is not valid/trusted or does not point to anything
1286  }
1287  if (
1288  IPUtils::isPublic( $ipchain[$i + 1] ) ||
1289  $wgUsePrivateIPs ||
1290  $proxyLookup->isConfiguredProxy( $curIP ) // T50919; treat IP as sane
1291  ) {
1292  // Follow the next IP according to the proxy
1293  $nextIP = IPUtils::canonicalize( $ipchain[$i + 1] );
1294  if ( !$nextIP && $isConfigured ) {
1295  // We have not yet made it past CDN/proxy servers of this site,
1296  // so either they are misconfigured or there is some IP spoofing.
1297  throw new MWException( "Invalid IP given in XFF '$forwardedFor'." );
1298  }
1299  $ip = $nextIP;
1300  // keep traversing the chain
1301  continue;
1302  }
1303  break;
1304  }
1305  }
1306 
1307  # Allow extensions to improve our guess
1308  Hooks::runner()->onGetIP( $ip );
1309 
1310  if ( !$ip ) {
1311  throw new MWException( "Unable to determine IP." );
1312  }
1313 
1314  $this->ip = $ip;
1315  return $ip;
1316  }
1317 
1323  public function setIP( $ip ) {
1324  $this->ip = $ip;
1325  }
1326 
1339  public function hasSafeMethod() {
1340  if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
1341  return false; // CLI mode
1342  }
1343 
1344  return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
1345  }
1346 
1365  public function isSafeRequest() {
1366  if ( $this->markedAsSafe && $this->wasPosted() ) {
1367  return true; // marked as a "safe" POST
1368  }
1369 
1370  return $this->hasSafeMethod();
1371  }
1372 
1383  public function markAsSafeRequest() {
1384  $this->markedAsSafe = true;
1385  }
1386 }
PathRouter\add
add( $path, $params=[], $options=[])
Add a new path pattern to the path router.
Definition: PathRouter.php:158
WebRequest\initHeaders
initHeaders()
Initialise the header list.
Definition: WebRequest.php:1087
WebRequest\$sessionId
SessionId null $sessionId
Session ID to use for this request.
Definition: WebRequest.php:112
FauxRequest
WebRequest clone which takes values from a provided array.
Definition: FauxRequest.php:33
WebRequest\getSessionData
getSessionData( $key)
Get data from the session.
Definition: WebRequest.php:1137
WebRequest\$headers
array $headers
Lazy-initialized request headers indexed by upper-case header name.
Definition: WebRequest.php:66
WebRequest\getValueNames
getValueNames( $exclude=[])
Returns the names of all input values excluding those in $exclude.
Definition: WebRequest.php:711
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:146
WebRequest\$data
array $data
The parameters from $_GET, $_POST and the path router.
Definition: WebRequest.php:48
WebRequest\getRequestPathSuffix
static getRequestPathSuffix( $basePath)
If the request URL matches a given base path, extract the path part of the request URL after that bas...
Definition: WebRequest.php:234
WebRequest\getSessionId
getSessionId()
Get the session id for this request, if any.
Definition: WebRequest.php:851
$wgScript
$wgScript
The URL path to index.php.
Definition: DefaultSettings.php:185
WebRequest\$queryParams
$queryParams
The parameters from $_GET only.
Definition: WebRequest.php:60
User\getIntOption
getIntOption( $oname, $defaultOverride=0)
Get the user's current setting for a given option, as an integer value.
Definition: User.php:2793
WebRequest\appendQueryValue
appendQueryValue( $key, $value)
Definition: WebRequest.php:954
WebRequest\interpolateTitle
interpolateTitle()
Check for title, action, and/or variant data in the URL and interpolate it into the GET variables.
Definition: WebRequest.php:376
WebRequest\setSessionId
setSessionId(SessionId $sessionId)
Set the session for this request.
Definition: WebRequest.php:841
WebRequest\getAcceptLang
getAcceptLang()
Parse the Accept-Language header sent by the client into an array.
Definition: WebRequest.php:1175
WebRequest\getElapsedTime
getElapsedTime()
Get the number of seconds to have elapsed since request start, in fractional seconds,...
Definition: WebRequest.php:318
WebRequest\getIntOrNull
getIntOrNull( $name)
Fetch an integer value from the input or return null if empty.
Definition: WebRequest.php:605
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
WebRequest\getRawPostString
getRawPostString()
Return the contents of the POST with no decoding.
Definition: WebRequest.php:768
WebRequest\detectProtocol
static detectProtocol()
Detect the protocol from $_SERVER.
Definition: WebRequest.php:301
WebRequest\getGPCVal
getGPCVal( $arr, $name, $default)
Fetch a value from the given array or return $default if it's not set.
Definition: WebRequest.php:445
WebRequest\hasSafeMethod
hasSafeMethod()
Check if this request uses a "safe" HTTP method.
Definition: WebRequest.php:1339
$base
$base
Definition: generateLocalAutoload.php:11
WebRequest\__construct
__construct()
Definition: WebRequest.php:120
WebRequest\getRawQueryString
getRawQueryString()
Return the contents of the Query with no decoding.
Definition: WebRequest.php:758
WebRequest\appendQueryArray
appendQueryArray( $array)
Appends or replaces value of query variables.
Definition: WebRequest.php:964
$wgAllowExternalReqID
$wgAllowExternalReqID
Whether to respect/honour the request ID provided by the incoming request via the X-Request-Id header...
Definition: DefaultSettings.php:8854
PathRouter\getActionPaths
static getActionPaths(array $actionPaths, $articlePath)
Definition: PathRouter.php:430
WebRequest\getFileTempname
getFileTempname( $key)
Return the path to the temporary file where PHP has stored the upload.
Definition: WebRequest.php:1027
$wgAssumeProxiesUseDefaultProtocolPorts
bool $wgAssumeProxiesUseDefaultProtocolPorts
When the wiki is running behind a proxy and this is set to true, assumes that the proxy exposes the w...
Definition: DefaultSettings.php:88
WebRequest\isSafeRequest
isSafeRequest()
Whether this request should be identified as being "safe".
Definition: WebRequest.php:1365
WebRequest\getText
getText( $name, $default='')
Fetch a text string from the given array or return $default if it's not set.
Definition: WebRequest.php:677
WebRequest\$protocol
string $protocol
Cached URL protocol.
Definition: WebRequest.php:102
WebRequest\getMethod
getMethod()
Get the HTTP method used for this request.
Definition: WebRequest.php:795
MWException
MediaWiki exception.
Definition: MWException.php:26
WebRequest\getFileName
getFileName( $key)
Return the original filename of the uploaded file, as reported by the submitting user agent.
Definition: WebRequest.php:1054
WebRequest\getQueryValuesOnly
getQueryValuesOnly()
Get the values passed in the query string only, not including the path router parameters.
Definition: WebRequest.php:735
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1030
MediaWiki\Session\Session
Manages data for an an authenticated session.
Definition: Session.php:48
WebRequest\setVal
setVal( $key, $value)
Set an arbitrary value into our get/post data.
Definition: WebRequest.php:526
WebRequest\$reqId
static string $reqId
The unique request ID.
Definition: WebRequest.php:78
$matches
$matches
Definition: NoLocalSettings.php:24
WebRequest\getRawInput
getRawInput()
Return the raw request body, with no processing.
Definition: WebRequest.php:782
WebRequest\getUpload
getUpload( $key)
Return a WebRequestUpload object corresponding to the key.
Definition: WebRequest.php:1065
WebRequest\getValues
getValues()
Extracts the given named values into an array.
Definition: WebRequest.php:689
WebRequest\getPathInfo
static getPathInfo( $want='all')
Extract relevant query arguments from the http request uri's path to be merged with the normal php pr...
Definition: WebRequest.php:149
WebRequest\getFullRequestURL
getFullRequestURL()
Return the request URI with the canonical service and hostname, path, and query string.
Definition: WebRequest.php:938
WebRequest\getArray
getArray( $name, $default=null)
Fetch an array from the input or return $default if it's not set.
Definition: WebRequest.php:557
WebRequest\$response
WebResponse $response
Lazy-init response object.
Definition: WebRequest.php:84
WebRequest\markAsSafeRequest
markAsSafeRequest()
Mark this request as identified as being nullipotent even if it is a POST request.
Definition: WebRequest.php:1383
WebRequest\getAllHeaders
getAllHeaders()
Get an array containing all request headers.
Definition: WebRequest.php:1100
WebRequestUpload
Object to access the $_FILES array.
Definition: WebRequestUpload.php:30
PROTO_HTTPS
const PROTO_HTTPS
Definition: Defines.php:209
WebRequest\normalizeUnicode
normalizeUnicode( $data)
Recursively normalizes UTF-8 strings in the given array.
Definition: WebRequest.php:424
WebRequest\getRawVal
getRawVal( $name, $default=null)
Fetch a scalar from the input without normalization, or return $default if it's not set.
Definition: WebRequest.php:483
WebRequest\getCheck
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
Definition: WebRequest.php:661
WebRequest\getProtocol
getProtocol()
Get the current URL protocol (http or https)
Definition: WebRequest.php:362
WebRequest\setIP
setIP( $ip)
Definition: WebRequest.php:1323
WebRequest\getSession
getSession()
Return the session for this request.
Definition: WebRequest.php:822
WebRequest\response
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff,...
Definition: WebRequest.php:1075
$wgUsePrivateIPs
$wgUsePrivateIPs
Should forwarded Private IPs be accepted?
Definition: DefaultSettings.php:3039
MediaWiki\Session\SessionManager
This serves as the entry point to the MediaWiki session handling system.
Definition: SessionManager.php:52
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:171
WebRequest\getLimitOffset
getLimitOffset( $deflimit=50, $optionname='rclimit')
Same as ::getLimitOffsetForUser, but without a user parameter, instead using $wgUser.
Definition: WebRequest.php:981
WebRequest\checkUrlExtension
checkUrlExtension( $extWhitelist=[])
This function formerly did a security check to prevent an XSS vulnerability in IE6,...
Definition: WebRequest.php:1161
PROTO_HTTP
const PROTO_HTTP
Definition: Defines.php:208
WebRequest\getIntArray
getIntArray( $name, $default=null)
Fetch an array of integers, or return $default if it's not set.
Definition: WebRequest.php:576
WebRequest\$requestTime
float $requestTime
The timestamp of the start of the request, with microsecond precision.
Definition: WebRequest.php:96
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:451
WebRequest\$markedAsSafe
bool $markedAsSafe
Whether this HTTP request is "safe" (even if it is an HTTP post)
Definition: WebRequest.php:115
WebRequest\getCookie
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
Definition: WebRequest.php:863
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:43
WebRequest\getIP
getIP()
Work out the IP address based on various globals For trusted proxies, use the XFF client IP (first of...
Definition: WebRequest.php:1252
WebRequest\getUploadError
getUploadError( $key)
Return the upload error or 0.
Definition: WebRequest.php:1038
WebRequest\$queryAndPathParams
array $queryAndPathParams
The parameters from $_GET.
Definition: WebRequest.php:55
$wgArticlePath
$wgArticlePath
The URL path for primary article page views.
Definition: DefaultSettings.php:245
WebRequest\setSessionData
setSessionData( $key, $data)
Set session data.
Definition: WebRequest.php:1148
$wgActionPaths
$wgActionPaths
To set 'pretty' URL paths for actions other than plain page views, add to this array.
Definition: DefaultSettings.php:424
WebRequest\GETHEADER_LIST
const GETHEADER_LIST
Flag to make WebRequest::getHeader return an array of values.
Definition: WebRequest.php:72
WebRequest\getVal
getVal( $name, $default=null)
Fetch a scalar from the input or return $default if it's not set.
Definition: WebRequest.php:507
WebRequest\getInt
getInt( $name, $default=0)
Fetch an integer value from the input or return $default if not set.
Definition: WebRequest.php:593
MediaWiki\Session\SessionId
Value object holding the session ID in a manner that can be globally updated.
Definition: SessionId.php:38
WebRequest\getRequestId
static getRequestId()
Get the unique request ID.
Definition: WebRequest.php:330
WebRequest\getLimitOffsetForUser
getLimitOffsetForUser(User $user, $deflimit=50, $optionname='rclimit')
Check for limit and offset parameters on the input, and return sensible defaults if not given.
Definition: WebRequest.php:998
$path
$path
Definition: NoLocalSettings.php:25
WebRequest\getFloat
getFloat( $name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
Definition: WebRequest.php:622
WebRequest\detectServer
static detectServer()
Work out an appropriate URL prefix containing scheme and host, based on information detected from $_S...
Definition: WebRequest.php:255
WebRequest\getHeader
getHeader( $name, $flags=0)
Get a request header, or false if it isn't set.
Definition: WebRequest.php:1117
$basePath
$basePath
Definition: addSite.php:5
WebRequest\getGlobalRequestURL
static getGlobalRequestURL()
Return the path and query string portion of the main request URI.
Definition: WebRequest.php:878
WebRequest\$ip
string $ip
Cached client IP address.
Definition: WebRequest.php:90
WebRequest\getPostValues
getPostValues()
Get the values passed via POST.
Definition: WebRequest.php:747
WebRequest\wasPosted
wasPosted()
Returns true if the present request was reached by a POST operation, false otherwise (GET,...
Definition: WebRequest.php:808
WebRequest\unsetVal
unsetVal( $key)
Unset an arbitrary value from our get/post data.
Definition: WebRequest.php:538
wfGetServerUrl
wfGetServerUrl( $proto)
Get the wiki's "server", i.e.
Definition: GlobalFunctions.php:569
WebRequest\getRequestURL
getRequestURL()
Return the path and query string portion of the request URI.
Definition: WebRequest.php:924
WebRequest\overrideRequestId
static overrideRequestId( $id)
Override the unique request ID.
Definition: WebRequest.php:354
WebRequest\extractTitle
static extractTitle( $path, $bases, $key=false)
URL rewriting function; tries to extract page title and, optionally, one other fixed parameter value ...
Definition: WebRequest.php:398
WebResponse
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
Definition: WebResponse.php:28
WebRequest\getRawIP
getRawIP()
Fetch the raw IP from the request.
Definition: WebRequest.php:1228
WebRequest\getQueryValues
getQueryValues()
Get the values passed in the query string and the path router parameters.
Definition: WebRequest.php:722
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:55
WebRequest\getFuzzyBool
getFuzzyBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:648
$wgVariantArticlePath
$wgVariantArticlePath
Like $wgArticlePath, but on multi-variant wikis, this provides a path format that describes which par...
Definition: DefaultSettings.php:3259
PathRouter
PathRouter class.
Definition: PathRouter.php:73
$wgCookiePrefix
$wgCookiePrefix
Cookies generated by MediaWiki have names starting with this prefix.
Definition: DefaultSettings.php:6414
WebRequest\getBool
getBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:635
$wgUsePathInfo
$wgUsePathInfo
Whether to support URLs like index.php/Page_title These often break when PHP is set up in CGI mode.
Definition: DefaultSettings.php:156
wfArrayToCgi
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
Definition: GlobalFunctions.php:347
wfRandomString
wfRandomString( $length=32)
Get a random string containing a number of pseudo-random hex characters.
Definition: GlobalFunctions.php:274