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://www.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_string( $data ) ) {
391  # Check for alternate/legacy character encoding.
392  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
393  $data = $contLang->checkTitleEncoding( $data );
394  }
395  $data = $this->normalizeUnicode( $data );
396  return $data;
397  } else {
398  return $default;
399  }
400  }
401 
414  public function getRawVal( $name, $default = null ) {
415  $name = strtr( $name, '.', '_' ); // See comment in self::getGPCVal()
416  if ( isset( $this->data[$name] ) && !is_array( $this->data[$name] ) ) {
417  $val = $this->data[$name];
418  } else {
419  $val = $default;
420  }
421  if ( is_null( $val ) ) {
422  return $val;
423  } else {
424  return (string)$val;
425  }
426  }
427 
438  public function getVal( $name, $default = null ) {
439  $val = $this->getGPCVal( $this->data, $name, $default );
440  if ( is_array( $val ) ) {
441  $val = $default;
442  }
443  if ( is_null( $val ) ) {
444  return $val;
445  } else {
446  return (string)$val;
447  }
448  }
449 
457  public function setVal( $key, $value ) {
458  $ret = $this->data[$key] ?? null;
459  $this->data[$key] = $value;
460  return $ret;
461  }
462 
469  public function unsetVal( $key ) {
470  if ( !isset( $this->data[$key] ) ) {
471  $ret = null;
472  } else {
473  $ret = $this->data[$key];
474  unset( $this->data[$key] );
475  }
476  return $ret;
477  }
478 
488  public function getArray( $name, $default = null ) {
489  $val = $this->getGPCVal( $this->data, $name, $default );
490  if ( is_null( $val ) ) {
491  return null;
492  } else {
493  return (array)$val;
494  }
495  }
496 
507  public function getIntArray( $name, $default = null ) {
508  $val = $this->getArray( $name, $default );
509  if ( is_array( $val ) ) {
510  $val = array_map( 'intval', $val );
511  }
512  return $val;
513  }
514 
524  public function getInt( $name, $default = 0 ) {
525  return intval( $this->getRawVal( $name, $default ) );
526  }
527 
536  public function getIntOrNull( $name ) {
537  $val = $this->getRawVal( $name );
538  return is_numeric( $val )
539  ? intval( $val )
540  : null;
541  }
542 
553  public function getFloat( $name, $default = 0.0 ) {
554  return floatval( $this->getRawVal( $name, $default ) );
555  }
556 
566  public function getBool( $name, $default = false ) {
567  return (bool)$this->getRawVal( $name, $default );
568  }
569 
579  public function getFuzzyBool( $name, $default = false ) {
580  return $this->getBool( $name, $default )
581  && strcasecmp( $this->getRawVal( $name ), 'false' ) !== 0;
582  }
583 
592  public function getCheck( $name ) {
593  # Checkboxes and buttons are only present when clicked
594  # Presence connotes truth, absence false
595  return $this->getRawVal( $name, null ) !== null;
596  }
597 
608  public function getText( $name, $default = '' ) {
609  $val = $this->getVal( $name, $default );
610  return str_replace( "\r\n", "\n", $val );
611  }
612 
620  public function getValues() {
621  $names = func_get_args();
622  if ( count( $names ) == 0 ) {
623  $names = array_keys( $this->data );
624  }
625 
626  $retVal = [];
627  foreach ( $names as $name ) {
628  $value = $this->getGPCVal( $this->data, $name, null );
629  if ( !is_null( $value ) ) {
630  $retVal[$name] = $value;
631  }
632  }
633  return $retVal;
634  }
635 
642  public function getValueNames( $exclude = [] ) {
643  return array_diff( array_keys( $this->getValues() ), $exclude );
644  }
645 
653  public function getQueryValues() {
654  return $_GET;
655  }
656 
665  public function getPostValues() {
666  return $_POST;
667  }
668 
676  public function getRawQueryString() {
677  return $_SERVER['QUERY_STRING'];
678  }
679 
686  public function getRawPostString() {
687  if ( !$this->wasPosted() ) {
688  return '';
689  }
690  return $this->getRawInput();
691  }
692 
700  public function getRawInput() {
701  static $input = null;
702  if ( $input === null ) {
703  $input = file_get_contents( 'php://input' );
704  }
705  return $input;
706  }
707 
713  public function getMethod() {
714  return $_SERVER['REQUEST_METHOD'] ?? 'GET';
715  }
716 
726  public function wasPosted() {
727  return $this->getMethod() == 'POST';
728  }
729 
740  public function getSession() {
741  if ( $this->sessionId !== null ) {
742  $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
743  if ( $session ) {
744  return $session;
745  }
746  }
747 
748  $session = SessionManager::singleton()->getSessionForRequest( $this );
749  $this->sessionId = $session->getSessionId();
750  return $session;
751  }
752 
759  public function setSessionId( SessionId $sessionId ) {
760  $this->sessionId = $sessionId;
761  }
762 
769  public function getSessionId() {
770  return $this->sessionId;
771  }
772 
781  public function getCookie( $key, $prefix = null, $default = null ) {
782  if ( $prefix === null ) {
783  global $wgCookiePrefix;
784  $prefix = $wgCookiePrefix;
785  }
786  return $this->getGPCVal( $_COOKIE, $prefix . $key, $default );
787  }
788 
796  public static function getGlobalRequestURL() {
797  // This method is called on fatal errors; it should not depend on anything complex.
798 
799  if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
800  $base = $_SERVER['REQUEST_URI'];
801  } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] )
802  && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] )
803  ) {
804  // Probably IIS; doesn't set REQUEST_URI
805  $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
806  } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
807  $base = $_SERVER['SCRIPT_NAME'];
808  if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
809  $base .= '?' . $_SERVER['QUERY_STRING'];
810  }
811  } else {
812  // This shouldn't happen!
813  throw new MWException( "Web server doesn't provide either " .
814  "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
815  "of your web server configuration to https://phabricator.wikimedia.org/" );
816  }
817  // User-agents should not send a fragment with the URI, but
818  // if they do, and the web server passes it on to us, we
819  // need to strip it or we get false-positive redirect loops
820  // or weird output URLs
821  $hash = strpos( $base, '#' );
822  if ( $hash !== false ) {
823  $base = substr( $base, 0, $hash );
824  }
825 
826  if ( $base[0] == '/' ) {
827  // More than one slash will look like it is protocol relative
828  return preg_replace( '!^/+!', '/', $base );
829  } else {
830  // We may get paths with a host prepended; strip it.
831  return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base );
832  }
833  }
834 
842  public function getRequestURL() {
843  return self::getGlobalRequestURL();
844  }
845 
856  public function getFullRequestURL() {
857  return wfGetServerUrl( PROTO_CURRENT ) . $this->getRequestURL();
858  }
859 
865  public function appendQueryValue( $key, $value ) {
866  return $this->appendQueryArray( [ $key => $value ] );
867  }
868 
875  public function appendQueryArray( $array ) {
876  $newquery = $this->getQueryValues();
877  unset( $newquery['title'] );
878  $newquery = array_merge( $newquery, $array );
879 
880  return wfArrayToCgi( $newquery );
881  }
882 
892  public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
893  global $wgUser;
894 
895  $limit = $this->getInt( 'limit', 0 );
896  if ( $limit < 0 ) {
897  $limit = 0;
898  }
899  if ( ( $limit == 0 ) && ( $optionname != '' ) ) {
900  $limit = $wgUser->getIntOption( $optionname );
901  }
902  if ( $limit <= 0 ) {
903  $limit = $deflimit;
904  }
905  if ( $limit > 5000 ) {
906  $limit = 5000; # We have *some* limits...
907  }
908 
909  $offset = $this->getInt( 'offset', 0 );
910  if ( $offset < 0 ) {
911  $offset = 0;
912  }
913 
914  return [ $limit, $offset ];
915  }
916 
923  public function getFileTempname( $key ) {
924  $file = new WebRequestUpload( $this, $key );
925  return $file->getTempName();
926  }
927 
934  public function getUploadError( $key ) {
935  $file = new WebRequestUpload( $this, $key );
936  return $file->getError();
937  }
938 
950  public function getFileName( $key ) {
951  $file = new WebRequestUpload( $this, $key );
952  return $file->getName();
953  }
954 
961  public function getUpload( $key ) {
962  return new WebRequestUpload( $this, $key );
963  }
964 
971  public function response() {
972  /* Lazy initialization of response object for this request */
973  if ( !is_object( $this->response ) ) {
974  $class = ( $this instanceof FauxRequest ) ? FauxResponse::class : WebResponse::class;
975  $this->response = new $class();
976  }
977  return $this->response;
978  }
979 
983  protected function initHeaders() {
984  if ( count( $this->headers ) ) {
985  return;
986  }
987 
988  $apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false;
989  if ( $apacheHeaders ) {
990  foreach ( $apacheHeaders as $tempName => $tempValue ) {
991  $this->headers[strtoupper( $tempName )] = $tempValue;
992  }
993  } else {
994  foreach ( $_SERVER as $name => $value ) {
995  if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
996  $name = str_replace( '_', '-', substr( $name, 5 ) );
997  $this->headers[$name] = $value;
998  } elseif ( $name === 'CONTENT_LENGTH' ) {
999  $this->headers['CONTENT-LENGTH'] = $value;
1000  }
1001  }
1002  }
1003  }
1004 
1010  public function getAllHeaders() {
1011  $this->initHeaders();
1012  return $this->headers;
1013  }
1014 
1027  public function getHeader( $name, $flags = 0 ) {
1028  $this->initHeaders();
1029  $name = strtoupper( $name );
1030  if ( !isset( $this->headers[$name] ) ) {
1031  return false;
1032  }
1033  $value = $this->headers[$name];
1034  if ( $flags & self::GETHEADER_LIST ) {
1035  $value = array_map( 'trim', explode( ',', $value ) );
1036  }
1037  return $value;
1038  }
1039 
1047  public function getSessionData( $key ) {
1048  return $this->getSession()->get( $key );
1049  }
1050 
1058  public function setSessionData( $key, $data ) {
1059  $this->getSession()->set( $key, $data );
1060  }
1061 
1072  public function checkUrlExtension( $extWhitelist = [] ) {
1073  $extWhitelist[] = 'php';
1074  if ( IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ) ) {
1075  if ( !$this->wasPosted() ) {
1076  $newUrl = IEUrlExtension::fixUrlForIE6(
1077  $this->getFullRequestURL(), $extWhitelist );
1078  if ( $newUrl !== false ) {
1079  $this->doSecurityRedirect( $newUrl );
1080  return false;
1081  }
1082  }
1083  throw new HttpError( 403,
1084  'Invalid file extension found in the path info or query string.' );
1085  }
1086  return true;
1087  }
1088 
1096  protected function doSecurityRedirect( $url ) {
1097  header( 'Location: ' . $url );
1098  header( 'Content-Type: text/html' );
1099  $encUrl = htmlspecialchars( $url );
1100  echo <<<HTML
1101 <!DOCTYPE html>
1102 <html>
1103 <head>
1104 <title>Security redirect</title>
1105 </head>
1106 <body>
1107 <h1>Security redirect</h1>
1108 <p>
1109 We can't serve non-HTML content from the URL you have requested, because
1110 Internet Explorer would interpret it as an incorrect and potentially dangerous
1111 content type.</p>
1112 <p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the
1113 URL you have requested, except that "&amp;*" is appended. This prevents Internet
1114 Explorer from seeing a bogus file extension.
1115 </p>
1116 </body>
1117 </html>
1118 HTML;
1119  echo "\n";
1120  return true;
1121  }
1122 
1132  public function getAcceptLang() {
1133  // Modified version of code found at
1134  // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1135  $acceptLang = $this->getHeader( 'Accept-Language' );
1136  if ( !$acceptLang ) {
1137  return [];
1138  }
1139 
1140  // Return the language codes in lower case
1141  $acceptLang = strtolower( $acceptLang );
1142 
1143  // Break up string into pieces (languages and q factors)
1144  $lang_parse = null;
1145  preg_match_all(
1146  '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/',
1147  $acceptLang,
1148  $lang_parse
1149  );
1150 
1151  if ( !count( $lang_parse[1] ) ) {
1152  return [];
1153  }
1154 
1155  $langcodes = $lang_parse[1];
1156  $qvalues = $lang_parse[4];
1157  $indices = range( 0, count( $lang_parse[1] ) - 1 );
1158 
1159  // Set default q factor to 1
1160  foreach ( $indices as $index ) {
1161  if ( $qvalues[$index] === '' ) {
1162  $qvalues[$index] = 1;
1163  } elseif ( $qvalues[$index] == 0 ) {
1164  unset( $langcodes[$index], $qvalues[$index], $indices[$index] );
1165  }
1166  }
1167 
1168  // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way
1169  array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes );
1170 
1171  // Create a list like "en" => 0.8
1172  $langs = array_combine( $langcodes, $qvalues );
1173 
1174  return $langs;
1175  }
1176 
1185  protected function getRawIP() {
1186  if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) {
1187  return null;
1188  }
1189 
1190  if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) {
1191  throw new MWException( __METHOD__
1192  . " : Could not determine the remote IP address due to multiple values." );
1193  } else {
1194  $ipchain = $_SERVER['REMOTE_ADDR'];
1195  }
1196 
1197  return IP::canonicalize( $ipchain );
1198  }
1199 
1209  public function getIP() {
1210  global $wgUsePrivateIPs;
1211 
1212  # Return cached result
1213  if ( $this->ip !== null ) {
1214  return $this->ip;
1215  }
1216 
1217  # collect the originating ips
1218  $ip = $this->getRawIP();
1219  if ( !$ip ) {
1220  throw new MWException( 'Unable to determine IP.' );
1221  }
1222 
1223  # Append XFF
1224  $forwardedFor = $this->getHeader( 'X-Forwarded-For' );
1225  if ( $forwardedFor !== false ) {
1226  $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
1227  $isConfigured = $proxyLookup->isConfiguredProxy( $ip );
1228  $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) );
1229  $ipchain = array_reverse( $ipchain );
1230  array_unshift( $ipchain, $ip );
1231 
1232  # Step through XFF list and find the last address in the list which is a
1233  # trusted server. Set $ip to the IP address given by that trusted server,
1234  # unless the address is not sensible (e.g. private). However, prefer private
1235  # IP addresses over proxy servers controlled by this site (more sensible).
1236  # Note that some XFF values might be "unknown" with Squid/Varnish.
1237  foreach ( $ipchain as $i => $curIP ) {
1238  $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) );
1239  if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown'
1240  || !$proxyLookup->isTrustedProxy( $curIP )
1241  ) {
1242  break; // IP is not valid/trusted or does not point to anything
1243  }
1244  if (
1245  IP::isPublic( $ipchain[$i + 1] ) ||
1246  $wgUsePrivateIPs ||
1247  $proxyLookup->isConfiguredProxy( $curIP ) // T50919; treat IP as sane
1248  ) {
1249  // Follow the next IP according to the proxy
1250  $nextIP = IP::canonicalize( $ipchain[$i + 1] );
1251  if ( !$nextIP && $isConfigured ) {
1252  // We have not yet made it past CDN/proxy servers of this site,
1253  // so either they are misconfigured or there is some IP spoofing.
1254  throw new MWException( "Invalid IP given in XFF '$forwardedFor'." );
1255  }
1256  $ip = $nextIP;
1257  // keep traversing the chain
1258  continue;
1259  }
1260  break;
1261  }
1262  }
1263 
1264  # Allow extensions to improve our guess
1265  Hooks::run( 'GetIP', [ &$ip ] );
1266 
1267  if ( !$ip ) {
1268  throw new MWException( "Unable to determine IP." );
1269  }
1270 
1271  wfDebug( "IP: $ip\n" );
1272  $this->ip = $ip;
1273  return $ip;
1274  }
1275 
1281  public function setIP( $ip ) {
1282  $this->ip = $ip;
1283  }
1284 
1297  public function hasSafeMethod() {
1298  if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
1299  return false; // CLI mode
1300  }
1301 
1302  return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
1303  }
1304 
1323  public function isSafeRequest() {
1324  if ( $this->markedAsSafe && $this->wasPosted() ) {
1325  return true; // marked as a "safe" POST
1326  }
1327 
1328  return $this->hasSafeMethod();
1329  }
1330 
1341  public function markAsSafeRequest() {
1342  $this->markedAsSafe = true;
1343  }
1344 }
getRawVal( $name, $default=null)
Fetch a scalar from the input without normalization, or return $default if it&#39;s not set...
Definition: WebRequest.php:414
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:665
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:524
getRawPostString()
Return the contents of the POST with no decoding.
Definition: WebRequest.php:686
$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:856
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:1982
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:438
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:457
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff...
Definition: WebRequest.php:971
unsetVal( $key)
Unset an arbitrary value from our get/post data.
Definition: WebRequest.php:469
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:950
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:740
getFloat( $name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
Definition: WebRequest.php:553
wfGetServerUrl( $proto)
Get the wiki&#39;s "server", i.e.
$wgArticlePath
Definition: img_auth.php:46
initHeaders()
Initialise the header list.
Definition: WebRequest.php:983
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:566
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
Definition: WebRequest.php:592
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:892
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:726
getRequestURL()
Return the path and query string portion of the request URI.
Definition: WebRequest.php:842
PathRouter class.
Definition: PathRouter.php:73
getQueryValues()
Get the values passed in the query string.
Definition: WebRequest.php:653
appendQueryArray( $array)
Appends or replaces value of query variables.
Definition: WebRequest.php:875
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:780
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:579
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:769
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:961
getValues()
Extracts the given named values into an array.
Definition: WebRequest.php:620
setSessionId(SessionId $sessionId)
Set the session for this request.
Definition: WebRequest.php:759
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:796
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:608
getValueNames( $exclude=[])
Returns the names of all input values excluding those in $exclude.
Definition: WebRequest.php:642
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:713
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:700
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:271
getArray( $name, $default=null)
Fetch an array from the input or return $default if it&#39;s not set.
Definition: WebRequest.php:488
getIntOrNull( $name)
Fetch an integer value from the input or return null if empty.
Definition: WebRequest.php:536
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
Definition: WebRequest.php:781
static string $reqId
The unique request ID.
Definition: WebRequest.php:54
getUploadError( $key)
Return the upload error or 0.
Definition: WebRequest.php:934
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:507
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:676
getFileTempname( $key)
Return the path to the temporary file where PHP has stored the upload.
Definition: WebRequest.php:923
$matches
appendQueryValue( $key, $value)
Definition: WebRequest.php:865