SECURITY: Don't use m modifier when checking link prefix
[lhc/web/wiklou.git] / includes / upload / UploadBase.php
index 17fcab8..ba5171f 100644 (file)
  * @author Michael Dale
  */
 abstract class UploadBase {
+       /** @var string Local file system path to the file to upload (or a local copy) */
        protected $mTempPath;
+       /** @var TempFSFile|null Wrapper to handle deleting the temp file */
+       protected $tempFileObj;
+
        protected $mDesiredDestName, $mDestName, $mRemoveTempFile, $mSourceType;
        protected $mTitle = false, $mTitleError = 0;
        protected $mFilteredName, $mFinalExtension;
@@ -44,13 +48,13 @@ abstract class UploadBase {
        protected $mBlackListedExtensions;
        protected $mJavaDetected, $mSVGNSError;
 
-       protected static $safeXmlEncodings = array(
+       protected static $safeXmlEncodings = [
                'UTF-8',
                'ISO-8859-1',
                'ISO-8859-2',
                'UTF-16',
                'UTF-32'
-       );
+       ];
 
        const SUCCESS = 0;
        const OK = 0;
@@ -71,7 +75,7 @@ abstract class UploadBase {
         * @return string
         */
        public function getVerificationErrorCode( $error ) {
-               $code_to_status = array(
+               $code_to_status = [
                        self::EMPTY_FILE => 'empty-file',
                        self::FILE_TOO_LARGE => 'file-too-large',
                        self::FILETYPE_MISSING => 'filetype-missing',
@@ -83,7 +87,7 @@ abstract class UploadBase {
                        self::HOOK_ABORTED => 'hookaborted',
                        self::WINDOWS_NONASCII_FILENAME => 'windows-nonascii-filename',
                        self::FILENAME_TOO_LONG => 'filename-toolong',
-               );
+               ];
                if ( isset( $code_to_status[$error] ) ) {
                        return $code_to_status[$error];
                }
@@ -116,7 +120,7 @@ abstract class UploadBase {
         * @return bool|string
         */
        public static function isAllowed( $user ) {
-               foreach ( array( 'upload', 'edit' ) as $permission ) {
+               foreach ( [ 'upload', 'edit' ] as $permission ) {
                        if ( !$user->isAllowed( $permission ) ) {
                                return $permission;
                        }
@@ -136,7 +140,7 @@ abstract class UploadBase {
        }
 
        // Upload handlers. Should probably just be a global.
-       private static $uploadHandlers = array( 'Stash', 'File', 'Url' );
+       private static $uploadHandlers = [ 'Stash', 'File', 'Url' ];
 
        /**
         * Create a form of UploadBase depending on wpSourceType and initializes it
@@ -157,7 +161,7 @@ abstract class UploadBase {
 
                // Give hooks the chance to handle this request
                $className = null;
-               Hooks::run( 'UploadCreateFromRequest', array( $type, &$className ) );
+               Hooks::run( 'UploadCreateFromRequest', [ $type, &$className ] );
                if ( is_null( $className ) ) {
                        $className = 'UploadFrom' . $type;
                        wfDebug( __METHOD__ . ": class name: $className\n" );
@@ -167,12 +171,12 @@ abstract class UploadBase {
                }
 
                // Check whether this upload class is enabled
-               if ( !call_user_func( array( $className, 'isEnabled' ) ) ) {
+               if ( !call_user_func( [ $className, 'isEnabled' ] ) ) {
                        return null;
                }
 
                // Check whether the request is valid
-               if ( !call_user_func( array( $className, 'isValidRequest' ), $request ) ) {
+               if ( !call_user_func( [ $className, 'isValidRequest' ], $request ) ) {
                        return null;
                }
 
@@ -219,8 +223,8 @@ abstract class UploadBase {
                if ( FileBackend::isStoragePath( $tempPath ) ) {
                        throw new MWException( __METHOD__ . " given storage path `$tempPath`." );
                }
-               $this->mTempPath = $tempPath;
-               $this->mFileSize = $fileSize;
+
+               $this->setTempFile( $tempPath, $fileSize );
                $this->mRemoveTempFile = $removeTempFile;
        }
 
@@ -231,6 +235,23 @@ abstract class UploadBase {
         */
        abstract public function initializeFromRequest( &$request );
 
+       /**
+        * @param string $tempPath File system path to temporary file containing the upload
+        * @param integer $fileSize
+        */
+       protected function setTempFile( $tempPath, $fileSize = null ) {
+               $this->mTempPath = $tempPath;
+               $this->mFileSize = $fileSize ?: null;
+               if ( strlen( $this->mTempPath ) && file_exists( $this->mTempPath ) ) {
+                       $this->tempFileObj = new TempFSFile( $this->mTempPath );
+                       if ( !$fileSize ) {
+                               $this->mFileSize = filesize( $this->mTempPath );
+                       }
+               } else {
+                       $this->tempFileObj = null;
+               }
+       }
+
        /**
         * Fetch the file. Usually a no-op
         * @return Status
@@ -295,7 +316,7 @@ abstract class UploadBase {
                 * If there was no filename or a zero size given, give up quick.
                 */
                if ( $this->isEmptyFile() ) {
-                       return array( 'status' => self::EMPTY_FILE );
+                       return [ 'status' => self::EMPTY_FILE ];
                }
 
                /**
@@ -303,10 +324,10 @@ abstract class UploadBase {
                 */
                $maxSize = self::getMaxUploadSize( $this->getSourceType() );
                if ( $this->mFileSize > $maxSize ) {
-                       return array(
+                       return [
                                'status' => self::FILE_TOO_LARGE,
                                'max' => $maxSize,
-                       );
+                       ];
                }
 
                /**
@@ -316,10 +337,10 @@ abstract class UploadBase {
                 */
                $verification = $this->verifyFile();
                if ( $verification !== true ) {
-                       return array(
+                       return [
                                'status' => self::VERIFICATION_ERROR,
                                'details' => $verification
-                       );
+                       ];
                }
 
                /**
@@ -332,12 +353,12 @@ abstract class UploadBase {
 
                $error = '';
                if ( !Hooks::run( 'UploadVerification',
-                       array( $this->mDestName, $this->mTempPath, &$error ) )
+                       [ $this->mDestName, $this->mTempPath, &$error ] )
                ) {
-                       return array( 'status' => self::HOOK_ABORTED, 'error' => $error );
+                       return [ 'status' => self::HOOK_ABORTED, 'error' => $error ];
                }
 
-               return array( 'status' => self::OK );
+               return [ 'status' => self::OK ];
        }
 
        /**
@@ -349,7 +370,7 @@ abstract class UploadBase {
        public function validateName() {
                $nt = $this->getTitle();
                if ( is_null( $nt ) ) {
-                       $result = array( 'status' => $this->mTitleError );
+                       $result = [ 'status' => $this->mTitleError ];
                        if ( $this->mTitleError == self::ILLEGAL_FILENAME ) {
                                $result['filtered'] = $this->mFilteredName;
                        }
@@ -382,7 +403,7 @@ abstract class UploadBase {
                        wfDebug( "mime: <$mime> extension: <{$this->mFinalExtension}>\n" );
                        global $wgMimeTypeBlacklist;
                        if ( $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
-                               return array( 'filetype-badmime', $mime );
+                               return [ 'filetype-badmime', $mime ];
                        }
 
                        # Check what Internet Explorer would detect
@@ -395,7 +416,7 @@ abstract class UploadBase {
                        $ieTypes = $magic->getIEMimeTypes( $this->mTempPath, $chunk, $extMime );
                        foreach ( $ieTypes as $ieType ) {
                                if ( $this->checkFileExtension( $ieType, $wgMimeTypeBlacklist ) ) {
-                                       return array( 'filetype-bad-ie-mime', $ieType );
+                                       return [ 'filetype-bad-ie-mime', $ieType ];
                                }
                        }
                }
@@ -422,7 +443,7 @@ abstract class UploadBase {
                if ( $wgVerifyMimeType ) {
                        # XXX: Missing extension will be caught by validateName() via getTitle()
                        if ( $this->mFinalExtension != '' && !$this->verifyExtension( $mime, $this->mFinalExtension ) ) {
-                               return array( 'filetype-mime-mismatch', $this->mFinalExtension, $mime );
+                               return [ 'filetype-mime-mismatch', $this->mFinalExtension, $mime ];
                        }
                }
 
@@ -446,7 +467,7 @@ abstract class UploadBase {
                        }
                }
 
-               Hooks::run( 'UploadVerifyFile', array( $this, $mime, &$status ) );
+               Hooks::run( 'UploadVerifyFile', [ $this, $mime, &$status ] );
                if ( $status !== true ) {
                        return $status;
                }
@@ -482,7 +503,7 @@ abstract class UploadBase {
                # check for htmlish code and javascript
                if ( !$wgDisableUploadScriptChecks ) {
                        if ( self::detectScript( $this->mTempPath, $mime, $this->mFinalExtension ) ) {
-                               return array( 'uploadscripted' );
+                               return [ 'uploadscripted' ];
                        }
                        if ( $this->mFinalExtension == 'svg' || $mime == 'image/svg+xml' ) {
                                $svgStatus = $this->detectScriptInSvg( $this->mTempPath, true );
@@ -497,7 +518,7 @@ abstract class UploadBase {
                if ( !$wgAllowJavaUploads ) {
                        $this->mJavaDetected = false;
                        $zipStatus = ZipDirectoryReader::read( $this->mTempPath,
-                               array( $this, 'zipEntryCallback' ) );
+                               [ $this, 'zipEntryCallback' ] );
                        if ( !$zipStatus->isOK() ) {
                                $errors = $zipStatus->getErrorsArray();
                                $error = reset( $errors );
@@ -506,14 +527,14 @@ abstract class UploadBase {
                                }
                        }
                        if ( $this->mJavaDetected ) {
-                               return array( 'uploadjava' );
+                               return [ 'uploadjava' ];
                        }
                }
 
                # Scan the uploaded file for viruses
                $virus = $this->detectVirus( $this->mTempPath );
                if ( $virus ) {
-                       return array( 'uploadvirus', $virus );
+                       return [ 'uploadvirus', $virus ];
                }
 
                return true;
@@ -525,7 +546,7 @@ abstract class UploadBase {
         * @param array $entry
         */
        function zipEntryCallback( $entry ) {
-               $names = array( $entry['name'] );
+               $names = [ $entry['name'] ];
 
                // If there is a null character, cut off the name at it, because JDK's
                // ZIP_GetEntry() uses strcmp() if the name hashes match. If a file name
@@ -582,7 +603,7 @@ abstract class UploadBase {
                if ( !$nt->exists() ) {
                        $permErrorsCreate = $nt->getUserPermissionsErrors( 'create', $user );
                } else {
-                       $permErrorsCreate = array();
+                       $permErrorsCreate = [];
                }
                if ( $permErrors || $permErrorsUpload || $permErrorsCreate ) {
                        $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsUpload, $permErrors ) );
@@ -593,7 +614,7 @@ abstract class UploadBase {
 
                $overwriteError = $this->checkOverwrite( $user );
                if ( $overwriteError !== true ) {
-                       return array( $overwriteError );
+                       return [ $overwriteError ];
                }
 
                return true;
@@ -609,7 +630,7 @@ abstract class UploadBase {
        public function checkWarnings() {
                global $wgLang;
 
-               $warnings = array();
+               $warnings = [];
 
                $localFile = $this->getLocalFile();
                $localFile->load( File::READ_LATEST );
@@ -624,9 +645,6 @@ abstract class UploadBase {
 
                if ( $this->mDesiredDestName != $filename && $comparableName != $filename ) {
                        $warnings['badfilename'] = $filename;
-                       // Debugging for bug 62241
-                       wfDebugLog( 'upload', "Filename: '$filename', mDesiredDestName: "
-                               . "'$this->mDesiredDestName', comparableName: '$comparableName'" );
                }
 
                // Check whether the file extension is on the unwanted list
@@ -634,18 +652,18 @@ abstract class UploadBase {
                if ( $wgCheckFileExtensions ) {
                        $extensions = array_unique( $wgFileExtensions );
                        if ( !$this->checkFileExtension( $this->mFinalExtension, $extensions ) ) {
-                               $warnings['filetype-unwanted-type'] = array( $this->mFinalExtension,
-                                       $wgLang->commaList( $extensions ), count( $extensions ) );
+                               $warnings['filetype-unwanted-type'] = [ $this->mFinalExtension,
+                                       $wgLang->commaList( $extensions ), count( $extensions ) ];
                        }
                }
 
                global $wgUploadSizeWarning;
                if ( $wgUploadSizeWarning && ( $this->mFileSize > $wgUploadSizeWarning ) ) {
-                       $warnings['large-file'] = array( $wgUploadSizeWarning, $this->mFileSize );
+                       $warnings['large-file'] = [ $wgUploadSizeWarning, $this->mFileSize ];
                }
 
                if ( $this->mFileSize == 0 ) {
-                       $warnings['emptyfile'] = true;
+                       $warnings['empty-file'] = true;
                }
 
                $exists = self::getExistsWarning( $localFile );
@@ -690,12 +708,14 @@ abstract class UploadBase {
         *
         * @param string $comment
         * @param string $pageText
-        * @param bool $watch
+        * @param bool $watch Whether the file page should be added to user's watchlist.
+        *   (This doesn't check $user's permissions.)
         * @param User $user
-        *
+        * @param string[] $tags Change tags to add to the log entry and page revision.
+        *   (This doesn't check $user's permissions.)
         * @return Status Indicating the whether the upload succeeded.
         */
-       public function performUpload( $comment, $pageText, $watch, $user ) {
+       public function performUpload( $comment, $pageText, $watch, $user, $tags = [] ) {
                $this->getLocalFile()->load( File::READ_LATEST );
 
                $status = $this->getLocalFile()->upload(
@@ -705,7 +725,8 @@ abstract class UploadBase {
                        File::DELETE_SOURCE,
                        $this->mFileProps,
                        false,
-                       $user
+                       $user,
+                       $tags
                );
 
                if ( $status->isGood() ) {
@@ -713,10 +734,10 @@ abstract class UploadBase {
                                WatchAction::doWatch(
                                        $this->getLocalFile()->getTitle(),
                                        $user,
-                                       WatchedItem::IGNORE_USER_RIGHTS
+                                       User::IGNORE_USER_RIGHTS
                                );
                        }
-                       Hooks::run( 'UploadComplete', array( &$this ) );
+                       Hooks::run( 'UploadComplete', [ &$this ] );
 
                        $this->postProcessUpload();
                }
@@ -732,7 +753,7 @@ abstract class UploadBase {
        public function postProcessUpload() {
                global $wgUploadThumbnailRenderMap;
 
-               $jobs = array();
+               $jobs = [];
 
                $sizes = $wgUploadThumbnailRenderMap;
                rsort( $sizes );
@@ -743,7 +764,7 @@ abstract class UploadBase {
                        if ( $file->isVectorized() || $file->getWidth() > $size ) {
                                $jobs[] = new ThumbnailRenderJob(
                                        $file->getTitle(),
-                                       array( 'transformParams' => array( 'width' => $size ) )
+                                       [ 'transformParams' => [ 'width' => $size ] ]
                                );
                        }
                }
@@ -828,7 +849,7 @@ abstract class UploadBase {
                                        # Fix up the other variables
                                        $this->mFilteredName .= ".{$this->mFinalExtension}";
                                        $nt = Title::makeTitleSafe( NS_FILE, $this->mFilteredName );
-                                       $ext = array( $this->mFinalExtension );
+                                       $ext = [ $this->mFinalExtension ];
                                }
                        }
                }
@@ -949,9 +970,10 @@ abstract class UploadBase {
         * on exit to clean up.
         */
        public function cleanupTempFile() {
-               if ( $this->mRemoveTempFile && $this->mTempPath && file_exists( $this->mTempPath ) ) {
-                       wfDebug( __METHOD__ . ": Removing temporary file {$this->mTempPath}\n" );
-                       unlink( $this->mTempPath );
+               if ( $this->mRemoveTempFile && $this->tempFileObj ) {
+                       // Delete when all relevant TempFSFile handles go out of scope
+                       wfDebug( __METHOD__ . ": Marked temporary file '{$this->mTempPath}' for removal\n" );
+                       $this->tempFileObj->autocollect();
                }
        }
 
@@ -972,7 +994,7 @@ abstract class UploadBase {
                $bits = explode( '.', $filename );
                $basename = array_shift( $bits );
 
-               return array( $basename, $bits );
+               return [ $basename, $bits ];
        }
 
        /**
@@ -1125,7 +1147,7 @@ abstract class UploadBase {
                 * Also returns true if Safari would mistake the given file for HTML
                 * when served with a generic content-type.
                 */
-               $tags = array(
+               $tags = [
                        '<a href',
                        '<body',
                        '<head',
@@ -1134,7 +1156,7 @@ abstract class UploadBase {
                        '<pre',
                        '<script', # also in safari
                        '<table'
-               );
+               ];
 
                if ( !$wgAllowTitlesInSVG && $extension !== 'svg' && $mime !== 'image/svg' ) {
                        $tags[] = '<title';
@@ -1216,7 +1238,7 @@ abstract class UploadBase {
 
                // It's possible the file is encoded with multi-byte encoding, so re-encode attempt to
                // detect the encoding in case is specifies an encoding not whitelisted in self::$safeXmlEncodings
-               $attemptEncodings = array( 'UTF-16', 'UTF-16BE', 'UTF-32', 'UTF-32BE' );
+               $attemptEncodings = [ 'UTF-16', 'UTF-16BE', 'UTF-32', 'UTF-32BE' ];
                foreach ( $attemptEncodings as $encoding ) {
                        MediaWiki\suppressWarnings();
                        $str = iconv( $encoding, 'UTF-8', $contents );
@@ -1250,17 +1272,17 @@ abstract class UploadBase {
                $this->mSVGNSError = false;
                $check = new XmlTypeCheck(
                        $filename,
-                       array( $this, 'checkSvgScriptCallback' ),
+                       [ $this, 'checkSvgScriptCallback' ],
                        true,
-                       array( 'processing_instruction_handler' => 'UploadBase::checkSvgPICallback' )
+                       [ 'processing_instruction_handler' => 'UploadBase::checkSvgPICallback' ]
                );
                if ( $check->wellFormed !== true ) {
                        // Invalid xml (bug 58553)
                        // But only when non-partial (bug 65724)
-                       return $partial ? false : array( 'uploadinvalidxml' );
+                       return $partial ? false : [ 'uploadinvalidxml' ];
                } elseif ( $check->filterMatch ) {
                        if ( $this->mSVGNSError ) {
-                               return array( 'uploadscriptednamespace', $this->mSVGNSError );
+                               return [ 'uploadscriptednamespace', $this->mSVGNSError ];
                        }
 
                        return $check->filterMatchType;
@@ -1278,7 +1300,7 @@ abstract class UploadBase {
        public static function checkSvgPICallback( $target, $data ) {
                // Don't allow external stylesheets (bug 57550)
                if ( preg_match( '/xml-stylesheet/i', $target ) ) {
-                       return array( 'upload-scripted-pi-callback' );
+                       return [ 'upload-scripted-pi-callback' ];
                }
 
                return false;
@@ -1296,7 +1318,7 @@ abstract class UploadBase {
 
                // We specifically don't include:
                // http://www.w3.org/1999/xhtml (bug 60771)
-               static $validNamespaces = array(
+               static $validNamespaces = [
                        '',
                        'adobe:ns:meta/',
                        'http://creativecommons.org/ns#',
@@ -1334,7 +1356,7 @@ abstract class UploadBase {
                        'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
                        'http://www.w3.org/2000/svg',
                        'http://www.w3.org/tr/rec-rdf-syntax/',
-               );
+               ];
 
                if ( !in_array( $namespace, $validNamespaces ) ) {
                        wfDebug( __METHOD__ . ": Non-svg namespace '$namespace' in uploaded file.\n" );
@@ -1350,7 +1372,7 @@ abstract class UploadBase {
                if ( $strippedElement == 'script' ) {
                        wfDebug( __METHOD__ . ": Found script element '$element' in uploaded file.\n" );
 
-                       return array( 'uploaded-script-svg', $strippedElement );
+                       return [ 'uploaded-script-svg', $strippedElement ];
                }
 
                # e.g., <svg xmlns="http://www.w3.org/2000/svg">
@@ -1358,21 +1380,21 @@ abstract class UploadBase {
                if ( $strippedElement == 'handler' ) {
                        wfDebug( __METHOD__ . ": Found scriptable element '$element' in uploaded file.\n" );
 
-                       return array( 'uploaded-script-svg', $strippedElement );
+                       return [ 'uploaded-script-svg', $strippedElement ];
                }
 
                # SVG reported in Feb '12 that used xml:stylesheet to generate javascript block
                if ( $strippedElement == 'stylesheet' ) {
                        wfDebug( __METHOD__ . ": Found scriptable element '$element' in uploaded file.\n" );
 
-                       return array( 'uploaded-script-svg', $strippedElement );
+                       return [ 'uploaded-script-svg', $strippedElement ];
                }
 
                # Block iframes, in case they pass the namespace check
                if ( $strippedElement == 'iframe' ) {
                        wfDebug( __METHOD__ . ": iframe in uploaded file.\n" );
 
-                       return array( 'uploaded-script-svg', $strippedElement );
+                       return [ 'uploaded-script-svg', $strippedElement ];
                }
 
                # Check <style> css
@@ -1380,7 +1402,7 @@ abstract class UploadBase {
                        && self::checkCssFragment( Sanitizer::normalizeCss( $data ) )
                ) {
                        wfDebug( __METHOD__ . ": hostile css in style element.\n" );
-                       return array( 'uploaded-hostile-svg' );
+                       return [ 'uploaded-hostile-svg' ];
                }
 
                foreach ( $attribs as $attrib => $value ) {
@@ -1391,7 +1413,7 @@ abstract class UploadBase {
                                wfDebug( __METHOD__
                                        . ": Found event-handler attribute '$attrib'='$value' in uploaded file.\n" );
 
-                               return array( 'uploaded-event-handler-on-svg', $attrib, $value );
+                               return [ 'uploaded-event-handler-on-svg', $attrib, $value ];
                        }
 
                        # href with non-local target (don't allow http://, javascript:, etc)
@@ -1400,12 +1422,12 @@ abstract class UploadBase {
                                && strpos( $value, '#' ) !== 0
                        ) {
                                if ( !( $strippedElement === 'a'
-                                       && preg_match( '!^https?://!im', $value ) )
+                                       && preg_match( '!^https?://!i', $value ) )
                                ) {
                                        wfDebug( __METHOD__ . ": Found href attribute <$strippedElement "
                                                . "'$attrib'='$value' in uploaded file.\n" );
 
-                                       return array( 'uploaded-href-attribute-svg', $strippedElement, $attrib, $value );
+                                       return [ 'uploaded-href-attribute-svg', $strippedElement, $attrib, $value ];
                                }
                        }
 
@@ -1420,7 +1442,7 @@ abstract class UploadBase {
                                if ( !preg_match( "!^data:\s*image/(gif|jpeg|jpg|png)$parameters,!i", $value ) ) {
                                        wfDebug( __METHOD__ . ": Found href to unwhitelisted data: uri "
                                                . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
-                                       return array( 'uploaded-href-unsafe-target-svg', $strippedElement, $attrib, $value );
+                                       return [ 'uploaded-href-unsafe-target-svg', $strippedElement, $attrib, $value ];
                                }
                        }
 
@@ -1432,7 +1454,7 @@ abstract class UploadBase {
                                wfDebug( __METHOD__ . ": Found animate that might be changing href using from "
                                        . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
 
-                               return array( 'uploaded-animate-svg', $strippedElement, $attrib, $value );
+                               return [ 'uploaded-animate-svg', $strippedElement, $attrib, $value ];
                        }
 
                        # use set/animate to add event-handler attribute to parent
@@ -1443,7 +1465,7 @@ abstract class UploadBase {
                                wfDebug( __METHOD__ . ": Found svg setting event-handler attribute with "
                                        . "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
 
-                               return array( 'uploaded-setting-event-handler-svg', $strippedElement, $stripped, $value );
+                               return [ 'uploaded-setting-event-handler-svg', $strippedElement, $stripped, $value ];
                        }
 
                        # use set to add href attribute to parent element
@@ -1453,7 +1475,7 @@ abstract class UploadBase {
                        ) {
                                wfDebug( __METHOD__ . ": Found svg setting href attribute '$value' in uploaded file.\n" );
 
-                               return array( 'uploaded-setting-href-svg' );
+                               return [ 'uploaded-setting-href-svg' ];
                        }
 
                        # use set to add a remote / data / script target to an element
@@ -1463,7 +1485,7 @@ abstract class UploadBase {
                        ) {
                                wfDebug( __METHOD__ . ": Found svg setting attribute to '$value' in uploaded file.\n" );
 
-                               return array( 'uploaded-wrong-setting-svg', $value );
+                               return [ 'uploaded-wrong-setting-svg', $value ];
                        }
 
                        # use handler attribute with remote / data / script
@@ -1471,7 +1493,7 @@ abstract class UploadBase {
                                wfDebug( __METHOD__ . ": Found svg setting handler with remote/data/script "
                                        . "'$attrib'='$value' in uploaded file.\n" );
 
-                               return array( 'uploaded-setting-handler-svg', $attrib, $value );
+                               return [ 'uploaded-setting-handler-svg', $attrib, $value ];
                        }
 
                        # use CSS styles to bring in remote code
@@ -1480,18 +1502,18 @@ abstract class UploadBase {
                        ) {
                                wfDebug( __METHOD__ . ": Found svg setting a style with "
                                        . "remote url '$attrib'='$value' in uploaded file.\n" );
-                               return array( 'uploaded-remote-url-svg', $attrib, $value );
+                               return [ 'uploaded-remote-url-svg', $attrib, $value ];
                        }
 
                        # Several attributes can include css, css character escaping isn't allowed
-                       $cssAttrs = array( 'font', 'clip-path', 'fill', 'filter', 'marker',
-                               'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke' );
+                       $cssAttrs = [ 'font', 'clip-path', 'fill', 'filter', 'marker',
+                               'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke' ];
                        if ( in_array( $stripped, $cssAttrs )
                                && self::checkCssFragment( $value )
                        ) {
                                wfDebug( __METHOD__ . ": Found svg setting a style with "
                                        . "remote url '$attrib'='$value' in uploaded file.\n" );
-                               return array( 'uploaded-remote-url-svg', $attrib, $value );
+                               return [ 'uploaded-remote-url-svg', $attrib, $value ];
                        }
 
                        # image filters can pull in url, which could be svg that executes scripts
@@ -1502,7 +1524,7 @@ abstract class UploadBase {
                                wfDebug( __METHOD__ . ": Found image filter with url: "
                                        . "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
 
-                               return array( 'uploaded-image-filter-svg', $strippedElement, $stripped, $value );
+                               return [ 'uploaded-image-filter-svg', $strippedElement, $stripped, $value ];
                        }
                }
 
@@ -1572,7 +1594,7 @@ abstract class UploadBase {
                $name = array_pop( $parts );
                $ns = implode( ':', $parts );
 
-               return array( $ns, $name );
+               return [ $ns, $name ];
        }
 
        /**
@@ -1608,7 +1630,7 @@ abstract class UploadBase {
                if ( !$wgAntivirusSetup[$wgAntivirus] ) {
                        wfDebug( __METHOD__ . ": unknown virus scanner: $wgAntivirus\n" );
                        $wgOut->wrapWikiMsg( "<div class=\"error\">\n$1\n</div>",
-                               array( 'virus-badscanner', $wgAntivirus ) );
+                               [ 'virus-badscanner', $wgAntivirus ] );
 
                        return wfMessage( 'virus-unknownscanner' )->text() . " $wgAntivirus";
                }
@@ -1655,7 +1677,7 @@ abstract class UploadBase {
                        wfDebug( __METHOD__ . ": failed to scan $file (code $exitCode).\n" );
 
                        $output = $wgAntivirusRequired
-                               ? wfMessage( 'virus-scanfailed', array( $exitCode ) )->text()
+                               ? wfMessage( 'virus-scanfailed', [ $exitCode ] )->text()
                                : null;
                } elseif ( $mappedCode === AV_SCAN_ABORTED ) {
                        # scan failed because filetype is unknown (probably imune)
@@ -1671,7 +1693,7 @@ abstract class UploadBase {
                        if ( !$output ) {
                                $output = true; # if there's no output, return true
                        } elseif ( $msgPattern ) {
-                               $groups = array();
+                               $groups = [];
                                if ( preg_match( $msgPattern, $output, $groups ) ) {
                                        if ( $groups[1] ) {
                                                $output = $groups[1];
@@ -1699,7 +1721,7 @@ abstract class UploadBase {
                $file->load( File::READ_LATEST );
                if ( $file->exists() ) {
                        if ( !self::userCanReUpload( $user, $file ) ) {
-                               return array( 'fileexists-forbidden', $file->getName() );
+                               return [ 'fileexists-forbidden', $file->getName() ];
                        } else {
                                return true;
                        }
@@ -1708,9 +1730,9 @@ abstract class UploadBase {
                /* Check shared conflicts: if the local file does not exist, but
                 * wfFindFile finds a file, it exists in a shared repository.
                 */
-               $file = wfFindFile( $this->getTitle(), array( 'latest' => true ) );
+               $file = wfFindFile( $this->getTitle(), [ 'latest' => true ] );
                if ( $file && !$user->isAllowed( 'reupload-shared' ) ) {
-                       return array( 'fileexists-shared-forbidden', $file->getName() );
+                       return [ 'fileexists-shared-forbidden', $file->getName() ];
                }
 
                return true;
@@ -1752,11 +1774,11 @@ abstract class UploadBase {
         */
        public static function getExistsWarning( $file ) {
                if ( $file->exists() ) {
-                       return array( 'warning' => 'exists', 'file' => $file );
+                       return [ 'warning' => 'exists', 'file' => $file ];
                }
 
                if ( $file->getTitle()->getArticleID() ) {
-                       return array( 'warning' => 'page-exists', 'file' => $file );
+                       return [ 'warning' => 'page-exists', 'file' => $file ];
                }
 
                if ( strpos( $file->getName(), '.' ) == false ) {
@@ -1779,11 +1801,11 @@ abstract class UploadBase {
                        $file_lc = wfLocalFile( $nt_lc );
 
                        if ( $file_lc->exists() ) {
-                               return array(
+                               return [
                                        'warning' => 'exists-normalized',
                                        'file' => $file,
                                        'normalizedFile' => $file_lc
-                               );
+                               ];
                        }
                }
 
@@ -1791,11 +1813,11 @@ abstract class UploadBase {
                $similarFiles = RepoGroup::singleton()->getLocalRepo()->findFilesByPrefix(
                        "{$partname}.", 1 );
                if ( count( $similarFiles ) ) {
-                       return array(
+                       return [
                                'warning' => 'exists-normalized',
                                'file' => $file,
                                'normalizedFile' => $similarFiles[0],
-                       );
+                       ];
                }
 
                if ( self::isThumbName( $file->getName() ) ) {
@@ -1806,28 +1828,28 @@ abstract class UploadBase {
                        );
                        $file_thb = wfLocalFile( $nt_thb );
                        if ( $file_thb->exists() ) {
-                               return array(
+                               return [
                                        'warning' => 'thumb',
                                        'file' => $file,
                                        'thumbFile' => $file_thb
-                               );
+                               ];
                        } else {
                                // File does not exist, but we just don't like the name
-                               return array(
+                               return [
                                        'warning' => 'thumb-name',
                                        'file' => $file,
                                        'thumbFile' => $file_thb
-                               );
+                               ];
                        }
                }
 
                foreach ( self::getFilenamePrefixBlacklist() as $prefix ) {
                        if ( substr( $partname, 0, strlen( $prefix ) ) == $prefix ) {
-                               return array(
+                               return [
                                        'warning' => 'bad-prefix',
                                        'file' => $file,
                                        'prefix' => $prefix
-                               );
+                               ];
                        }
                }
 
@@ -1856,7 +1878,7 @@ abstract class UploadBase {
         * @return array List of prefixes
         */
        public static function getFilenamePrefixBlacklist() {
-               $blacklist = array();
+               $blacklist = [];
                $message = wfMessage( 'filename-prefix-blacklist' )->inContentLanguage();
                if ( !$message->isDisabled() ) {
                        $lines = explode( "\n", $message->plain() );
@@ -1919,6 +1941,9 @@ abstract class UploadBase {
        }
 
        /**
+        * Get the MediaWiki maximum uploaded file size for given type of upload, based on
+        * $wgMaxUploadSize.
+        *
         * @param null|string $forType
         * @return int
         */
@@ -1936,6 +1961,25 @@ abstract class UploadBase {
                }
        }
 
+       /**
+        * Get the PHP maximum uploaded file size, based on ini settings. If there is no limit or the
+        * limit can't be guessed, returns a very large number (PHP_INT_MAX).
+        *
+        * @since 1.27
+        * @return int
+        */
+       public static function getMaxPhpUploadSize() {
+               $phpMaxFileSize = wfShorthandToInteger(
+                       ini_get( 'upload_max_filesize' ) ?: ini_get( 'hhvm.server.upload.upload_max_file_size' ),
+                       PHP_INT_MAX
+               );
+               $phpMaxPostSize = wfShorthandToInteger(
+                       ini_get( 'post_max_size' ) ?: ini_get( 'hhvm.server.max_post_size' ),
+                       PHP_INT_MAX
+               ) ?: PHP_INT_MAX;
+               return min( $phpMaxFileSize, $phpMaxPostSize );
+       }
+
        /**
         * Get the current status of a chunked upload (used for polling)
         *