MediaWiki  REL1_31
WebRequest.php
Go to the documentation of this file.
1 <?php
30 
38 class WebRequest {
39  protected $data, $headers = [];
40 
45  const GETHEADER_LIST = 1;
46 
51  private static $reqId;
52 
57  private $response;
58 
63  private $ip;
64 
69  protected $requestTime;
70 
75  protected $protocol;
76 
82  protected $sessionId = null;
83 
85  protected $markedAsSafe = false;
86 
90  public function __construct() {
91  $this->requestTime = $_SERVER['REQUEST_TIME_FLOAT'];
92 
93  // POST overrides GET data
94  // We don't use $_REQUEST here to avoid interference from cookies...
95  $this->data = $_POST + $_GET;
96  }
97 
113  public static function getPathInfo( $want = 'all' ) {
115  // PATH_INFO is mangled due to https://bugs.php.net/bug.php?id=31892
116  // And also by Apache 2.x, double slashes are converted to single slashes.
117  // So we will use REQUEST_URI if possible.
118  $matches = [];
119  if ( !empty( $_SERVER['REQUEST_URI'] ) ) {
120  // Slurp out the path portion to examine...
121  $url = $_SERVER['REQUEST_URI'];
122  if ( !preg_match( '!^https?://!', $url ) ) {
123  $url = 'http://unused' . $url;
124  }
125  Wikimedia\suppressWarnings();
126  $a = parse_url( $url );
127  Wikimedia\restoreWarnings();
128  if ( $a ) {
129  $path = isset( $a['path'] ) ? $a['path'] : '';
130 
132  if ( $path == $wgScript && $want !== 'all' ) {
133  // Script inside a rewrite path?
134  // Abort to keep from breaking...
135  return $matches;
136  }
137 
138  $router = new PathRouter;
139 
140  // Raw PATH_INFO style
141  $router->add( "$wgScript/$1" );
142 
143  if ( isset( $_SERVER['SCRIPT_NAME'] )
144  && preg_match( '/\.php/', $_SERVER['SCRIPT_NAME'] )
145  ) {
146  # Check for SCRIPT_NAME, we handle index.php explicitly
147  # But we do have some other .php files such as img_auth.php
148  # Don't let root article paths clober the parsing for them
149  $router->add( $_SERVER['SCRIPT_NAME'] . "/$1" );
150  }
151 
153  if ( $wgArticlePath ) {
154  $router->add( $wgArticlePath );
155  }
156 
158  if ( $wgActionPaths ) {
159  $router->add( $wgActionPaths, [ 'action' => '$key' ] );
160  }
161 
163  if ( $wgVariantArticlePath ) {
164  $router->add( $wgVariantArticlePath,
165  [ 'variant' => '$2' ],
166  [ '$2' => $wgContLang->getVariants() ]
167  );
168  }
169 
170  Hooks::run( 'WebRequestPathInfoRouter', [ $router ] );
171 
172  $matches = $router->parse( $path );
173  }
174  } elseif ( $wgUsePathInfo ) {
175  if ( isset( $_SERVER['ORIG_PATH_INFO'] ) && $_SERVER['ORIG_PATH_INFO'] != '' ) {
176  // Mangled PATH_INFO
177  // https://bugs.php.net/bug.php?id=31892
178  // Also reported when ini_get('cgi.fix_pathinfo')==false
179  $matches['title'] = substr( $_SERVER['ORIG_PATH_INFO'], 1 );
180 
181  } elseif ( isset( $_SERVER['PATH_INFO'] ) && $_SERVER['PATH_INFO'] != '' ) {
182  // Regular old PATH_INFO yay
183  $matches['title'] = substr( $_SERVER['PATH_INFO'], 1 );
184  }
185  }
186 
187  return $matches;
188  }
189 
196  public static function detectServer() {
198 
199  $proto = self::detectProtocol();
200  $stdPort = $proto === 'https' ? 443 : 80;
201 
202  $varNames = [ 'HTTP_HOST', 'SERVER_NAME', 'HOSTNAME', 'SERVER_ADDR' ];
203  $host = 'localhost';
204  $port = $stdPort;
205  foreach ( $varNames as $varName ) {
206  if ( !isset( $_SERVER[$varName] ) ) {
207  continue;
208  }
209 
210  $parts = IP::splitHostAndPort( $_SERVER[$varName] );
211  if ( !$parts ) {
212  // Invalid, do not use
213  continue;
214  }
215 
216  $host = $parts[0];
217  if ( $wgAssumeProxiesUseDefaultProtocolPorts && isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
218  // T72021: Assume that upstream proxy is running on the default
219  // port based on the protocol. We have no reliable way to determine
220  // the actual port in use upstream.
221  $port = $stdPort;
222  } elseif ( $parts[1] === false ) {
223  if ( isset( $_SERVER['SERVER_PORT'] ) ) {
224  $port = $_SERVER['SERVER_PORT'];
225  } // else leave it as $stdPort
226  } else {
227  $port = $parts[1];
228  }
229  break;
230  }
231 
232  return $proto . '://' . IP::combineHostAndPort( $host, $port, $stdPort );
233  }
234 
242  public static function detectProtocol() {
243  if ( ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ||
244  ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) &&
245  $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) ) {
246  return 'https';
247  } else {
248  return 'http';
249  }
250  }
251 
259  public function getElapsedTime() {
260  return microtime( true ) - $this->requestTime;
261  }
262 
271  public static function getRequestId() {
272  // This method is called from various error handlers and should be kept simple.
273 
274  if ( !self::$reqId ) {
275  self::$reqId = isset( $_SERVER['UNIQUE_ID'] )
276  ? $_SERVER['UNIQUE_ID'] : wfRandomString( 24 );
277  }
278 
279  return self::$reqId;
280  }
281 
289  public static function overrideRequestId( $id ) {
290  self::$reqId = $id;
291  }
292 
297  public function getProtocol() {
298  if ( $this->protocol === null ) {
299  $this->protocol = self::detectProtocol();
300  }
301  return $this->protocol;
302  }
303 
311  public function interpolateTitle() {
312  // T18019: title interpolation on API queries is useless and sometimes harmful
313  if ( defined( 'MW_API' ) ) {
314  return;
315  }
316 
317  $matches = self::getPathInfo( 'title' );
318  foreach ( $matches as $key => $val ) {
319  $this->data[$key] = $_GET[$key] = $_REQUEST[$key] = $val;
320  }
321  }
322 
333  static function extractTitle( $path, $bases, $key = false ) {
334  foreach ( (array)$bases as $keyValue => $base ) {
335  // Find the part after $wgArticlePath
336  $base = str_replace( '$1', '', $base );
337  $baseLen = strlen( $base );
338  if ( substr( $path, 0, $baseLen ) == $base ) {
339  $raw = substr( $path, $baseLen );
340  if ( $raw !== '' ) {
341  $matches = [ 'title' => rawurldecode( $raw ) ];
342  if ( $key ) {
343  $matches[$key] = $keyValue;
344  }
345  return $matches;
346  }
347  }
348  }
349  return [];
350  }
351 
359  public function normalizeUnicode( $data ) {
360  if ( is_array( $data ) ) {
361  foreach ( $data as $key => $val ) {
362  $data[$key] = $this->normalizeUnicode( $val );
363  }
364  } else {
366  $data = isset( $wgContLang ) ?
367  $wgContLang->normalize( $data ) :
368  UtfNormal\Validator::cleanUp( $data );
369  }
370  return $data;
371  }
372 
381  private function getGPCVal( $arr, $name, $default ) {
382  # PHP is so nice to not touch input data, except sometimes:
383  # https://secure.php.net/variables.external#language.variables.external.dot-in-names
384  # Work around PHP *feature* to avoid *bugs* elsewhere.
385  $name = strtr( $name, '.', '_' );
386  if ( isset( $arr[$name] ) ) {
388  $data = $arr[$name];
389  if ( isset( $_GET[$name] ) && !is_array( $data ) ) {
390  # Check for alternate/legacy character encoding.
391  if ( isset( $wgContLang ) ) {
392  $data = $wgContLang->checkTitleEncoding( $data );
393  }
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 = isset( $this->data[$key] ) ? $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 
664  public function getRawQueryString() {
665  return $_SERVER['QUERY_STRING'];
666  }
667 
674  public function getRawPostString() {
675  if ( !$this->wasPosted() ) {
676  return '';
677  }
678  return $this->getRawInput();
679  }
680 
688  public function getRawInput() {
689  static $input = null;
690  if ( $input === null ) {
691  $input = file_get_contents( 'php://input' );
692  }
693  return $input;
694  }
695 
701  public function getMethod() {
702  return isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : 'GET';
703  }
704 
714  public function wasPosted() {
715  return $this->getMethod() == 'POST';
716  }
717 
728  public function getSession() {
729  if ( $this->sessionId !== null ) {
730  $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
731  if ( $session ) {
732  return $session;
733  }
734  }
735 
736  $session = SessionManager::singleton()->getSessionForRequest( $this );
737  $this->sessionId = $session->getSessionId();
738  return $session;
739  }
740 
747  public function setSessionId( SessionId $sessionId ) {
748  $this->sessionId = $sessionId;
749  }
750 
757  public function getSessionId() {
758  return $this->sessionId;
759  }
760 
769  public function getCookie( $key, $prefix = null, $default = null ) {
770  if ( $prefix === null ) {
772  $prefix = $wgCookiePrefix;
773  }
774  $name = $prefix . $key;
775  // Work around mangling of $_COOKIE
776  $name = strtr( $name, '.', '_' );
777  if ( isset( $_COOKIE[$name] ) ) {
778  return $_COOKIE[$name];
779  } else {
780  return $default;
781  }
782  }
783 
792  public function getCrossSiteCookie( $key, $prefix = '', $default = null ) {
794  $name = $prefix . $key;
795  // Work around mangling of $_COOKIE
796  $name = strtr( $name, '.', '_' );
797  if ( isset( $_COOKIE[$name] ) ) {
798  return $_COOKIE[$name];
799  }
801  $legacyName = $prefix . "ss0-" . $key;
802  $legacyName = strtr( $legacyName, '.', '_' );
803  if ( isset( $_COOKIE[$legacyName] ) ) {
804  return $_COOKIE[$legacyName];
805  }
806  }
807  return $default;
808  }
809 
817  public static function getGlobalRequestURL() {
818  // This method is called on fatal errors; it should not depend on anything complex.
819 
820  if ( isset( $_SERVER['REQUEST_URI'] ) && strlen( $_SERVER['REQUEST_URI'] ) ) {
821  $base = $_SERVER['REQUEST_URI'];
822  } elseif ( isset( $_SERVER['HTTP_X_ORIGINAL_URL'] )
823  && strlen( $_SERVER['HTTP_X_ORIGINAL_URL'] )
824  ) {
825  // Probably IIS; doesn't set REQUEST_URI
826  $base = $_SERVER['HTTP_X_ORIGINAL_URL'];
827  } elseif ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
828  $base = $_SERVER['SCRIPT_NAME'];
829  if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] != '' ) {
830  $base .= '?' . $_SERVER['QUERY_STRING'];
831  }
832  } else {
833  // This shouldn't happen!
834  throw new MWException( "Web server doesn't provide either " .
835  "REQUEST_URI, HTTP_X_ORIGINAL_URL or SCRIPT_NAME. Report details " .
836  "of your web server configuration to https://phabricator.wikimedia.org/" );
837  }
838  // User-agents should not send a fragment with the URI, but
839  // if they do, and the web server passes it on to us, we
840  // need to strip it or we get false-positive redirect loops
841  // or weird output URLs
842  $hash = strpos( $base, '#' );
843  if ( $hash !== false ) {
844  $base = substr( $base, 0, $hash );
845  }
846 
847  if ( $base[0] == '/' ) {
848  // More than one slash will look like it is protocol relative
849  return preg_replace( '!^/+!', '/', $base );
850  } else {
851  // We may get paths with a host prepended; strip it.
852  return preg_replace( '!^[^:]+://[^/]+/+!', '/', $base );
853  }
854  }
855 
863  public function getRequestURL() {
864  return self::getGlobalRequestURL();
865  }
866 
877  public function getFullRequestURL() {
878  return wfExpandUrl( $this->getRequestURL(), PROTO_CURRENT );
879  }
880 
886  public function appendQueryValue( $key, $value ) {
887  return $this->appendQueryArray( [ $key => $value ] );
888  }
889 
896  public function appendQueryArray( $array ) {
897  $newquery = $this->getQueryValues();
898  unset( $newquery['title'] );
899  $newquery = array_merge( $newquery, $array );
900 
901  return wfArrayToCgi( $newquery );
902  }
903 
913  public function getLimitOffset( $deflimit = 50, $optionname = 'rclimit' ) {
914  global $wgUser;
915 
916  $limit = $this->getInt( 'limit', 0 );
917  if ( $limit < 0 ) {
918  $limit = 0;
919  }
920  if ( ( $limit == 0 ) && ( $optionname != '' ) ) {
921  $limit = $wgUser->getIntOption( $optionname );
922  }
923  if ( $limit <= 0 ) {
924  $limit = $deflimit;
925  }
926  if ( $limit > 5000 ) {
927  $limit = 5000; # We have *some* limits...
928  }
929 
930  $offset = $this->getInt( 'offset', 0 );
931  if ( $offset < 0 ) {
932  $offset = 0;
933  }
934 
935  return [ $limit, $offset ];
936  }
937 
944  public function getFileTempname( $key ) {
945  $file = new WebRequestUpload( $this, $key );
946  return $file->getTempName();
947  }
948 
955  public function getUploadError( $key ) {
956  $file = new WebRequestUpload( $this, $key );
957  return $file->getError();
958  }
959 
971  public function getFileName( $key ) {
972  $file = new WebRequestUpload( $this, $key );
973  return $file->getName();
974  }
975 
982  public function getUpload( $key ) {
983  return new WebRequestUpload( $this, $key );
984  }
985 
992  public function response() {
993  /* Lazy initialization of response object for this request */
994  if ( !is_object( $this->response ) ) {
995  $class = ( $this instanceof FauxRequest ) ? FauxResponse::class : WebResponse::class;
996  $this->response = new $class();
997  }
998  return $this->response;
999  }
1000 
1004  protected function initHeaders() {
1005  if ( count( $this->headers ) ) {
1006  return;
1007  }
1008 
1009  $apacheHeaders = function_exists( 'apache_request_headers' ) ? apache_request_headers() : false;
1010  if ( $apacheHeaders ) {
1011  foreach ( $apacheHeaders as $tempName => $tempValue ) {
1012  $this->headers[strtoupper( $tempName )] = $tempValue;
1013  }
1014  } else {
1015  foreach ( $_SERVER as $name => $value ) {
1016  if ( substr( $name, 0, 5 ) === 'HTTP_' ) {
1017  $name = str_replace( '_', '-', substr( $name, 5 ) );
1018  $this->headers[$name] = $value;
1019  } elseif ( $name === 'CONTENT_LENGTH' ) {
1020  $this->headers['CONTENT-LENGTH'] = $value;
1021  }
1022  }
1023  }
1024  }
1025 
1031  public function getAllHeaders() {
1032  $this->initHeaders();
1033  return $this->headers;
1034  }
1035 
1048  public function getHeader( $name, $flags = 0 ) {
1049  $this->initHeaders();
1050  $name = strtoupper( $name );
1051  if ( !isset( $this->headers[$name] ) ) {
1052  return false;
1053  }
1054  $value = $this->headers[$name];
1055  if ( $flags & self::GETHEADER_LIST ) {
1056  $value = array_map( 'trim', explode( ',', $value ) );
1057  }
1058  return $value;
1059  }
1060 
1068  public function getSessionData( $key ) {
1069  return $this->getSession()->get( $key );
1070  }
1071 
1079  public function setSessionData( $key, $data ) {
1080  $this->getSession()->set( $key, $data );
1081  }
1082 
1093  public function checkUrlExtension( $extWhitelist = [] ) {
1094  $extWhitelist[] = 'php';
1095  if ( IEUrlExtension::areServerVarsBad( $_SERVER, $extWhitelist ) ) {
1096  if ( !$this->wasPosted() ) {
1097  $newUrl = IEUrlExtension::fixUrlForIE6(
1098  $this->getFullRequestURL(), $extWhitelist );
1099  if ( $newUrl !== false ) {
1100  $this->doSecurityRedirect( $newUrl );
1101  return false;
1102  }
1103  }
1104  throw new HttpError( 403,
1105  'Invalid file extension found in the path info or query string.' );
1106  }
1107  return true;
1108  }
1109 
1117  protected function doSecurityRedirect( $url ) {
1118  header( 'Location: ' . $url );
1119  header( 'Content-Type: text/html' );
1120  $encUrl = htmlspecialchars( $url );
1121  echo <<<HTML
1122 <!DOCTYPE html>
1123 <html>
1124 <head>
1125 <title>Security redirect</title>
1126 </head>
1127 <body>
1128 <h1>Security redirect</h1>
1129 <p>
1130 We can't serve non-HTML content from the URL you have requested, because
1131 Internet Explorer would interpret it as an incorrect and potentially dangerous
1132 content type.</p>
1133 <p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the
1134 URL you have requested, except that "&amp;*" is appended. This prevents Internet
1135 Explorer from seeing a bogus file extension.
1136 </p>
1137 </body>
1138 </html>
1139 HTML;
1140  echo "\n";
1141  return true;
1142  }
1143 
1153  public function getAcceptLang() {
1154  // Modified version of code found at
1155  // http://www.thefutureoftheweb.com/blog/use-accept-language-header
1156  $acceptLang = $this->getHeader( 'Accept-Language' );
1157  if ( !$acceptLang ) {
1158  return [];
1159  }
1160 
1161  // Return the language codes in lower case
1162  $acceptLang = strtolower( $acceptLang );
1163 
1164  // Break up string into pieces (languages and q factors)
1165  $lang_parse = null;
1166  preg_match_all(
1167  '/([a-z]{1,8}(-[a-z]{1,8})*|\*)\s*(;\s*q\s*=\s*(1(\.0{0,3})?|0(\.[0-9]{0,3})?)?)?/',
1168  $acceptLang,
1169  $lang_parse
1170  );
1171 
1172  if ( !count( $lang_parse[1] ) ) {
1173  return [];
1174  }
1175 
1176  $langcodes = $lang_parse[1];
1177  $qvalues = $lang_parse[4];
1178  $indices = range( 0, count( $lang_parse[1] ) - 1 );
1179 
1180  // Set default q factor to 1
1181  foreach ( $indices as $index ) {
1182  if ( $qvalues[$index] === '' ) {
1183  $qvalues[$index] = 1;
1184  } elseif ( $qvalues[$index] == 0 ) {
1185  unset( $langcodes[$index], $qvalues[$index], $indices[$index] );
1186  }
1187  }
1188 
1189  // Sort list. First by $qvalues, then by order. Reorder $langcodes the same way
1190  array_multisort( $qvalues, SORT_DESC, SORT_NUMERIC, $indices, $langcodes );
1191 
1192  // Create a list like "en" => 0.8
1193  $langs = array_combine( $langcodes, $qvalues );
1194 
1195  return $langs;
1196  }
1197 
1206  protected function getRawIP() {
1207  if ( !isset( $_SERVER['REMOTE_ADDR'] ) ) {
1208  return null;
1209  }
1210 
1211  if ( is_array( $_SERVER['REMOTE_ADDR'] ) || strpos( $_SERVER['REMOTE_ADDR'], ',' ) !== false ) {
1212  throw new MWException( __METHOD__
1213  . " : Could not determine the remote IP address due to multiple values." );
1214  } else {
1215  $ipchain = $_SERVER['REMOTE_ADDR'];
1216  }
1217 
1218  return IP::canonicalize( $ipchain );
1219  }
1220 
1230  public function getIP() {
1231  global $wgUsePrivateIPs;
1232 
1233  # Return cached result
1234  if ( $this->ip !== null ) {
1235  return $this->ip;
1236  }
1237 
1238  # collect the originating ips
1239  $ip = $this->getRawIP();
1240  if ( !$ip ) {
1241  throw new MWException( 'Unable to determine IP.' );
1242  }
1243 
1244  # Append XFF
1245  $forwardedFor = $this->getHeader( 'X-Forwarded-For' );
1246  if ( $forwardedFor !== false ) {
1247  $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
1248  $isConfigured = $proxyLookup->isConfiguredProxy( $ip );
1249  $ipchain = array_map( 'trim', explode( ',', $forwardedFor ) );
1250  $ipchain = array_reverse( $ipchain );
1251  array_unshift( $ipchain, $ip );
1252 
1253  # Step through XFF list and find the last address in the list which is a
1254  # trusted server. Set $ip to the IP address given by that trusted server,
1255  # unless the address is not sensible (e.g. private). However, prefer private
1256  # IP addresses over proxy servers controlled by this site (more sensible).
1257  # Note that some XFF values might be "unknown" with Squid/Varnish.
1258  foreach ( $ipchain as $i => $curIP ) {
1259  $curIP = IP::sanitizeIP( IP::canonicalize( $curIP ) );
1260  if ( !$curIP || !isset( $ipchain[$i + 1] ) || $ipchain[$i + 1] === 'unknown'
1261  || !$proxyLookup->isTrustedProxy( $curIP )
1262  ) {
1263  break; // IP is not valid/trusted or does not point to anything
1264  }
1265  if (
1266  IP::isPublic( $ipchain[$i + 1] ) ||
1267  $wgUsePrivateIPs ||
1268  $proxyLookup->isConfiguredProxy( $curIP ) // T50919; treat IP as sane
1269  ) {
1270  // Follow the next IP according to the proxy
1271  $nextIP = IP::canonicalize( $ipchain[$i + 1] );
1272  if ( !$nextIP && $isConfigured ) {
1273  // We have not yet made it past CDN/proxy servers of this site,
1274  // so either they are misconfigured or there is some IP spoofing.
1275  throw new MWException( "Invalid IP given in XFF '$forwardedFor'." );
1276  }
1277  $ip = $nextIP;
1278  // keep traversing the chain
1279  continue;
1280  }
1281  break;
1282  }
1283  }
1284 
1285  # Allow extensions to improve our guess
1286  Hooks::run( 'GetIP', [ &$ip ] );
1287 
1288  if ( !$ip ) {
1289  throw new MWException( "Unable to determine IP." );
1290  }
1291 
1292  wfDebug( "IP: $ip\n" );
1293  $this->ip = $ip;
1294  return $ip;
1295  }
1296 
1302  public function setIP( $ip ) {
1303  $this->ip = $ip;
1304  }
1305 
1318  public function hasSafeMethod() {
1319  if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
1320  return false; // CLI mode
1321  }
1322 
1323  return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
1324  }
1325 
1344  public function isSafeRequest() {
1345  if ( $this->markedAsSafe && $this->wasPosted() ) {
1346  return true; // marked as a "safe" POST
1347  }
1348 
1349  return $this->hasSafeMethod();
1350  }
1351 
1362  public function markAsSafeRequest() {
1363  $this->markedAsSafe = true;
1364  }
1365 }
PathRouter\add
add( $path, $params=[], $options=[])
Add a new path pattern to the path router.
Definition: PathRouter.php:160
WebRequest\initHeaders
initHeaders()
Initialise the header list.
Definition: WebRequest.php:1004
WebRequest\$sessionId
SessionId null $sessionId
Session ID to use for this request.
Definition: WebRequest.php:82
$wgUser
$wgUser
Definition: Setup.php:902
FauxRequest
WebRequest clone which takes values from a provided array.
Definition: FauxRequest.php:33
WebRequest\getSessionData
getSessionData( $key)
Get data from the session.
Definition: WebRequest.php:1068
$wgActionPaths
$wgActionPaths
Definition: img_auth.php:46
use
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Definition: APACHE-LICENSE-2.0.txt:10
WebRequest\getValueNames
getValueNames( $exclude=[])
Returns the names of all input values excluding those in $exclude.
Definition: WebRequest.php:642
array
the array() calling protocol came about after MediaWiki 1.4rc1.
IP\combineHostAndPort
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
WebRequest\getSessionId
getSessionId()
Get the session id for this request, if any.
Definition: WebRequest.php:757
$wgScript
$wgScript
The URL path to index.php.
Definition: DefaultSettings.php:206
WebRequest\appendQueryValue
appendQueryValue( $key, $value)
Definition: WebRequest.php:886
WebRequest\interpolateTitle
interpolateTitle()
Check for title, action, and/or variant data in the URL and interpolate it into the GET variables.
Definition: WebRequest.php:311
WebRequest\setSessionId
setSessionId(SessionId $sessionId)
Set the session for this request.
Definition: WebRequest.php:747
$ret
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:2005
WebRequest\getElapsedTime
getElapsedTime()
Get the number of seconds to have elapsed since request start, in fractional seconds,...
Definition: WebRequest.php:259
WebRequest\getIntOrNull
getIntOrNull( $name)
Fetch an integer value from the input or return null if empty.
Definition: WebRequest.php:536
IP
A collection of public static functions to play with IP address and IP ranges.
Definition: IP.php:67
WebRequest\getRawPostString
getRawPostString()
Return the contents of the POST with no decoding.
Definition: WebRequest.php:674
WebRequest\detectProtocol
static detectProtocol()
Detect the protocol from $_SERVER.
Definition: WebRequest.php:242
WebRequest\getGPCVal
getGPCVal( $arr, $name, $default)
Fetch a value from the given array or return $default if it's not set.
Definition: WebRequest.php:381
$wgUseSameSiteLegacyCookies
bool $wgUseSameSiteLegacyCookies
If true, when a cross-site cookie with SameSite=None is sent, a legacy cookie with an "ss0" prefix wi...
Definition: DefaultSettings.php:6065
$base
$base
Definition: generateLocalAutoload.php:11
WebRequest\$headers
$headers
Definition: WebRequest.php:39
HttpError
Show an error that looks like an HTTP server error.
Definition: HttpError.php:30
php
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:37
WebRequest\__construct
__construct()
Definition: WebRequest.php:90
WebRequest\getRawQueryString
getRawQueryString()
Return the contents of the Query with no decoding.
Definition: WebRequest.php:664
WebRequest\appendQueryArray
appendQueryArray( $array)
Appends or replaces value of query variables.
Definition: WebRequest.php:896
WebRequest\getFileTempname
getFileTempname( $key)
Return the path to the temporary file where PHP has stored the upload.
Definition: WebRequest.php:944
$wgAssumeProxiesUseDefaultProtocolPorts
bool $wgAssumeProxiesUseDefaultProtocolPorts
When the wiki is running behind a proxy and this is set to true, assumes that the proxy exposes the w...
Definition: DefaultSettings.php:88
WebRequest\getText
getText( $name, $default='')
Fetch a text string from the given array or return $default if it's not set.
Definition: WebRequest.php:608
WebRequest\$protocol
string $protocol
Cached URL protocol.
Definition: WebRequest.php:75
WebRequest\getMethod
getMethod()
Get the HTTP method used for this request.
Definition: WebRequest.php:701
MWException
MediaWiki exception.
Definition: MWException.php:26
WebRequest\getFileName
getFileName( $key)
Return the original filename of the uploaded file, as reported by the submitting user agent.
Definition: WebRequest.php:971
MediaWiki\Session\Session
Manages data for an an authenticated session.
Definition: Session.php:48
WebRequest\setVal
setVal( $key, $value)
Set an arbitrary value into our get/post data.
Definition: WebRequest.php:457
$input
if(is_array( $mode)) switch( $mode) $input
Definition: postprocess-phan.php:145
WebRequest\$reqId
static string $reqId
The unique request ID.
Definition: WebRequest.php:51
$matches
$matches
Definition: NoLocalSettings.php:24
WebRequest\getRawInput
getRawInput()
Return the raw request body, with no processing.
Definition: WebRequest.php:688
WebRequest\getUpload
getUpload( $key)
Return a WebRequestUpload object corresponding to the key.
Definition: WebRequest.php:982
title
title
Definition: parserTests.txt:219
WebRequest\getValues
getValues()
Extracts the given named values into an array.
Definition: WebRequest.php:620
WebRequest\getPathInfo
static getPathInfo( $want='all')
Extract relevant query arguments from the http request uri's path to be merged with the normal php pr...
Definition: WebRequest.php:113
PROTO_CURRENT
const PROTO_CURRENT
Definition: Defines.php:232
WebRequest\getFullRequestURL
getFullRequestURL()
Return the request URI with the canonical service and hostname, path, and query string.
Definition: WebRequest.php:877
WebRequest\getArray
getArray( $name, $default=null)
Fetch an array from the input or return $default if it's not set.
Definition: WebRequest.php:488
WebRequest\$response
WebResponse $response
Lazy-init response object.
Definition: WebRequest.php:57
WebRequest\getAllHeaders
getAllHeaders()
Get an array containing all request headers.
Definition: WebRequest.php:1031
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:95
WebRequestUpload
Object to access the $_FILES array.
Definition: WebRequestUpload.php:28
WebRequest\normalizeUnicode
normalizeUnicode( $data)
Recursively normalizes UTF-8 strings in the given array.
Definition: WebRequest.php:359
WebRequest\getRawVal
getRawVal( $name, $default=null)
Fetch a scalar from the input without normalization, or return $default if it's not set.
Definition: WebRequest.php:414
WebRequest\$data
$data
Definition: WebRequest.php:39
IEUrlExtension\areServerVarsBad
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...
Definition: IEUrlExtension.php:62
WebRequest\getCheck
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
Definition: WebRequest.php:592
WebRequest\getProtocol
getProtocol()
Get the current URL protocol (http or https)
Definition: WebRequest.php:297
WebRequest\getSession
getSession()
Return the session for this request.
Definition: WebRequest.php:728
WebRequest\response
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff,...
Definition: WebRequest.php:992
$value
$value
Definition: styleTest.css.php:45
IP\splitHostAndPort
static splitHostAndPort( $both)
Given a host/port string, like one might find in the host part of a URL per RFC 2732,...
Definition: IP.php:266
some
I won t presume to tell you how to I m just describing the methods I chose to use for myself If you do choose to follow these it will probably be easier for you to collaborate with others on the but if you want to contribute without by all means do which work well I also use K &R brace matching style I know that s a religious issue for some
Definition: design.txt:79
MediaWiki\Session\SessionManager
This serves as the entry point to the MediaWiki session handling system.
Definition: SessionManager.php:50
WebRequest\getCrossSiteCookie
getCrossSiteCookie( $key, $prefix='', $default=null)
Get a cookie set with SameSite=None possibly with a legacy fallback cookie.
Definition: WebRequest.php:792
WebRequest\getLimitOffset
getLimitOffset( $deflimit=50, $optionname='rclimit')
Check for limit and offset parameters on the input, and return sensible defaults if not given.
Definition: WebRequest.php:913
WebRequest\checkUrlExtension
checkUrlExtension( $extWhitelist=[])
Check if Internet Explorer will detect an incorrect cache extension in PATH_INFO or QUERY_STRING.
Definition: WebRequest.php:1093
WebRequest\getIntArray
getIntArray( $name, $default=null)
Fetch an array of integers, or return $default if it's not set.
Definition: WebRequest.php:507
WebRequest\$requestTime
float $requestTime
The timestamp of the start of the request, with microsecond precision.
Definition: WebRequest.php:69
IEUrlExtension\fixUrlForIE6
static fixUrlForIE6( $url, $extWhitelist=[])
Returns a variant of $url which will pass isUrlExtensionBad() but has the same GET parameters,...
Definition: IEUrlExtension.php:140
WebRequest\$markedAsSafe
bool $markedAsSafe
Whether this HTTP request is "safe" (even if it is an HTTP post)
Definition: WebRequest.php:85
WebRequest\getCookie
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
Definition: WebRequest.php:769
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:38
WebRequest\getUploadError
getUploadError( $key)
Return the upload error or 0.
Definition: WebRequest.php:955
$wgArticlePath
$wgArticlePath
Definition: img_auth.php:45
WebRequest\setSessionData
setSessionData( $key, $data)
Set session data.
Definition: WebRequest.php:1079
WebRequest\doSecurityRedirect
doSecurityRedirect( $url)
Attempt to redirect to a URL with a QUERY_STRING that's not dangerous in IE 6.
Definition: WebRequest.php:1117
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
WebRequest\GETHEADER_LIST
const GETHEADER_LIST
Flag to make WebRequest::getHeader return an array of values.
Definition: WebRequest.php:45
WebRequest\getVal
getVal( $name, $default=null)
Fetch a scalar from the input or return $default if it's not set.
Definition: WebRequest.php:438
WebRequest\getInt
getInt( $name, $default=0)
Fetch an integer value from the input or return $default if not set.
Definition: WebRequest.php:524
MediaWiki\Session\SessionId
Value object holding the session ID in a manner that can be globally updated.
Definition: SessionId.php:38
WebRequest\getRequestId
static getRequestId()
Get the unique request ID.
Definition: WebRequest.php:271
$path
$path
Definition: NoLocalSettings.php:25
as
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:22
WebRequest\getFloat
getFloat( $name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
Definition: WebRequest.php:553
WebRequest\detectServer
static detectServer()
Work out an appropriate URL prefix containing scheme and host, based on information detected from $_S...
Definition: WebRequest.php:196
WebRequest\getHeader
getHeader( $name, $flags=0)
Get a request header, or false if it isn't set.
Definition: WebRequest.php:1048
WebRequest\getGlobalRequestURL
static getGlobalRequestURL()
Return the path and query string portion of the main request URI.
Definition: WebRequest.php:817
WebRequest\$ip
string $ip
Cached client IP address.
Definition: WebRequest.php:63
class
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:56
MediaWikiServices
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:25
WebRequest\wasPosted
wasPosted()
Returns true if the present request was reached by a POST operation, false otherwise (GET,...
Definition: WebRequest.php:714
WebRequest\unsetVal
unsetVal( $key)
Unset an arbitrary value from our get/post data.
Definition: WebRequest.php:469
WebRequest\getRequestURL
getRequestURL()
Return the path and query string portion of the request URI.
Definition: WebRequest.php:863
WebRequest\overrideRequestId
static overrideRequestId( $id)
Override the unique request ID.
Definition: WebRequest.php:289
WebRequest\extractTitle
static extractTitle( $path, $bases, $key=false)
URL rewriting function; tries to extract page title and, optionally, one other fixed parameter value ...
Definition: WebRequest.php:333
WebResponse
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
Definition: WebResponse.php:30
WebRequest\getQueryValues
getQueryValues()
Get the values passed in the query string.
Definition: WebRequest.php:653
WebRequest\getFuzzyBool
getFuzzyBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:579
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
$wgVariantArticlePath
$wgVariantArticlePath
Like $wgArticlePath, but on multi-variant wikis, this provides a path format that describes which par...
Definition: DefaultSettings.php:3104
PathRouter
PathRouter class.
Definition: PathRouter.php:73
$wgCookiePrefix
$wgCookiePrefix
Cookies generated by MediaWiki have names starting with this prefix.
Definition: DefaultSettings.php:6036
headers
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add headers
Definition: design.txt:18
redirect
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
Definition: All_system_messages.txt:1267
Language
Internationalisation code.
Definition: Language.php:35
WebRequest\getBool
getBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
Definition: WebRequest.php:566
wfExpandUrl
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
Definition: GlobalFunctions.php:521
data
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:18
$wgUsePathInfo
$wgUsePathInfo
Whether to support URLs like index.php/Page_title These often break when PHP is set up in CGI mode.
Definition: DefaultSettings.php:177
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:57
wfArrayToCgi
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
Definition: GlobalFunctions.php:377
wfRandomString
wfRandomString( $length=32)
Get a random string containing a number of pseudo-random hex characters.
Definition: GlobalFunctions.php:305