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  self::$reqId = $_SERVER['UNIQUE_ID'] ?? wfRandomString( 24 );
280  }
281 
282  return self::$reqId;
283  }
284 
292  public static function overrideRequestId( $id ) {
293  self::$reqId = $id;
294  }
295 
300  public function getProtocol() {
301  if ( $this->protocol === null ) {
302  $this->protocol = self::detectProtocol();
303  }
304  return $this->protocol;
305  }
306 
314  public function interpolateTitle() {
315  // T18019: title interpolation on API queries is useless and sometimes harmful
316  if ( defined( 'MW_API' ) ) {
317  return;
318  }
319 
320  $matches = self::getPathInfo( 'title' );
321  foreach ( $matches as $key => $val ) {
322  $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val;
323  }
324  }
325 
336  static function extractTitle( $path, $bases, $key = false ) {
337  foreach ( (array)$bases as $keyValue => $base ) {
338  // Find the part after $wgArticlePath
339  $base = str_replace( '$1', '', $base );
340  $baseLen = strlen( $base );
341  if ( substr( $path, 0, $baseLen ) == $base ) {
342  $raw = substr( $path, $baseLen );
343  if ( $raw !== '' ) {
344  $matches = [ 'title' => rawurldecode( $raw ) ];
345  if ( $key ) {
346  $matches[$key] = $keyValue;
347  }
348  return $matches;
349  }
350  }
351  }
352  return [];
353  }
354 
362  public function normalizeUnicode( $data ) {
363  if ( is_array( $data ) ) {
364  foreach ( $data as $key => $val ) {
365  $data[$key] = $this->normalizeUnicode( $val );
366  }
367  } else {
368  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
369  $data = $contLang ? $contLang->normalize( $data ) :
370  UtfNormal\Validator::cleanUp( $data );
371  }
372  return $data;
373  }
374 
383  private function getGPCVal( $arr, $name, $default ) {
384  # PHP is so nice to not touch input data, except sometimes:
385  # https://secure.php.net/variables.external#language.variables.external.dot-in-names
386  # Work around PHP *feature* to avoid *bugs* elsewhere.
387  $name = strtr( $name, '.', '_' );
388  if ( isset( $arr[$name] ) ) {
389  $data = $arr[$name];
390  if ( isset( $_GET[$name] ) && !is_array( $data ) ) {
391  # Check for alternate/legacy character encoding.
392  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
393  if ( $contLang ) {
394  $data = $contLang->checkTitleEncoding( $data );
395  }
396  }
397  $data = $this->normalizeUnicode( $data );
398  return $data;
399  } else {
400  return $default;
401  }
402  }
403 
416  public function getRawVal( $name, $default = null ) {
417  $name = strtr( $name, '.', '_' ); // See comment in self::getGPCVal()
418  if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
419  $val = $this->data[$name];
420  } else {
421  $val = $default;
422  }
423  if ( is_null( $val ) ) {
424  return $val;
425  } else {
426  return (string)$val;
427  }
428  }
429 
440  public function getVal( $name, $default = null ) {
441  $val = $this->getGPCVal( $this->data, $name, $default );
442  if ( is_array( $val ) ) {
443  $val = $default;
444  }
445  if ( is_null( $val ) ) {
446  return $val;
447  } else {
448  return (string)$val;
449  }
450  }
451 
459  public function setVal( $key, $value ) {
460  $ret = $this->data[$key] ?? null;
461  $this->data[$key] = $value;
462  return $ret;
463  }
464 
471  public function unsetVal( $key ) {
472  if ( !isset( $this->data[$key] ) ) {
473  $ret = null;
474  } else {
475  $ret = $this->data[$key];
476  unset( $this->data[$key] );
477  }
478  return $ret;
479  }
480 
490  public function getArray( $name, $default = null ) {
491  $val = $this->getGPCVal( $this->data, $name, $default );
492  if ( is_null( $val ) ) {
493  return null;
494  } else {
495  return (array)$val;
496  }
497  }
498 
509  public function getIntArray( $name, $default = null ) {
510  $val = $this->getArray( $name, $default );
511  if ( is_array( $val ) ) {
512  $val = array_map( 'intval', $val );
513  }
514  return $val;
515  }
516 
526  public function getInt( $name, $default = 0 ) {
527  return intval( $this->getRawVal( $name, $default ) );
528  }
529 
538  public function getIntOrNull( $name ) {
539  $val = $this->getRawVal( $name );
540  return is_numeric( $val )
541  ? intval( $val )
542  : null;
543  }
544 
555  public function getFloat( $name, $default = 0.0 ) {
556  return floatval( $this->getRawVal( $name, $default ) );
557  }
558 
568  public function getBool( $name, $default = false ) {
569  return (bool)$this->getRawVal( $name, $default );
570  }
571 
581  public function getFuzzyBool( $name, $default = false ) {
582  return $this->getBool( $name, $default )
583  && strcasecmp( $this->getRawVal( $name ), 'false' ) !== 0;
584  }
585 
594  public function getCheck( $name ) {
595  # Checkboxes and buttons are only present when clicked
596  # Presence connotes truth, absence false
597  return $this->getRawVal( $name, null ) !== null;
598  }
599 
610  public function getText( $name, $default = '' ) {
611  $val = $this->getVal( $name, $default );
612  return str_replace( "\r\n", "\n", $val );
613  }
614 
622  public function getValues() {
623  $names = func_get_args();
624  if ( count( $names ) == 0 ) {
625  $names = array_keys( $this->data );
626  }
627 
628  $retVal = [];
629  foreach ( $names as $name ) {
630  $value = $this->getGPCVal( $this->data, $name, null );
631  if ( !is_null( $value ) ) {
632  $retVal[$name] = $value;
633  }
634  }
635  return $retVal;
636  }
637 
644  public function getValueNames( $exclude = [] ) {
645  return array_diff( array_keys( $this->getValues() ), $exclude );
646  }
647 
655  public function getQueryValues() {
656  return $_GET;
657  }
658 
667  public function getPostValues() {
668  return $_POST;
669  }
670 
678  public function getRawQueryString() {
679  return $_SERVER['QUERY_STRING'];
680  }
681 
688  public function getRawPostString() {
689  if ( !$this->wasPosted() ) {
690  return '';
691  }
692  return $this->getRawInput();
693  }
694 
702  public function getRawInput() {
703  static $input = null;
704  if ( $input === null ) {
705  $input = file_get_contents( 'php://input' );
706  }
707  return $input;
708  }
709 
715  public function getMethod() {
716  return $_SERVER['REQUEST_METHOD'] ?? 'GET';
717  }
718 
728  public function wasPosted() {
729  return $this->getMethod() == 'POST';
730  }
731 
742  public function getSession() {
743  if ( $this->sessionId !== null ) {
744  $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
745  if ( $session ) {
746  return $session;
747  }
748  }
749 
750  $session = SessionManager::singleton()->getSessionForRequest( $this );
751  $this->sessionId = $session->getSessionId();
752  return $session;
753  }
754 
761  public function setSessionId( SessionId $sessionId ) {
762  $this->sessionId = $sessionId;
763  }
764 
771  public function getSessionId() {
772  return $this->sessionId;
773  }
774 
783  public function getCookie( $key, $prefix = null, $default = null ) {
784  if ( $prefix === null ) {
785  global $wgCookiePrefix;
786  $prefix = $wgCookiePrefix;
787  }
788  return $this->getGPCVal( $_COOKIE, $prefix . $key, $default );
789  }
790 
798  public static function getGlobalRequestURL() {
799  // This method is called on fatal errors; it should not depend on anything complex.
800 
801  if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
802  $base = $_SERVER['REQUEST_URI'];
803  } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] )
804  && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] )
805  ) {
806  // Probably IIS; doesn't set REQUEST_URI
807  $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
808  } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
809  $base = $_SERVER['SCRIPT_NAME'];
810  if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
811  $base .= '?' . $_SERVER['QUERY_STRING'];
812  }
813  } else {
814  // This shouldn't happen!
815  throw new MWException( "Web server doesn't provide either " .
816  "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
817  "of your web server configuration to https://phabricator.wikimedia.org/" );
818  }
819  // User-agents should not send a fragment with the URI, but
820  // if they do, and the web server passes it on to us, we
821  // need to strip it or we get false-positive redirect loops
822  // or weird output URLs
823  $hash = strpos( $base, '#' );
824  if ( $hash !== false ) {
825  $base = substr( $base, 0, $hash );
826  }
827 
828  if ( $base[0] == '/' ) {
829  // More than one slash will look like it is protocol relative
830  return preg_replace( '!^/+!', '/', $base );
831  } else {
832  // We may get paths with a host prepended; strip it.
833  return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base );
834  }
835  }
836 
844  public function getRequestURL() {
845  return self::getGlobalRequestURL();
846  }
847 
858  public function getFullRequestURL() {
859  return wfGetServerUrl( PROTO_CURRENT ) . $this->getRequestURL();
860  }
861 
867  public function appendQueryValue( $key, $value ) {
868  return $this->appendQueryArray( [ $key => $value ] );
869  }
870 
877  public function appendQueryArray( $array ) {
878  $newquery = $this->getQueryValues();
879  unset( $newquery['title'] );
880  $newquery = array_merge( $newquery, $array );
881 
882  return wfArrayToCgi( $newquery );
883  }
884 
894  public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
895  global $wgUser;
896 
897  $limit = $this->getInt( 'limit', 0 );
898  if ( $limit < 0 ) {
899  $limit = 0;
900  }
901  if ( ( $limit == 0 ) && ( $optionname != '' ) ) {
902  $limit = $wgUser->getIntOption( $optionname );
903  }
904  if ( $limit <= 0 ) {
905  $limit = $deflimit;
906  }
907  if ( $limit > 5000 ) {
908  $limit = 5000; # We have *some* limits...
909  }
910 
911  $offset = $this->getInt( 'offset', 0 );
912  if ( $offset < 0 ) {
913  $offset = 0;
914  }
915 
916  return [ $limit, $offset ];
917  }
918 
925  public function getFileTempname( $key ) {
926  $file = new WebRequestUpload( $this, $key );
927  return $file->getTempName();
928  }
929 
936  public function getUploadError( $key ) {
937  $file = new WebRequestUpload( $this, $key );
938  return $file->getError();
939  }
940 
952  public function getFileName( $key ) {
953  $file = new WebRequestUpload( $this, $key );
954  return $file->getName();
955  }
956 
963  public function getUpload( $key ) {
964  return new WebRequestUpload( $this, $key );
965  }
966 
973  public function response() {
974  /* Lazy initialization of response object for this request */
975  if ( !is_object( $this->response ) ) {
976  $class = ( $this instanceof FauxRequest ) ? FauxResponse::class : WebResponse::class;
977  $this->response = new $class();
978  }
979  return $this->response;
980  }
981 
985  protected function initHeaders() {
986  if ( count( $this->headers ) ) {
987  return;
988  }
989 
990  $apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false;
991  if ( $apacheHeaders ) {
992  foreach ( $apacheHeaders as $tempName => $tempValue ) {
993  $this->headers[strtoupper( $tempName )] = $tempValue;
994  }
995  } else {
996  foreach ( $_SERVER as $name => $value ) {
997  if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
998  $name = str_replace( '_', '-', substr( $name, 5 ) );
999  $this->headers[$name] = $value;
1000  } elseif ( $name === 'CONTENT_LENGTH' ) {
1001  $this->headers['CONTENT-LENGTH'] = $value;
1002  }
1003  }
1004  }
1005  }
1006 
1012  public function getAllHeaders() {
1013  $this->initHeaders();
1014  return $this->headers;
1015  }
1016 
1029  public function getHeader( $name, $flags = 0 ) {
1030  $this->initHeaders();
1031  $name = strtoupper( $name );
1032  if ( !isset( $this->headers[$name] ) ) {
1033  return false;
1034  }
1035  $value = $this->headers[$name];
1036  if ( $flags & self::GETHEADER_LIST ) {
1037  $value = array_map( 'trim', explode( ',', $value ) );
1038  }
1039  return $value;
1040  }
1041 
1049  public function getSessionData( $key ) {
1050  return $this->getSession()->get( $key );
1051  }
1052 
1060  public function setSessionData( $key, $data ) {
1061  $this->getSession()->set( $key, $data );
1062  }
1063 
1074  public function checkUrlExtension( $extWhitelist = [] ) {
1075  $extWhitelist[] = 'php';
1076  if ( IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ) ) {
1077  if ( !$this->wasPosted() ) {
1078  $newUrl = IEUrlExtension::fixUrlForIE6(
1079  $this->getFullRequestURL(), $extWhitelist );
1080  if ( $newUrl !== false ) {
1081  $this->doSecurityRedirect( $newUrl );
1082  return false;
1083  }
1084  }
1085  throw new HttpError( 403,
1086  'Invalid file extension found in the path info or query string.' );
1087  }
1088  return true;
1089  }
1090 
1098  protected function doSecurityRedirect( $url ) {
1099  header( 'Location: ' . $url );
1100  header( 'Content-Type: text/html' );
1101  $encUrl = htmlspecialchars( $url );
1102  echo <<<HTML
1103 <!DOCTYPE html>
1104 <html>
1105 <head>
1106 <title>Security redirect</title>
1107 </head>
1108 <body>
1109 <h1>Security redirect</h1>
1110 <p>
1111 We can't serve non-HTML content from the URL you have requested, because
1112 Internet Explorer would interpret it as an incorrect and potentially dangerous
1113 content type.</p>
1114 <p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the
1115 URL you have requested, except that "&amp;*" is appended. This prevents Internet
1116 Explorer from seeing a bogus file extension.
1117 </p>
1118 </body>
1119 </html>
1120 HTML;
1121  echo "\n";
1122  return true;
1123  }
1124 
1134  public function getAcceptLang() {
1135  // Modified version of code found at
1136  // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1137  $acceptLang = $this->getHeader( 'Accept-Language' );
1138  if ( !$acceptLang ) {
1139  return [];
1140  }
1141 
1142  // Return the language codes in lower case
1143  $acceptLang = strtolower( $acceptLang );
1144 
1145  // Break up string into pieces (languages and q factors)
1146  $lang_parse = null;
1147  preg_match_all(
1148  '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/',
1149  $acceptLang,
1150  $lang_parse
1151  );
1152 
1153  if ( !count( $lang_parse[1] ) ) {
1154  return [];
1155  }
1156 
1157  $langcodes = $lang_parse[1];
1158  $qvalues = $lang_parse[4];
1159  $indices = range( 0, count( $lang_parse[1] ) - 1 );
1160 
1161  // Set default q factor to 1
1162  foreach ( $indices as $index ) {
1163  if ( $qvalues[$index] === '' ) {
1164  $qvalues[$index] = 1;
1165  } elseif ( $qvalues[$index] == 0 ) {
1166  unset( $langcodes[$index], $qvalues[$index], $indices[$index] );
1167  }
1168  }
1169 
1170  // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way
1171  array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes );
1172 
1173  // Create a list like "en" => 0.8
1174  $langs = array_combine( $langcodes, $qvalues );
1175 
1176  return $langs;
1177  }
1178 
1187  protected function getRawIP() {
1188  if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) {
1189  return null;
1190  }
1191 
1192  if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) {
1193  throw new MWException( __METHOD__
1194  . " : Could not determine the remote IP address due to multiple values." );
1195  } else {
1196  $ipchain = $_SERVER['REMOTE_ADDR'];
1197  }
1198 
1199  return IP::canonicalize( $ipchain );
1200  }
1201 
1211  public function getIP() {
1212  global $wgUsePrivateIPs;
1213 
1214  # Return cached result
1215  if ( $this->ip !== null ) {
1216  return $this->ip;
1217  }
1218 
1219  # collect the originating ips
1220  $ip = $this->getRawIP();
1221  if ( !$ip ) {
1222  throw new MWException( 'Unable to determine IP.' );
1223  }
1224 
1225  # Append XFF
1226  $forwardedFor = $this->getHeader( 'X-Forwarded-For' );
1227  if ( $forwardedFor !== false ) {
1228  $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
1229  $isConfigured = $proxyLookup->isConfiguredProxy( $ip );
1230  $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) );
1231  $ipchain = array_reverse( $ipchain );
1232  array_unshift( $ipchain, $ip );
1233 
1234  # Step through XFF list and find the last address in the list which is a
1235  # trusted server. Set $ip to the IP address given by that trusted server,
1236  # unless the address is not sensible (e.g. private). However, prefer private
1237  # IP addresses over proxy servers controlled by this site (more sensible).
1238  # Note that some XFF values might be "unknown" with Squid/Varnish.
1239  foreach ( $ipchain as $i => $curIP ) {
1240  $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) );
1241  if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown'
1242  || !$proxyLookup->isTrustedProxy( $curIP )
1243  ) {
1244  break; // IP is not valid/trusted or does not point to anything
1245  }
1246  if (
1247  IP::isPublic( $ipchain[$i + 1] ) ||
1248  $wgUsePrivateIPs ||
1249  $proxyLookup->isConfiguredProxy( $curIP ) // T50919; treat IP as sane
1250  ) {
1251  // Follow the next IP according to the proxy
1252  $nextIP = IP::canonicalize( $ipchain[$i + 1] );
1253  if ( !$nextIP && $isConfigured ) {
1254  // We have not yet made it past CDN/proxy servers of this site,
1255  // so either they are misconfigured or there is some IP spoofing.
1256  throw new MWException( "Invalid IP given in XFF '$forwardedFor'." );
1257  }
1258  $ip = $nextIP;
1259  // keep traversing the chain
1260  continue;
1261  }
1262  break;
1263  }
1264  }
1265 
1266  # Allow extensions to improve our guess
1267  Hooks::run( 'GetIP', [ &$ip ] );
1268 
1269  if ( !$ip ) {
1270  throw new MWException( "Unable to determine IP." );
1271  }
1272 
1273  wfDebug( "IP: $ip\n" );
1274  $this->ip = $ip;
1275  return $ip;
1276  }
1277 
1283  public function setIP( $ip ) {
1284  $this->ip = $ip;
1285  }
1286 
1299  public function hasSafeMethod() {
1300  if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
1301  return false; // CLI mode
1302  }
1303 
1304  return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
1305  }
1306 
1325  public function isSafeRequest() {
1326  if ( $this->markedAsSafe && $this->wasPosted() ) {
1327  return true; // marked as a "safe" POST
1328  }
1329 
1330  return $this->hasSafeMethod();
1331  }
1332 
1343  public function markAsSafeRequest() {
1344  $this->markedAsSafe = true;
1345  }
1346 }
getRawVal( $name, $default=null)
Fetch a scalar from the input without normalization, or return $default if it&#39;s not set...
Definition: WebRequest.php:416
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
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:667
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:526
getRawPostString()
Return the contents of the POST with no decoding.
Definition: WebRequest.php:688
$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:858
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:1997
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:440
$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:459
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff...
Definition: WebRequest.php:973
unsetVal( $key)
Unset an arbitrary value from our get/post data.
Definition: WebRequest.php:471
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:315
$value
const PROTO_CURRENT
Definition: Defines.php:222
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
getFileName( $key)
Return the original filename of the uploaded file, as reported by the submitting user agent...
Definition: WebRequest.php:952
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:742
getFloat( $name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
Definition: WebRequest.php:555
wfGetServerUrl( $proto)
Get the wiki&#39;s "server", i.e.
$wgArticlePath
Definition: img_auth.php:46
initHeaders()
Initialise the header list.
Definition: WebRequest.php:985
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:362
getBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:568
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
Definition: WebRequest.php:594
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:336
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:894
getSessionData( $key)
Get data from the session.
getProtocol()
Get the current URL protocol (http or https)
Definition: WebRequest.php:300
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:728
getRequestURL()
Return the path and query string portion of the request URI.
Definition: WebRequest.php:844
PathRouter class.
Definition: PathRouter.php:73
getQueryValues()
Get the values passed in the query string.
Definition: WebRequest.php:655
appendQueryArray( $array)
Appends or replaces value of query variables.
Definition: WebRequest.php:877
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...
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:266
getFuzzyBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:581
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:383
getSessionId()
Get the session id for this request, if any.
Definition: WebRequest.php:771
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:963
getValues()
Extracts the given named values into an array.
Definition: WebRequest.php:622
setSessionId(SessionId $sessionId)
Set the session for this request.
Definition: WebRequest.php:761
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:798
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:610
getValueNames( $exclude=[])
Returns the names of all input values excluding those in $exclude.
Definition: WebRequest.php:644
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:715
static overrideRequestId( $id)
Override the unique request ID.
Definition: WebRequest.php:292
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:702
interpolateTitle()
Check for title, action, and/or variant data in the URL and interpolate it into the GET variables...
Definition: WebRequest.php:314
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:276
getArray( $name, $default=null)
Fetch an array from the input or return $default if it&#39;s not set.
Definition: WebRequest.php:490
getIntOrNull( $name)
Fetch an integer value from the input or return null if empty.
Definition: WebRequest.php:538
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
Definition: WebRequest.php:783
static string $reqId
The unique request ID.
Definition: WebRequest.php:54
getUploadError( $key)
Return the upload error or 0.
Definition: WebRequest.php:936
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:509
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:160
$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:678
getFileTempname( $key)
Return the path to the temporary file where PHP has stored the upload.
Definition: WebRequest.php:925
$matches
appendQueryValue( $key, $value)
Definition: WebRequest.php:867