SECURITY: API: Improve validation in chunked uploading
[lhc/web/wiklou.git] / includes / GlobalFunctions.php
index 404d2cf..4cc4f00 100644 (file)
@@ -1233,7 +1233,7 @@ function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
  * @param string $file Filename
  * @param array $context Additional logging context data
  * @throws MWException
- * @deprecated since 1.25 Use MediaWiki\Logger\LegacyLogger::emit or UDPTransport
+ * @deprecated since 1.25 Use \\MediaWiki\\Logger\\LegacyLogger::emit or UDPTransport
  */
 function wfErrorLog( $text, $file, array $context = array() ) {
        wfDeprecated( __METHOD__, '1.25' );
@@ -1342,33 +1342,57 @@ function wfReadOnly() {
 }
 
 /**
- * Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
+ * Check if the site is in read-only mode and return the message if so
+ *
+ * This checks wfConfiguredReadOnlyReason() and the main load balancer
+ * for slave lag. This may result in DB_SLAVE connection being made.
  *
  * @return string|bool String when in read-only mode; false otherwise
  */
 function wfReadOnlyReason() {
-       global $wgReadOnly, $wgReadOnlyFile;
+       $readOnly = wfConfiguredReadOnlyReason();
+       if ( $readOnly !== false ) {
+               return $readOnly;
+       }
 
-       if ( $wgReadOnly === null ) {
-               // Set $wgReadOnly for faster access next time
-               if ( is_file( $wgReadOnlyFile ) && filesize( $wgReadOnlyFile ) > 0 ) {
-                       $wgReadOnly = file_get_contents( $wgReadOnlyFile );
-               } else {
-                       $wgReadOnly = false;
-               }
+       static $autoReadOnly = null;
+       if ( $autoReadOnly === null ) {
                // Callers use this method to be aware that data presented to a user
                // may be very stale and thus allowing submissions can be problematic.
                try {
-                       if ( $wgReadOnly === false && wfGetLB()->getLaggedSlaveMode() ) {
-                               $wgReadOnly = 'The database has been automatically locked ' .
+                       if ( wfGetLB()->getLaggedSlaveMode() ) {
+                               $autoReadOnly = 'The database has been automatically locked ' .
                                        'while the slave database servers catch up to the master';
+                       } else {
+                               $autoReadOnly = false;
                        }
                } catch ( DBConnectionError $e ) {
-                       $wgReadOnly = 'The database has been automatically locked ' .
+                       $autoReadOnly = 'The database has been automatically locked ' .
                                'until the slave database servers become available';
                }
        }
 
+       return $autoReadOnly;
+}
+
+/**
+ * Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
+ *
+ * @return string|bool String when in read-only mode; false otherwise
+ * @since 1.27
+ */
+function wfConfiguredReadOnlyReason() {
+       global $wgReadOnly, $wgReadOnlyFile;
+
+       if ( $wgReadOnly === null ) {
+               // Set $wgReadOnly for faster access next time
+               if ( is_file( $wgReadOnlyFile ) && filesize( $wgReadOnlyFile ) > 0 ) {
+                       $wgReadOnly = file_get_contents( $wgReadOnlyFile );
+               } else {
+                       $wgReadOnly = false;
+               }
+       }
+
        return $wgReadOnly;
 }
 
@@ -1961,8 +1985,11 @@ function wfGetAllCallers( $limit = 3 ) {
  * @return string
  */
 function wfFormatStackFrame( $frame ) {
-       return isset( $frame['class'] ) ?
-               $frame['class'] . '::' . $frame['function'] :
+       if ( !isset( $frame['function'] ) ) {
+               return 'NO_FUNCTION_GIVEN';
+       }
+       return isset( $frame['class'] ) && isset( $frame['type'] ) ?
+               $frame['class'] . $frame['type'] . $frame['function'] :
                $frame['function'];
 }
 
@@ -2542,7 +2569,7 @@ function wfMkdirParents( $dir, $mode = null, $caller = null ) {
        MediaWiki\restoreWarnings();
 
        if ( !$ok ) {
-               //directory may have been created on another request since we last checked
+               // directory may have been created on another request since we last checked
                if ( is_dir( $dir ) ) {
                        return true;
                }
@@ -2832,16 +2859,17 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
        $status = false;
        $logMsg = false;
 
-       // According to the documentation, it is possible for stream_select()
-       // to fail due to EINTR. I haven't managed to induce this in testing
-       // despite sending various signals. If it did happen, the error
-       // message would take the form:
-       //
-       // stream_select(): unable to select [4]: Interrupted system call (max_fd=5)
-       //
-       // where [4] is the value of the macro EINTR and "Interrupted system
-       // call" is string which according to the Linux manual is "possibly"
-       // localised according to LC_MESSAGES.
+       /* According to the documentation, it is possible for stream_select()
+        * to fail due to EINTR. I haven't managed to induce this in testing
+        * despite sending various signals. If it did happen, the error
+        * message would take the form:
+        *
+        * stream_select(): unable to select [4]: Interrupted system call (max_fd=5)
+        *
+        * where [4] is the value of the macro EINTR and "Interrupted system
+        * call" is string which according to the Linux manual is "possibly"
+        * localised according to LC_MESSAGES.
+        */
        $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4;
        $eintrMessage = "stream_select(): unable to select [$eintr]";
 
@@ -3689,20 +3717,20 @@ function wfQueriesMustScale() {
 
 /**
  * Get the path to a specified script file, respecting file
- * extensions; this is a wrapper around $wgScriptExtension etc.
+ * extensions; this is a wrapper around $wgScriptPath etc.
  * except for 'index' and 'load' which use $wgScript/$wgLoadScript
  *
  * @param string $script Script filename, sans extension
  * @return string
  */
 function wfScript( $script = 'index' ) {
-       global $wgScriptPath, $wgScriptExtension, $wgScript, $wgLoadScript;
+       global $wgScriptPath, $wgScript, $wgLoadScript;
        if ( $script === 'index' ) {
                return $wgScript;
        } elseif ( $script === 'load' ) {
                return $wgLoadScript;
        } else {
-               return "{$wgScriptPath}/{$script}{$wgScriptExtension}";
+               return "{$wgScriptPath}/{$script}.php";
        }
 }
 
@@ -3713,16 +3741,16 @@ function wfScript( $script = 'index' ) {
  */
 function wfGetScriptUrl() {
        if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
-               #
-               # as it was called, minus the query string.
-               #
-               # Some sites use Apache rewrite rules to handle subdomains,
-               # and have PHP set up in a weird way that causes PHP_SELF
-               # to contain the rewritten URL instead of the one that the
-               # outside world sees.
-               #
-               # If in this mode, use SCRIPT_URL instead, which mod_rewrite
-               # provides containing the "before" URL.
+               /* as it was called, minus the query string.
+                *
+                * Some sites use Apache rewrite rules to handle subdomains,
+                * and have PHP set up in a weird way that causes PHP_SELF
+                * to contain the rewritten URL instead of the one that the
+                * outside world sees.
+                *
+                * If in this mode, use SCRIPT_URL instead, which mod_rewrite
+                * provides containing the "before" URL.
+                */
                return $_SERVER['SCRIPT_NAME'];
        } else {
                return $_SERVER['URL'];
@@ -3912,12 +3940,13 @@ function wfTransactionalTimeLimit() {
  * Converts shorthand byte notation to integer form
  *
  * @param string $string
+ * @param int $default Returned if $string is empty
  * @return int
  */
-function wfShorthandToInteger( $string = '' ) {
+function wfShorthandToInteger( $string = '', $default = -1 ) {
        $string = trim( $string );
        if ( $string === '' ) {
-               return -1;
+               return $default;
        }
        $last = $string[strlen( $string ) - 1];
        $val = intval( $string );
@@ -4072,13 +4101,10 @@ function wfUnpack( $format, $data, $length = false ) {
  * @return bool
  */
 function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
-       static $badImageCache = null; // based on bad_image_list msg
-
-       # Handle redirects
-       $redirectTitle = RepoGroup::singleton()->checkRedirect( Title::makeTitle( NS_FILE, $name ) );
-       if ( $redirectTitle ) {
-               $name = $redirectTitle->getDBkey();
-       }
+       # Handle redirects; callers almost always hit wfFindFile() anyway,
+       # so just use that method because it has a fast process cache.
+       $file = wfFindFile( $name ); // get the final name
+       $name = $file ? $file->getTitle()->getDBkey() : $name;
 
        # Run the extension hook
        $bad = false;
@@ -4086,10 +4112,11 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
                return $bad;
        }
 
-       $cacheable = ( $blacklist === null );
-       if ( $cacheable && $badImageCache !== null ) {
-               $badImages = $badImageCache;
-       } else { // cache miss
+       $cache = ObjectCache::newAccelerator( 'hash' );
+       $key = wfMemcKey( 'bad-image-list', ( $blacklist === null ) ? 'default' : md5( $blacklist ) );
+       $badImages = $cache->get( $key );
+
+       if ( $badImages === false ) { // cache miss
                if ( $blacklist === null ) {
                        $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list
                }
@@ -4125,13 +4152,12 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
                                $badImages[$imageDBkey] = $exceptions;
                        }
                }
-               if ( $cacheable ) {
-                       $badImageCache = $badImages;
-               }
+               $cache->set( $key, $badImages, 60 );
        }
 
        $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
        $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
+
        return $bad;
 }