MediaWiki  master
WebRequest.php
Go to the documentation of this file.
1 <?php
30 
31 // The point of this class is to be a wrapper around super globals
32 // phpcs:disable MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals
33 
41 class WebRequest {
42  protected $data, $headers = [];
43 
48  const GETHEADER_LIST = 1;
49 
54  private static $reqId;
55 
60  private $response;
61 
66  private $ip;
67 
72  protected $requestTime;
73 
78  protected $protocol;
79 
85  protected $sessionId = null;
86 
88  protected $markedAsSafe = false;
89 
93  public function __construct() {
94  $this->requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
95 
96  // POST overrides GET data
97  // We don't use $_REQUEST here to avoid interference from cookies...
98  $this->data = $_POST + $_GET;
99  }
100 
116  public static function getPathInfo( $want = 'all' ) {
117  global $wgUsePathInfo;
118  // PATH_INFO is mangled due to https://bugs.php.net/bug.php?id=31892
119  // And also by Apache 2.x, double slashes are converted to single slashes.
120  // So we will use REQUEST_URI if possible.
121  $matches = [];
122  if ( !empty( $_SERVER['REQUEST_URI'] ) ) {
123  // Slurp out the path portion to examine...
124  $url = $_SERVER['REQUEST_URI'];
125  if ( !preg_match( '!^https?://!', $url ) ) {
126  $url = 'http://unused' . $url;
127  }
128  Wikimedia\suppressWarnings();
129  $a = parse_url( $url );
130  Wikimedia\restoreWarnings();
131  if ( $a ) {
132  $path = $a['path'] ?? '';
133 
134  global $wgScript;
135  if ( $path == $wgScript && $want !== 'all' ) {
136  // Script inside a rewrite path?
137  // Abort to keep from breaking...
138  return $matches;
139  }
140 
141  $router = new PathRouter;
142 
143  // Raw PATH_INFO style
144  $router->add( "$wgScript/$1" );
145 
146  if ( isset( $_SERVER['SCRIPT_NAME'] )
147  && preg_match( '/\.php/', $_SERVER['SCRIPT_NAME'] )
148  ) {
149  # Check for SCRIPT_NAME, we handle index.php explicitly
150  # But we do have some other .php files such as img_auth.php
151  # Don't let root article paths clober the parsing for them
152  $router->add( $_SERVER['SCRIPT_NAME'] . "/$1" );
153  }
154 
155  global $wgArticlePath;
156  if ( $wgArticlePath ) {
157  $router->add( $wgArticlePath );
158  }
159 
160  global $wgActionPaths;
161  if ( $wgActionPaths ) {
162  $router->add( $wgActionPaths, [ 'action' => '$key' ] );
163  }
164 
165  global $wgVariantArticlePath;
166  if ( $wgVariantArticlePath ) {
167  $router->add( $wgVariantArticlePath,
168  [ 'variant' => '$2' ],
169  [ '$2' => MediaWikiServices::getInstance()->getContentLanguage()->
170  getVariants() ]
171  );
172  }
173 
174  Hooks::run( 'WebRequestPathInfoRouter', [ $router ] );
175 
176  $matches = $router->parse( $path );
177  }
178  } elseif ( $wgUsePathInfo ) {
179  if ( isset( $_SERVER['ORIG_PATH_INFO'] ) && $_SERVER['ORIG_PATH_INFO'] != '' ) {
180  // Mangled PATH_INFO
181  // https://bugs.php.net/bug.php?id=31892
182  // Also reported when ini_get('cgi.fix_pathinfo')==false
183  $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 );
184 
185  } elseif ( isset( $_SERVER['PATH_INFO'] ) && $_SERVER['PATH_INFO'] != '' ) {
186  // Regular old PATH_INFO yay
187  $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 );
188  }
189  }
190 
191  return $matches;
192  }
193 
200  public static function detectServer() {
202 
203  $proto = self::detectProtocol();
204  $stdPort = $proto === 'https' ? 443 : 80;
205 
206  $varNames = [ 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ];
207  $host = 'localhost';
208  $port = $stdPort;
209  foreach ( $varNames as $varName ) {
210  if ( !isset( $_SERVER[$varName] ) ) {
211  continue;
212  }
213 
214  $parts = IP::splitHostAndPort( $_SERVER[$varName] );
215  if ( !$parts ) {
216  // Invalid, do not use
217  continue;
218  }
219 
220  $host = $parts[0];
221  if ( $wgAssumeProxiesUseDefaultProtocolPorts && isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
222  // T72021: Assume that upstream proxy is running on the default
223  // port based on the protocol. We have no reliable way to determine
224  // the actual port in use upstream.
225  $port = $stdPort;
226  } elseif ( $parts[1] === false ) {
227  if ( isset( $_SERVER['SERVER_PORT'] ) ) {
228  $port = $_SERVER['SERVER_PORT'];
229  } // else leave it as $stdPort
230  } else {
231  $port = $parts[1];
232  }
233  break;
234  }
235 
236  return $proto . '://' . IP::combineHostAndPort( $host, $port, $stdPort );
237  }
238 
246  public static function detectProtocol() {
247  if ( ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ||
248  ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
249  $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) ) {
250  return 'https';
251  } else {
252  return 'http';
253  }
254  }
255 
263  public function getElapsedTime() {
264  return microtime( true ) - $this->requestTime;
265  }
266 
275  public static function getRequestId() {
276  // This method is called from various error handlers and should be kept simple.
277 
278  if ( self::$reqId ) {
279  return self::$reqId;
280  }
281 
282  global $wgAllowExternalReqID;
283 
284  self::$reqId = $_SERVER['UNIQUE_ID'] ?? wfRandomString( 24 );
285  if ( $wgAllowExternalReqID ) {
286  $id = RequestContext::getMain()->getRequest()->getHeader( 'X-Request-Id' );
287  if ( $id ) {
288  self::$reqId = $id;
289  }
290  }
291 
292  return self::$reqId;
293  }
294 
302  public static function overrideRequestId( $id ) {
303  self::$reqId = $id;
304  }
305 
310  public function getProtocol() {
311  if ( $this->protocol === null ) {
312  $this->protocol = self::detectProtocol();
313  }
314  return $this->protocol;
315  }
316 
324  public function interpolateTitle() {
325  // T18019: title interpolation on API queries is useless and sometimes harmful
326  if ( defined( 'MW_API' ) ) {
327  return;
328  }
329 
330  $matches = self::getPathInfo( 'title' );
331  foreach ( $matches as $key => $val ) {
332  $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val;
333  }
334  }
335 
346  static function extractTitle( $path, $bases, $key = false ) {
347  foreach ( (array)$bases as $keyValue => $base ) {
348  // Find the part after $wgArticlePath
349  $base = str_replace( '$1', '', $base );
350  $baseLen = strlen( $base );
351  if ( substr( $path, 0, $baseLen ) == $base ) {
352  $raw = substr( $path, $baseLen );
353  if ( $raw !== '' ) {
354  $matches = [ 'title' => rawurldecode( $raw ) ];
355  if ( $key ) {
356  $matches[$key] = $keyValue;
357  }
358  return $matches;
359  }
360  }
361  }
362  return [];
363  }
364 
372  public function normalizeUnicode( $data ) {
373  if ( is_array( $data ) ) {
374  foreach ( $data as $key => $val ) {
375  $data[$key] = $this->normalizeUnicode( $val );
376  }
377  } else {
378  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
379  $data = $contLang ? $contLang->normalize( $data ) :
380  UtfNormal\Validator::cleanUp( $data );
381  }
382  return $data;
383  }
384 
393  private function getGPCVal( $arr, $name, $default ) {
394  # PHP is so nice to not touch input data, except sometimes:
395  # https://www.php.net/variables.external#language.variables.external.dot-in-names
396  # Work around PHP *feature* to avoid *bugs* elsewhere.
397  $name = strtr( $name, '.', '_' );
398  if ( isset( $arr[$name] ) ) {
399  $data = $arr[$name];
400  if ( isset( $_GET[$name] ) && is_string( $data ) ) {
401  # Check for alternate/legacy character encoding.
402  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
403  $data = $contLang->checkTitleEncoding( $data );
404  }
405  $data = $this->normalizeUnicode( $data );
406  return $data;
407  } else {
408  return $default;
409  }
410  }
411 
424  public function getRawVal( $name, $default = null ) {
425  $name = strtr( $name, '.', '_' ); // See comment in self::getGPCVal()
426  if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
427  $val = $this->data[$name];
428  } else {
429  $val = $default;
430  }
431  if ( is_null( $val ) ) {
432  return $val;
433  } else {
434  return (string)$val;
435  }
436  }
437 
448  public function getVal( $name, $default = null ) {
449  $val = $this->getGPCVal( $this->data, $name, $default );
450  if ( is_array( $val ) ) {
451  $val = $default;
452  }
453  if ( is_null( $val ) ) {
454  return $val;
455  } else {
456  return (string)$val;
457  }
458  }
459 
467  public function setVal( $key, $value ) {
468  $ret = $this->data[$key] ?? null;
469  $this->data[$key] = $value;
470  return $ret;
471  }
472 
479  public function unsetVal( $key ) {
480  if ( !isset( $this->data[$key] ) ) {
481  $ret = null;
482  } else {
483  $ret = $this->data[$key];
484  unset( $this->data[$key] );
485  }
486  return $ret;
487  }
488 
498  public function getArray( $name, $default = null ) {
499  $val = $this->getGPCVal( $this->data, $name, $default );
500  if ( is_null( $val ) ) {
501  return null;
502  } else {
503  return (array)$val;
504  }
505  }
506 
517  public function getIntArray( $name, $default = null ) {
518  $val = $this->getArray( $name, $default );
519  if ( is_array( $val ) ) {
520  $val = array_map( 'intval', $val );
521  }
522  return $val;
523  }
524 
534  public function getInt( $name, $default = 0 ) {
535  return intval( $this->getRawVal( $name, $default ) );
536  }
537 
546  public function getIntOrNull( $name ) {
547  $val = $this->getRawVal( $name );
548  return is_numeric( $val )
549  ? intval( $val )
550  : null;
551  }
552 
563  public function getFloat( $name, $default = 0.0 ) {
564  return floatval( $this->getRawVal( $name, $default ) );
565  }
566 
576  public function getBool( $name, $default = false ) {
577  return (bool)$this->getRawVal( $name, $default );
578  }
579 
589  public function getFuzzyBool( $name, $default = false ) {
590  return $this->getBool( $name, $default )
591  && strcasecmp( $this->getRawVal( $name ), 'false' ) !== 0;
592  }
593 
602  public function getCheck( $name ) {
603  # Checkboxes and buttons are only present when clicked
604  # Presence connotes truth, absence false
605  return $this->getRawVal( $name, null ) !== null;
606  }
607 
618  public function getText( $name, $default = '' ) {
619  $val = $this->getVal( $name, $default );
620  return str_replace( "\r\n", "\n", $val );
621  }
622 
630  public function getValues() {
631  $names = func_get_args();
632  if ( count( $names ) == 0 ) {
633  $names = array_keys( $this->data );
634  }
635 
636  $retVal = [];
637  foreach ( $names as $name ) {
638  $value = $this->getGPCVal( $this->data, $name, null );
639  if ( !is_null( $value ) ) {
640  $retVal[$name] = $value;
641  }
642  }
643  return $retVal;
644  }
645 
652  public function getValueNames( $exclude = [] ) {
653  return array_diff( array_keys( $this->getValues() ), $exclude );
654  }
655 
663  public function getQueryValues() {
664  return $_GET;
665  }
666 
675  public function getPostValues() {
676  return $_POST;
677  }
678 
686  public function getRawQueryString() {
687  return $_SERVER['QUERY_STRING'];
688  }
689 
696  public function getRawPostString() {
697  if ( !$this->wasPosted() ) {
698  return '';
699  }
700  return $this->getRawInput();
701  }
702 
710  public function getRawInput() {
711  static $input = null;
712  if ( $input === null ) {
713  $input = file_get_contents( 'php://input' );
714  }
715  return $input;
716  }
717 
723  public function getMethod() {
724  return $_SERVER['REQUEST_METHOD'] ?? 'GET';
725  }
726 
736  public function wasPosted() {
737  return $this->getMethod() == 'POST';
738  }
739 
750  public function getSession() {
751  if ( $this->sessionId !== null ) {
752  $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
753  if ( $session ) {
754  return $session;
755  }
756  }
757 
758  $session = SessionManager::singleton()->getSessionForRequest( $this );
759  $this->sessionId = $session->getSessionId();
760  return $session;
761  }
762 
769  public function setSessionId( SessionId $sessionId ) {
770  $this->sessionId = $sessionId;
771  }
772 
779  public function getSessionId() {
780  return $this->sessionId;
781  }
782 
791  public function getCookie( $key, $prefix = null, $default = null ) {
792  if ( $prefix === null ) {
793  global $wgCookiePrefix;
794  $prefix = $wgCookiePrefix;
795  }
796  return $this->getGPCVal( $_COOKIE, $prefix . $key, $default );
797  }
798 
806  public static function getGlobalRequestURL() {
807  // This method is called on fatal errors; it should not depend on anything complex.
808 
809  if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
810  $base = $_SERVER['REQUEST_URI'];
811  } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] )
812  && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] )
813  ) {
814  // Probably IIS; doesn't set REQUEST_URI
815  $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
816  } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
817  $base = $_SERVER['SCRIPT_NAME'];
818  if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
819  $base .= '?' . $_SERVER['QUERY_STRING'];
820  }
821  } else {
822  // This shouldn't happen!
823  throw new MWException( "Web server doesn't provide either " .
824  "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
825  "of your web server configuration to https://phabricator.wikimedia.org/" );
826  }
827  // User-agents should not send a fragment with the URI, but
828  // if they do, and the web server passes it on to us, we
829  // need to strip it or we get false-positive redirect loops
830  // or weird output URLs
831  $hash = strpos( $base, '#' );
832  if ( $hash !== false ) {
833  $base = substr( $base, 0, $hash );
834  }
835 
836  if ( $base[0] == '/' ) {
837  // More than one slash will look like it is protocol relative
838  return preg_replace( '!^/+!', '/', $base );
839  } else {
840  // We may get paths with a host prepended; strip it.
841  return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base );
842  }
843  }
844 
852  public function getRequestURL() {
853  return self::getGlobalRequestURL();
854  }
855 
866  public function getFullRequestURL() {
867  // Pass an explicit PROTO constant instead of PROTO_CURRENT so that we
868  // do not rely on state from the global $wgRequest object (which it would,
869  // via wfGetServerUrl/wfExpandUrl/$wgRequest->protocol).
870  if ( $this->getProtocol() === 'http' ) {
871  return wfGetServerUrl( PROTO_HTTP ) . $this->getRequestURL();
872  } else {
873  return wfGetServerUrl( PROTO_HTTPS ) . $this->getRequestURL();
874  }
875  }
876 
882  public function appendQueryValue( $key, $value ) {
883  return $this->appendQueryArray( [ $key => $value ] );
884  }
885 
892  public function appendQueryArray( $array ) {
893  $newquery = $this->getQueryValues();
894  unset( $newquery['title'] );
895  $newquery = array_merge( $newquery, $array );
896 
897  return wfArrayToCgi( $newquery );
898  }
899 
909  public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
910  global $wgUser;
911 
912  $limit = $this->getInt( 'limit', 0 );
913  if ( $limit < 0 ) {
914  $limit = 0;
915  }
916  if ( ( $limit == 0 ) && ( $optionname != '' ) ) {
917  $limit = $wgUser->getIntOption( $optionname );
918  }
919  if ( $limit <= 0 ) {
920  $limit = $deflimit;
921  }
922  if ( $limit > 5000 ) {
923  $limit = 5000; # We have *some* limits...
924  }
925 
926  $offset = $this->getInt( 'offset', 0 );
927  if ( $offset < 0 ) {
928  $offset = 0;
929  }
930 
931  return [ $limit, $offset ];
932  }
933 
940  public function getFileTempname( $key ) {
941  $file = new WebRequestUpload( $this, $key );
942  return $file->getTempName();
943  }
944 
951  public function getUploadError( $key ) {
952  $file = new WebRequestUpload( $this, $key );
953  return $file->getError();
954  }
955 
967  public function getFileName( $key ) {
968  $file = new WebRequestUpload( $this, $key );
969  return $file->getName();
970  }
971 
978  public function getUpload( $key ) {
979  return new WebRequestUpload( $this, $key );
980  }
981 
988  public function response() {
989  /* Lazy initialization of response object for this request */
990  if ( !is_object( $this->response ) ) {
991  $class = ( $this instanceof FauxRequest ) ? FauxResponse::class : WebResponse::class;
992  $this->response = new $class();
993  }
994  return $this->response;
995  }
996 
1000  protected function initHeaders() {
1001  if ( count( $this->headers ) ) {
1002  return;
1003  }
1004 
1005  $apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false;
1006  if ( $apacheHeaders ) {
1007  foreach ( $apacheHeaders as $tempName => $tempValue ) {
1008  $this->headers[strtoupper( $tempName )] = $tempValue;
1009  }
1010  } else {
1011  foreach ( $_SERVER as $name => $value ) {
1012  if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
1013  $name = str_replace( '_', '-', substr( $name, 5 ) );
1014  $this->headers[$name] = $value;
1015  } elseif ( $name === 'CONTENT_LENGTH' ) {
1016  $this->headers['CONTENT-LENGTH'] = $value;
1017  }
1018  }
1019  }
1020  }
1021 
1027  public function getAllHeaders() {
1028  $this->initHeaders();
1029  return $this->headers;
1030  }
1031 
1044  public function getHeader( $name, $flags = 0 ) {
1045  $this->initHeaders();
1046  $name = strtoupper( $name );
1047  if ( !isset( $this->headers[$name] ) ) {
1048  return false;
1049  }
1050  $value = $this->headers[$name];
1051  if ( $flags & self::GETHEADER_LIST ) {
1052  $value = array_map( 'trim', explode( ',', $value ) );
1053  }
1054  return $value;
1055  }
1056 
1064  public function getSessionData( $key ) {
1065  return $this->getSession()->get( $key );
1066  }
1067 
1075  public function setSessionData( $key, $data ) {
1076  $this->getSession()->set( $key, $data );
1077  }
1078 
1089  public function checkUrlExtension( $extWhitelist = [] ) {
1090  $extWhitelist[] = 'php';
1091  if ( IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ) ) {
1092  if ( !$this->wasPosted() ) {
1093  $newUrl = IEUrlExtension::fixUrlForIE6(
1094  $this->getFullRequestURL(), $extWhitelist );
1095  if ( $newUrl !== false ) {
1096  $this->doSecurityRedirect( $newUrl );
1097  return false;
1098  }
1099  }
1100  throw new HttpError( 403,
1101  'Invalid file extension found in the path info or query string.' );
1102  }
1103  return true;
1104  }
1105 
1113  protected function doSecurityRedirect( $url ) {
1114  header( 'Location: ' . $url );
1115  header( 'Content-Type: text/html' );
1116  $encUrl = htmlspecialchars( $url );
1117  echo <<<HTML
1118 <!DOCTYPE html>
1119 <html>
1120 <head>
1121 <title>Security redirect</title>
1122 </head>
1123 <body>
1124 <h1>Security redirect</h1>
1125 <p>
1126 We can't serve non-HTML content from the URL you have requested, because
1127 Internet Explorer would interpret it as an incorrect and potentially dangerous
1128 content type.</p>
1129 <p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the
1130 URL you have requested, except that "&amp;*" is appended. This prevents Internet
1131 Explorer from seeing a bogus file extension.
1132 </p>
1133 </body>
1134 </html>
1135 HTML;
1136  echo "\n";
1137  return true;
1138  }
1139 
1149  public function getAcceptLang() {
1150  // Modified version of code found at
1151  // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1152  $acceptLang = $this->getHeader( 'Accept-Language' );
1153  if ( !$acceptLang ) {
1154  return [];
1155  }
1156 
1157  // Return the language codes in lower case
1158  $acceptLang = strtolower( $acceptLang );
1159 
1160  // Break up string into pieces (languages and q factors)
1161  $lang_parse = null;
1162  preg_match_all(
1163  '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/',
1164  $acceptLang,
1165  $lang_parse
1166  );
1167 
1168  if ( !count( $lang_parse[1] ) ) {
1169  return [];
1170  }
1171 
1172  $langcodes = $lang_parse[1];
1173  $qvalues = $lang_parse[4];
1174  $indices = range( 0, count( $lang_parse[1] ) - 1 );
1175 
1176  // Set default q factor to 1
1177  foreach ( $indices as $index ) {
1178  if ( $qvalues[$index] === '' ) {
1179  $qvalues[$index] = 1;
1180  } elseif ( $qvalues[$index] == 0 ) {
1181  unset( $langcodes[$index], $qvalues[$index], $indices[$index] );
1182  }
1183  }
1184 
1185  // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way
1186  array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes );
1187 
1188  // Create a list like "en" => 0.8
1189  $langs = array_combine( $langcodes, $qvalues );
1190 
1191  return $langs;
1192  }
1193 
1202  protected function getRawIP() {
1203  if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) {
1204  return null;
1205  }
1206 
1207  if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) {
1208  throw new MWException( __METHOD__
1209  . " : Could not determine the remote IP address due to multiple values." );
1210  } else {
1211  $ipchain = $_SERVER['REMOTE_ADDR'];
1212  }
1213 
1214  return IP::canonicalize( $ipchain );
1215  }
1216 
1226  public function getIP() {
1227  global $wgUsePrivateIPs;
1228 
1229  # Return cached result
1230  if ( $this->ip !== null ) {
1231  return $this->ip;
1232  }
1233 
1234  # collect the originating ips
1235  $ip = $this->getRawIP();
1236  if ( !$ip ) {
1237  throw new MWException( 'Unable to determine IP.' );
1238  }
1239 
1240  # Append XFF
1241  $forwardedFor = $this->getHeader( 'X-Forwarded-For' );
1242  if ( $forwardedFor !== false ) {
1243  $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
1244  $isConfigured = $proxyLookup->isConfiguredProxy( $ip );
1245  $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) );
1246  $ipchain = array_reverse( $ipchain );
1247  array_unshift( $ipchain, $ip );
1248 
1249  # Step through XFF list and find the last address in the list which is a
1250  # trusted server. Set $ip to the IP address given by that trusted server,
1251  # unless the address is not sensible (e.g. private). However, prefer private
1252  # IP addresses over proxy servers controlled by this site (more sensible).
1253  # Note that some XFF values might be "unknown" with Squid/Varnish.
1254  foreach ( $ipchain as $i => $curIP ) {
1255  $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) );
1256  if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown'
1257  || !$proxyLookup->isTrustedProxy( $curIP )
1258  ) {
1259  break; // IP is not valid/trusted or does not point to anything
1260  }
1261  if (
1262  IP::isPublic( $ipchain[$i + 1] ) ||
1263  $wgUsePrivateIPs ||
1264  $proxyLookup->isConfiguredProxy( $curIP ) // T50919; treat IP as sane
1265  ) {
1266  // Follow the next IP according to the proxy
1267  $nextIP = IP::canonicalize( $ipchain[$i + 1] );
1268  if ( !$nextIP && $isConfigured ) {
1269  // We have not yet made it past CDN/proxy servers of this site,
1270  // so either they are misconfigured or there is some IP spoofing.
1271  throw new MWException( "Invalid IP given in XFF '$forwardedFor'." );
1272  }
1273  $ip = $nextIP;
1274  // keep traversing the chain
1275  continue;
1276  }
1277  break;
1278  }
1279  }
1280 
1281  # Allow extensions to improve our guess
1282  Hooks::run( 'GetIP', [ &$ip ] );
1283 
1284  if ( !$ip ) {
1285  throw new MWException( "Unable to determine IP." );
1286  }
1287 
1288  wfDebug( "IP: $ip\n" );
1289  $this->ip = $ip;
1290  return $ip;
1291  }
1292 
1298  public function setIP( $ip ) {
1299  $this->ip = $ip;
1300  }
1301 
1314  public function hasSafeMethod() {
1315  if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
1316  return false; // CLI mode
1317  }
1318 
1319  return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
1320  }
1321 
1340  public function isSafeRequest() {
1341  if ( $this->markedAsSafe && $this->wasPosted() ) {
1342  return true; // marked as a "safe" POST
1343  }
1344 
1345  return $this->hasSafeMethod();
1346  }
1347 
1358  public function markAsSafeRequest() {
1359  $this->markedAsSafe = true;
1360  }
1361 }
getRawVal( $name, $default=null)
Fetch a scalar from the input without normalization, or return $default if it&#39;s not set...
Definition: WebRequest.php:424
checkUrlExtension( $extWhitelist=[])
Check if Internet Explorer will detect an incorrect cache extension in PATH_INFO or QUERY_STRING...
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of data
Definition: hooks.txt:6
getPostValues()
Get the values passed via POST.
Definition: WebRequest.php:675
float $requestTime
The timestamp of the start of the request, with microsecond precision.
Definition: WebRequest.php:72
getInt( $name, $default=0)
Fetch an integer value from the input or return $default if not set.
Definition: WebRequest.php:534
getRawPostString()
Return the contents of the POST with no decoding.
Definition: WebRequest.php:696
$wgScript
The URL path to index.php.
static getRequestId()
Get the unique request ID.
Definition: WebRequest.php:275
getHeader( $name, $flags=0)
Get a request header, or false if it isn&#39;t set.
if(is_array( $mode)) switch( $mode) $input
setSessionData( $key, $data)
Set session data.
getFullRequestURL()
Return the request URI with the canonical service and hostname, path, and query string.
Definition: WebRequest.php:866
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1972
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
getVal( $name, $default=null)
Fetch a scalar from the input or return $default if it&#39;s not set.
Definition: WebRequest.php:448
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Definition: router.php:42
$wgCookiePrefix
Cookies generated by MediaWiki have names starting with this prefix.
$wgActionPaths
Definition: img_auth.php:47
setVal( $key, $value)
Set an arbitrary value into our get/post data.
Definition: WebRequest.php:467
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff...
Definition: WebRequest.php:988
unsetVal( $key)
Unset an arbitrary value from our get/post data.
Definition: WebRequest.php:479
string $protocol
Cached URL protocol.
Definition: WebRequest.php:78
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
$value
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
$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...
Definition: WebRequest.php:967
const GETHEADER_LIST
Flag to make WebRequest::getHeader return an array of values.
Definition: WebRequest.php:48
getSession()
Return the session for this request.
Definition: WebRequest.php:750
getFloat( $name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
Definition: WebRequest.php:563
wfGetServerUrl( $proto)
Get the wiki&#39;s "server", i.e.
$wgArticlePath
Definition: img_auth.php:46
initHeaders()
Initialise the header list.
title
string $ip
Cached client IP address.
Definition: WebRequest.php:66
normalizeUnicode( $data)
Recursively normalizes UTF-8 strings in the given array.
Definition: WebRequest.php:372
getBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:576
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
Definition: WebRequest.php:602
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e.g.
Show an error that looks like an HTTP server error.
Definition: HttpError.php:30
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:346
static detectServer()
Work out an appropriate URL prefix containing scheme and host, based on information detected from $_S...
Definition: WebRequest.php:200
getLimitOffset( $deflimit=50, $optionname='rclimit')
Check for limit and offset parameters on the input, and return sensible defaults if not given...
Definition: WebRequest.php:909
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.
getProtocol()
Get the current URL protocol (http or https)
Definition: WebRequest.php:310
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:736
getRequestURL()
Return the path and query string portion of the request URI.
Definition: WebRequest.php:852
PathRouter class.
Definition: PathRouter.php:73
getQueryValues()
Get the values passed in the query string.
Definition: WebRequest.php:663
appendQueryArray( $array)
Appends or replaces value of query variables.
Definition: WebRequest.php:892
static areServerVarsBad( $vars, $extWhitelist=[])
Check a subset of $_SERVER (or the whole of $_SERVER if you like) to see if it indicates that the req...
$wgUsePathInfo
Whether to support URLs like index.php/Page_title These often break when PHP is set up in CGI mode...
bool $wgAssumeProxiesUseDefaultProtocolPorts
When the wiki is running behind a proxy and this is set to true, assumes that the proxy exposes the w...
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:767
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:589
doSecurityRedirect( $url)
Attempt to redirect to a URL with a QUERY_STRING that&#39;s not dangerous in IE 6.
static detectProtocol()
Detect the protocol from $_SERVER.
Definition: WebRequest.php:246
getGPCVal( $arr, $name, $default)
Fetch a value from the given array or return $default if it&#39;s not set.
Definition: WebRequest.php:393
getSessionId()
Get the session id for this request, if any.
Definition: WebRequest.php:779
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
getUpload( $key)
Return a WebRequestUpload object corresponding to the key.
Definition: WebRequest.php:978
const PROTO_HTTP
Definition: Defines.php:199
getValues()
Extracts the given named values into an array.
Definition: WebRequest.php:630
setSessionId(SessionId $sessionId)
Set the session for this request.
Definition: WebRequest.php:769
WebResponse $response
Lazy-init response object.
Definition: WebRequest.php:60
static fixUrlForIE6( $url, $extWhitelist=[])
Returns a variant of $url which will pass isUrlExtensionBad() but has the same GET parameters...
SessionId null $sessionId
Session ID to use for this request.
Definition: WebRequest.php:85
static getGlobalRequestURL()
Return the path and query string portion of the main request URI.
Definition: WebRequest.php:806
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
getText( $name, $default='')
Fetch a text string from the given array or return $default if it&#39;s not set.
Definition: WebRequest.php:618
getValueNames( $exclude=[])
Returns the names of all input values excluding those in $exclude.
Definition: WebRequest.php:652
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
getMethod()
Get the HTTP method used for this request.
Definition: WebRequest.php:723
static overrideRequestId( $id)
Override the unique request ID.
Definition: WebRequest.php:302
Value object holding the session ID in a manner that can be globally updated.
Definition: SessionId.php:38
getRawInput()
Return the raw request body, with no processing.
Definition: WebRequest.php:710
interpolateTitle()
Check for title, action, and/or variant data in the URL and interpolate it into the GET variables...
Definition: WebRequest.php:324
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second redirect
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
getArray( $name, $default=null)
Fetch an array from the input or return $default if it&#39;s not set.
Definition: WebRequest.php:498
getIntOrNull( $name)
Fetch an integer value from the input or return null if empty.
Definition: WebRequest.php:546
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
Definition: WebRequest.php:791
static string $reqId
The unique request ID.
Definition: WebRequest.php:54
getUploadError( $key)
Return the upload error or 0.
Definition: WebRequest.php:951
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:116
getIntArray( $name, $default=null)
Fetch an array of integers, or return $default if it&#39;s not set.
Definition: WebRequest.php:517
getElapsedTime()
Get the number of seconds to have elapsed since request start, in fractional seconds, with microsecond resolution.
Definition: WebRequest.php:263
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:686
getFileTempname( $key)
Return the path to the temporary file where PHP has stored the upload.
Definition: WebRequest.php:940
$matches
appendQueryValue( $key, $value)
Definition: WebRequest.php:882