MediaWiki REL1_31
WebRequest.php
Go to the documentation of this file.
1<?php
30
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' ) {
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() ) {
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>
1130We can't serve non-HTML content from the URL you have requested, because
1131Internet Explorer would interpret it as an incorrect and potentially dangerous
1132content type.</p>
1133<p>Instead, please use <a href="$encUrl">this URL</a>, which is the same as the
1134URL you have requested, except that "&amp;*" is appended. This prevents Internet
1135Explorer from seeing a bogus file extension.
1136</p>
1137</body>
1138</html>
1139HTML;
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}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
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
$wgUsePathInfo
Whether to support URLs like index.php/Page_title These often break when PHP is set up in CGI mode.
$wgScript
The URL path to index.php.
bool $wgUseSameSiteLegacyCookies
If true, when a cross-site cookie with SameSite=None is sent, a legacy cookie with an "ss0" prefix wi...
bool $wgAssumeProxiesUseDefaultProtocolPorts
When the wiki is running behind a proxy and this is set to true, assumes that the proxy exposes the w...
$wgVariantArticlePath
Like $wgArticlePath, but on multi-variant wikis, this provides a path format that describes which par...
$wgCookiePrefix
Cookies generated by MediaWiki have names starting with this prefix.
wfRandomString( $length=32)
Get a random string containing a number of pseudo-random hex characters.
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
$wgUser
Definition Setup.php:902
WebRequest clone which takes values from a provided array.
Show an error that looks like an HTTP server error.
Definition HttpError.php:30
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...
static fixUrlForIE6( $url, $extWhitelist=[])
Returns a variant of $url which will pass isUrlExtensionBad() but has the same GET parameters,...
A collection of public static functions to play with IP address and IP ranges.
Definition IP.php:67
Internationalisation code.
Definition Language.php:35
MediaWiki exception.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Value object holding the session ID in a manner that can be globally updated.
Definition SessionId.php:38
This serves as the entry point to the MediaWiki session handling system.
Manages data for an an authenticated session.
Definition Session.php:48
PathRouter class.
add( $path, $params=[], $options=[])
Add a new path pattern to the path router.
Object to access the $_FILES array.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
getIntOrNull( $name)
Fetch an integer value from the input or return null if empty.
getValueNames( $exclude=[])
Returns the names of all input values excluding those in $exclude.
bool $markedAsSafe
Whether this HTTP request is "safe" (even if it is an HTTP post)
getUpload( $key)
Return a WebRequestUpload object corresponding to the key.
string $protocol
Cached URL protocol.
getArray( $name, $default=null)
Fetch an array from the input or return $default if it's not set.
interpolateTitle()
Check for title, action, and/or variant data in the URL and interpolate it into the GET variables.
static detectProtocol()
Detect the protocol from $_SERVER.
getSession()
Return the session for this request.
getRawInput()
Return the raw request body, with no processing.
getRawQueryString()
Return the contents of the Query with no decoding.
getFileTempname( $key)
Return the path to the temporary file where PHP has stored the upload.
getVal( $name, $default=null)
Fetch a scalar from the input or return $default if it's not set.
getFloat( $name, $default=0.0)
Fetch a floating point value from the input or return $default if not set.
WebResponse $response
Lazy-init response object.
getUploadError( $key)
Return the upload error or 0.
getAllHeaders()
Get an array containing all request headers.
getFuzzyBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
getGPCVal( $arr, $name, $default)
Fetch a value from the given array or return $default if it's not set.
static getRequestId()
Get the unique request ID.
getProtocol()
Get the current URL protocol (http or https)
getMethod()
Get the HTTP method used for this request.
initHeaders()
Initialise the header list.
getBool( $name, $default=false)
Fetch a boolean value from the input or return $default if not set.
string $ip
Cached client IP address.
static getGlobalRequestURL()
Return the path and query string portion of the main request URI.
setVal( $key, $value)
Set an arbitrary value into our get/post data.
static string $reqId
The unique request ID.
getFullRequestURL()
Return the request URI with the canonical service and hostname, path, and query string.
checkUrlExtension( $extWhitelist=[])
Check if Internet Explorer will detect an incorrect cache extension in PATH_INFO or QUERY_STRING.
getElapsedTime()
Get the number of seconds to have elapsed since request start, in fractional seconds,...
float $requestTime
The timestamp of the start of the request, with microsecond precision.
getCrossSiteCookie( $key, $prefix='', $default=null)
Get a cookie set with SameSite=None possibly with a legacy fallback cookie.
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
appendQueryArray( $array)
Appends or replaces value of query variables.
static detectServer()
Work out an appropriate URL prefix containing scheme and host, based on information detected from $_S...
getSessionId()
Get the session id for this request, if any.
getRawPostString()
Return the contents of the POST with no decoding.
getQueryValues()
Get the values passed in the query string.
response()
Return a handle to WebResponse style object, for setting cookies, headers and other stuff,...
getInt( $name, $default=0)
Fetch an integer value from the input or return $default if not set.
wasPosted()
Returns true if the present request was reached by a POST operation, false otherwise (GET,...
setSessionData( $key, $data)
Set session data.
doSecurityRedirect( $url)
Attempt to redirect to a URL with a QUERY_STRING that's not dangerous in IE 6.
getFileName( $key)
Return the original filename of the uploaded file, as reported by the submitting user agent.
const GETHEADER_LIST
Flag to make WebRequest::getHeader return an array of values.
getRawVal( $name, $default=null)
Fetch a scalar from the input without normalization, or return $default if it's not set.
getIntArray( $name, $default=null)
Fetch an array of integers, or return $default if it's not set.
appendQueryValue( $key, $value)
normalizeUnicode( $data)
Recursively normalizes UTF-8 strings in the given array.
static overrideRequestId( $id)
Override the unique request ID.
unsetVal( $key)
Unset an arbitrary value from our get/post data.
static getPathInfo( $want='all')
Extract relevant query arguments from the http request uri's path to be merged with the normal php pr...
SessionId null $sessionId
Session ID to use for this request.
setSessionId(SessionId $sessionId)
Set the session for this request.
getCookie( $key, $prefix=null, $default=null)
Get a cookie from the $_COOKIE jar.
getLimitOffset( $deflimit=50, $optionname='rclimit')
Check for limit and offset parameters on the input, and return sensible defaults if not given.
static extractTitle( $path, $bases, $key=false)
URL rewriting function; tries to extract page title and, optionally, one other fixed parameter value ...
getText( $name, $default='')
Fetch a text string from the given array or return $default if it's not set.
getRequestURL()
Return the path and query string portion of the request URI.
getHeader( $name, $flags=0)
Get a request header, or false if it isn't set.
getSessionData( $key)
Get data from the session.
getValues()
Extracts the given named values into an array.
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
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 local content language as $wgContLang
Definition design.txt:57
when a variable name is used in a it is silently declared as a new local masking the global
Definition design.txt:95
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
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
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
const PROTO_CURRENT
Definition Defines.php:232
the array() calling protocol came about after MediaWiki 1.4rc1.
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
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
this hook is for auditing only $response
Definition hooks.txt:783
$wgActionPaths
Definition img_auth.php:46
$wgArticlePath
Definition img_auth.php:45
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
title
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN boolean columns are always mapped to as the code does not always treat the column as a and VARBINARY columns should simply be TEXT The only exception is when VARBINARY is used to store true binary data
Definition postgres.txt:37
if(is_array($mode)) switch( $mode) $input