Add some SVG element namespaces
[lhc/web/wiklou.git] / includes / upload / UploadBase.php
index 746c16d..b8ca434 100644 (file)
@@ -44,7 +44,13 @@ abstract class UploadBase {
        protected $mBlackListedExtensions;
        protected $mJavaDetected, $mSVGNSError;
 
-       protected static $safeXmlEncodings = array( 'UTF-8', 'ISO-8859-1', 'ISO-8859-2', 'UTF-16', 'UTF-32' );
+       protected static $safeXmlEncodings = array(
+               'UTF-8',
+               'ISO-8859-1',
+               'ISO-8859-2',
+               'UTF-16',
+               'UTF-32'
+       );
 
        const SUCCESS = 0;
        const OK = 0;
@@ -120,11 +126,12 @@ abstract class UploadBase {
                                return $permission;
                        }
                }
+
                return true;
        }
 
        // Upload handlers. Should probably just be a global.
-       static $uploadHandlers = array( 'Stash', 'File', 'Url' );
+       private static $uploadHandlers = array( 'Stash', 'File', 'Url' );
 
        /**
         * Create a form of UploadBase depending on wpSourceType and initializes it
@@ -167,6 +174,7 @@ abstract class UploadBase {
                $handler = new $className;
 
                $handler->initializeFromRequest( $request );
+
                return $handler;
        }
 
@@ -179,7 +187,8 @@ abstract class UploadBase {
                return false;
        }
 
-       public function __construct() {}
+       public function __construct() {
+       }
 
        /**
         * Returns the upload type. Should be overridden by child classes
@@ -256,8 +265,9 @@ abstract class UploadBase {
                wfProfileIn( __METHOD__ );
                $repo = RepoGroup::singleton()->getLocalRepo();
                if ( $repo->isVirtualUrl( $srcPath ) ) {
-                       // @todo just make uploads work with storage paths
-                       // UploadFromStash loads files via virtual URLs
+                       /** @todo Just make uploads work with storage paths UploadFromStash
+                        *  loads files via virtual URLs.
+                        */
                        $tmpFile = $repo->getLocalCopy( $srcPath );
                        if ( $tmpFile ) {
                                $tmpFile->bind( $this ); // keep alive with $this
@@ -267,6 +277,7 @@ abstract class UploadBase {
                        $path = $srcPath;
                }
                wfProfileOut( __METHOD__ );
+
                return $path;
        }
 
@@ -282,6 +293,7 @@ abstract class UploadBase {
                 */
                if ( $this->isEmptyFile() ) {
                        wfProfileOut( __METHOD__ );
+
                        return array( 'status' => self::EMPTY_FILE );
                }
 
@@ -291,6 +303,7 @@ abstract class UploadBase {
                $maxSize = self::getMaxUploadSize( $this->getSourceType() );
                if ( $this->mFileSize > $maxSize ) {
                        wfProfileOut( __METHOD__ );
+
                        return array(
                                'status' => self::FILE_TOO_LARGE,
                                'max' => $maxSize,
@@ -305,6 +318,7 @@ abstract class UploadBase {
                $verification = $this->verifyFile();
                if ( $verification !== true ) {
                        wfProfileOut( __METHOD__ );
+
                        return array(
                                'status' => self::VERIFICATION_ERROR,
                                'details' => $verification
@@ -317,6 +331,7 @@ abstract class UploadBase {
                $result = $this->validateName();
                if ( $result !== true ) {
                        wfProfileOut( __METHOD__ );
+
                        return $result;
                }
 
@@ -325,10 +340,12 @@ abstract class UploadBase {
                        array( $this->mDestName, $this->mTempPath, &$error ) )
                ) {
                        wfProfileOut( __METHOD__ );
+
                        return array( 'status' => self::HOOK_ABORTED, 'error' => $error );
                }
 
                wfProfileOut( __METHOD__ );
+
                return array( 'status' => self::OK );
        }
 
@@ -351,6 +368,7 @@ abstract class UploadBase {
                                        $result['blacklistedExt'] = $this->mBlackListedExtensions;
                                }
                        }
+
                        return $result;
                }
                $this->mDestName = $this->getLocalFile()->getName();
@@ -374,6 +392,7 @@ abstract class UploadBase {
                        global $wgMimeTypeBlacklist;
                        if ( $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) {
                                wfProfileOut( __METHOD__ );
+
                                return array( 'filetype-badmime', $mime );
                        }
 
@@ -388,12 +407,14 @@ abstract class UploadBase {
                        foreach ( $ieTypes as $ieType ) {
                                if ( $this->checkFileExtension( $ieType, $wgMimeTypeBlacklist ) ) {
                                        wfProfileOut( __METHOD__ );
+
                                        return array( 'filetype-bad-ie-mime', $ieType );
                                }
                        }
                }
 
                wfProfileOut( __METHOD__ );
+
                return true;
        }
 
@@ -409,6 +430,7 @@ abstract class UploadBase {
                $status = $this->verifyPartialFile();
                if ( $status !== true ) {
                        wfProfileOut( __METHOD__ );
+
                        return $status;
                }
 
@@ -419,6 +441,7 @@ abstract class UploadBase {
                        # XXX: Missing extension will be caught by validateName() via getTitle()
                        if ( $this->mFinalExtension != '' && !$this->verifyExtension( $mime, $this->mFinalExtension ) ) {
                                wfProfileOut( __METHOD__ );
+
                                return array( 'filetype-mime-mismatch', $this->mFinalExtension, $mime );
                        }
                }
@@ -429,6 +452,7 @@ abstract class UploadBase {
                        if ( !$handlerStatus->isOK() ) {
                                $errors = $handlerStatus->getErrorsArray();
                                wfProfileOut( __METHOD__ );
+
                                return reset( $errors );
                        }
                }
@@ -436,11 +460,13 @@ abstract class UploadBase {
                wfRunHooks( 'UploadVerifyFile', array( $this, $mime, &$status ) );
                if ( $status !== true ) {
                        wfProfileOut( __METHOD__ );
+
                        return $status;
                }
 
                wfDebug( __METHOD__ . ": all clear; passing.\n" );
                wfProfileOut( __METHOD__ );
+
                return true;
        }
 
@@ -466,6 +492,7 @@ abstract class UploadBase {
                $status = $this->verifyMimeType( $mime );
                if ( $status !== true ) {
                        wfProfileOut( __METHOD__ );
+
                        return $status;
                }
 
@@ -473,12 +500,14 @@ abstract class UploadBase {
                if ( !$wgDisableUploadScriptChecks ) {
                        if ( self::detectScript( $this->mTempPath, $mime, $this->mFinalExtension ) ) {
                                wfProfileOut( __METHOD__ );
+
                                return array( 'uploadscripted' );
                        }
                        if ( $this->mFinalExtension == 'svg' || $mime == 'image/svg+xml' ) {
                                $svgStatus = $this->detectScriptInSvg( $this->mTempPath );
                                if ( $svgStatus !== false ) {
                                        wfProfileOut( __METHOD__ );
+
                                        return $svgStatus;
                                }
                        }
@@ -495,11 +524,13 @@ abstract class UploadBase {
                                $error = reset( $errors );
                                if ( $error[0] !== 'zip-wrong-format' ) {
                                        wfProfileOut( __METHOD__ );
+
                                        return $error;
                                }
                        }
                        if ( $this->mJavaDetected ) {
                                wfProfileOut( __METHOD__ );
+
                                return array( 'uploadjava' );
                        }
                }
@@ -508,10 +539,12 @@ abstract class UploadBase {
                $virus = $this->detectVirus( $this->mTempPath );
                if ( $virus ) {
                        wfProfileOut( __METHOD__ );
+
                        return array( 'uploadvirus', $virus );
                }
 
                wfProfileOut( __METHOD__ );
+
                return true;
        }
 
@@ -541,8 +574,10 @@ abstract class UploadBase {
        }
 
        /**
-        * Alias for verifyTitlePermissions. The function was originally 'verifyPermissions'
-        * but that suggests it's checking the user, when it's really checking the title + user combination.
+        * Alias for verifyTitlePermissions. The function was originally
+        * 'verifyPermissions', but that suggests it's checking the user, when it's
+        * really checking the title + user combination.
+        *
         * @param User $user User object to verify the permissions against
         * @return mixed An array as returned by getUserPermissionsErrors or true
         *   in case the user has proper permissions.
@@ -581,6 +616,7 @@ abstract class UploadBase {
                if ( $permErrors || $permErrorsUpload || $permErrorsCreate ) {
                        $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsUpload, $permErrors ) );
                        $permErrors = array_merge( $permErrors, wfArrayDiff2( $permErrorsCreate, $permErrors ) );
+
                        return $permErrors;
                }
 
@@ -618,7 +654,8 @@ 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'" );
+                       wfDebugLog( 'upload', "Filename: '$filename', mDesiredDestName: "
+                               . "'$this->mDesiredDestName', comparableName: '$comparableName'" );
                }
 
                // Check whether the file extension is on the unwanted list
@@ -670,6 +707,7 @@ abstract class UploadBase {
                }
 
                wfProfileOut( __METHOD__ );
+
                return $warnings;
        }
 
@@ -699,12 +737,17 @@ abstract class UploadBase {
 
                if ( $status->isGood() ) {
                        if ( $watch ) {
-                               WatchAction::doWatch( $this->getLocalFile()->getTitle(), $user, WatchedItem::IGNORE_USER_RIGHTS );
+                               WatchAction::doWatch(
+                                       $this->getLocalFile()->getTitle(),
+                                       $user,
+                                       WatchedItem::IGNORE_USER_RIGHTS
+                               );
                        }
                        wfRunHooks( 'UploadComplete', array( &$this ) );
                }
 
                wfProfileOut( __METHOD__ );
+
                return $status;
        }
 
@@ -733,6 +776,7 @@ abstract class UploadBase {
                if ( strlen( $this->mFilteredName ) > 240 ) {
                        $this->mTitleError = self::FILENAME_TOO_LONG;
                        $this->mTitle = null;
+
                        return $this->mTitle;
                }
 
@@ -747,6 +791,7 @@ abstract class UploadBase {
                if ( is_null( $nt ) ) {
                        $this->mTitleError = self::ILLEGAL_FILENAME;
                        $this->mTitle = null;
+
                        return $this->mTitle;
                }
                $this->mFilteredName = $nt->getDBkey();
@@ -789,27 +834,34 @@ abstract class UploadBase {
                if ( $this->mFinalExtension == '' ) {
                        $this->mTitleError = self::FILETYPE_MISSING;
                        $this->mTitle = null;
+
                        return $this->mTitle;
                } elseif ( $blackListedExtensions ||
-                               ( $wgCheckFileExtensions && $wgStrictFileExtensions &&
-                                       !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) ) ) {
+                       ( $wgCheckFileExtensions && $wgStrictFileExtensions &&
+                               !$this->checkFileExtension( $this->mFinalExtension, $wgFileExtensions ) )
+               ) {
                        $this->mBlackListedExtensions = $blackListedExtensions;
                        $this->mTitleError = self::FILETYPE_BADTYPE;
                        $this->mTitle = null;
+
                        return $this->mTitle;
                }
 
-               // Windows may be broken with special characters, see bug XXX
-               if ( wfIsWindows() && !preg_match( '/^[\x0-\x7f]*$/', $nt->getText() ) ) {
+               // Windows may be broken with special characters, see bug 1780
+               if ( !preg_match( '/^[\x0-\x7f]*$/', $nt->getText() )
+                       && !RepoGroup::singleton()->getLocalRepo()->backendSupportsUnicodePaths()
+               ) {
                        $this->mTitleError = self::WINDOWS_NONASCII_FILENAME;
                        $this->mTitle = null;
+
                        return $this->mTitle;
                }
 
                # If there was more than one "extension", reassemble the base
                # filename to prevent bogus complaints about length
                if ( count( $ext ) > 1 ) {
-                       for ( $i = 0; $i < count( $ext ) - 1; $i++ ) {
+                       $iterations = count( $ext ) - 1;
+                       for ( $i = 0; $i < $iterations; $i++ ) {
                                $partname .= '.' . $ext[$i];
                        }
                }
@@ -817,10 +869,12 @@ abstract class UploadBase {
                if ( strlen( $partname ) < 1 ) {
                        $this->mTitleError = self::MIN_LENGTH_PARTNAME;
                        $this->mTitle = null;
+
                        return $this->mTitle;
                }
 
                $this->mTitle = $nt;
+
                return $this->mTitle;
        }
 
@@ -834,17 +888,21 @@ abstract class UploadBase {
                        $nt = $this->getTitle();
                        $this->mLocalFile = is_null( $nt ) ? null : wfLocalFile( $nt );
                }
+
                return $this->mLocalFile;
        }
 
        /**
-        * If the user does not supply all necessary information in the first upload form submission (either by accident or
-        * by design) then we may want to stash the file temporarily, get more information, and publish the file later.
+        * If the user does not supply all necessary information in the first upload
+        * form submission (either by accident or by design) then we may want to
+        * stash the file temporarily, get more information, and publish the file
+        * later.
         *
-        * This method will stash a file in a temporary directory for later processing, and save the necessary descriptive info
-        * into the database.
-        * This method returns the file object, which also has a 'fileKey' property which can be passed through a form or
-        * API request to find this stashed file again.
+        * This method will stash a file in a temporary directory for later
+        * processing, and save the necessary descriptive info into the database.
+        * This method returns the file object, which also has a 'fileKey' property
+        * which can be passed through a form or API request to find this stashed
+        * file again.
         *
         * @param User $user
         * @return UploadStashFile Stashed file
@@ -858,11 +916,13 @@ abstract class UploadBase {
                $this->mLocalFile = $file;
 
                wfProfileOut( __METHOD__ );
+
                return $file;
        }
 
        /**
-        * Stash a file in a temporary directory, returning a key which can be used to find the file again. See stashFile().
+        * Stash a file in a temporary directory, returning a key which can be used
+        * to find the file again. See stashFile().
         *
         * @return string File key
         */
@@ -906,6 +966,7 @@ abstract class UploadBase {
        public static function splitExtensions( $filename ) {
                $bits = explode( '.', $filename );
                $basename = array_shift( $bits );
+
                return array( $basename, $bits );
        }
 
@@ -947,10 +1008,12 @@ abstract class UploadBase {
                        if ( !$magic->isRecognizableExtension( $extension ) ) {
                                wfDebug( __METHOD__ . ": passing file with unknown detected mime type; " .
                                        "unrecognized extension '$extension', can't verify\n" );
+
                                return true;
                        } else {
                                wfDebug( __METHOD__ . ": rejecting file with unknown detected mime type; " .
                                        "recognized extension '$extension', so probably invalid file\n" );
+
                                return false;
                        }
                }
@@ -960,19 +1023,22 @@ abstract class UploadBase {
                if ( $match === null ) {
                        if ( $magic->getTypesForExtension( $extension ) !== null ) {
                                wfDebug( __METHOD__ . ": No extension known for $mime, but we know a mime for $extension\n" );
+
                                return false;
                        } else {
                                wfDebug( __METHOD__ . ": no file extension known for mime type $mime, passing file\n" );
+
                                return true;
                        }
                } elseif ( $match === true ) {
                        wfDebug( __METHOD__ . ": mime type $mime matches extension $extension, passing file\n" );
 
-                       #TODO: if it's a bitmap, make sure PHP or ImageMagic resp. can handle it!
+                       /** @todo If it's a bitmap, make sure PHP or ImageMagick resp. can handle it! */
                        return true;
-
                } else {
-                       wfDebug( __METHOD__ . ": mime type $mime mismatches file extension $extension, rejecting file\n" );
+                       wfDebug( __METHOD__
+                               . ": mime type $mime mismatches file extension $extension, rejecting file\n" );
+
                        return false;
                }
        }
@@ -1007,6 +1073,7 @@ abstract class UploadBase {
 
                if ( !$chunk ) {
                        wfProfileOut( __METHOD__ );
+
                        return false;
                }
 
@@ -1025,12 +1092,13 @@ abstract class UploadBase {
 
                $chunk = trim( $chunk );
 
-               # @todo FIXME: Convert from UTF-16 if necessary!
+               /** @todo FIXME: Convert from UTF-16 if necessary! */
                wfDebug( __METHOD__ . ": checking for embedded scripts and HTML stuff\n" );
 
                # check for HTML doctype
                if ( preg_match( "/<!DOCTYPE *X?HTML/i", $chunk ) ) {
                        wfProfileOut( __METHOD__ );
+
                        return true;
                }
 
@@ -1039,6 +1107,7 @@ abstract class UploadBase {
                if ( $extension == 'svg' || strpos( $mime, 'image/svg' ) === 0 ) {
                        if ( self::checkXMLEncodingMissmatch( $file ) ) {
                                wfProfileOut( __METHOD__ );
+
                                return true;
                        }
                }
@@ -1062,7 +1131,7 @@ abstract class UploadBase {
                        '<a href',
                        '<body',
                        '<head',
-                       '<html',   #also in safari
+                       '<html', #also in safari
                        '<img',
                        '<pre',
                        '<script', #also in safari
@@ -1077,6 +1146,7 @@ abstract class UploadBase {
                        if ( false !== strpos( $chunk, $tag ) ) {
                                wfDebug( __METHOD__ . ": found something that may make it be mistaken for html: $tag\n" );
                                wfProfileOut( __METHOD__ );
+
                                return true;
                        }
                }
@@ -1092,6 +1162,7 @@ abstract class UploadBase {
                if ( preg_match( '!type\s*=\s*[\'"]?\s*(?:\w*/)?(?:ecma|java)!sim', $chunk ) ) {
                        wfDebug( __METHOD__ . ": found script types\n" );
                        wfProfileOut( __METHOD__ );
+
                        return true;
                }
 
@@ -1099,6 +1170,7 @@ abstract class UploadBase {
                if ( preg_match( '!(?:href|src|data)\s*=\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
                        wfDebug( __METHOD__ . ": found html-style script urls\n" );
                        wfProfileOut( __METHOD__ );
+
                        return true;
                }
 
@@ -1106,11 +1178,13 @@ abstract class UploadBase {
                if ( preg_match( '!url\s*\(\s*[\'"]?\s*(?:ecma|java)script:!sim', $chunk ) ) {
                        wfDebug( __METHOD__ . ": found css-style script urls\n" );
                        wfProfileOut( __METHOD__ );
+
                        return true;
                }
 
                wfDebug( __METHOD__ . ": no scripts found\n" );
                wfProfileOut( __METHOD__ );
+
                return false;
        }
 
@@ -1131,16 +1205,19 @@ abstract class UploadBase {
                                && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
                        ) {
                                wfDebug( __METHOD__ . ": Found unsafe XML encoding '{$encMatch[1]}'\n" );
+
                                return true;
                        }
                } elseif ( preg_match( "!<\?xml\b!si", $contents ) ) {
                        // Start of XML declaration without an end in the first $wgSVGMetadataCutoff
                        // bytes. There shouldn't be a legitimate reason for this to happen.
                        wfDebug( __METHOD__ . ": Unmatched XML declaration start\n" );
+
                        return true;
                } elseif ( substr( $contents, 0, 4 ) == "\x4C\x6F\xA7\x94" ) {
                        // EBCDIC encoded XML
                        wfDebug( __METHOD__ . ": EBCDIC Encoded XML\n" );
+
                        return true;
                }
 
@@ -1151,17 +1228,19 @@ abstract class UploadBase {
                        wfSuppressWarnings();
                        $str = iconv( $encoding, 'UTF-8', $contents );
                        wfRestoreWarnings();
-                       if ( $str != '' && preg_match( "!<\?xml\b(.*?)\?>!si", $str, $matches ) ) {
+                       if ( $str != '' && preg_match( "!<\?xml\b(.*?)\?>!si", $str, $matches ) ) {
                                if ( preg_match( $encodingRegex, $matches[1], $encMatch )
                                        && !in_array( strtoupper( $encMatch[1] ), self::$safeXmlEncodings )
                                ) {
                                        wfDebug( __METHOD__ . ": Found unsafe XML encoding '{$encMatch[1]}'\n" );
+
                                        return true;
                                }
                        } elseif ( $str != '' && preg_match( "!<\?xml\b!si", $str ) ) {
                                // Start of XML declaration without an end in the first $wgSVGMetadataCutoff
                                // bytes. There shouldn't be a legitimate reason for this to happen.
                                wfDebug( __METHOD__ . ": Unmatched XML declaration start\n" );
+
                                return true;
                        }
                }
@@ -1188,8 +1267,10 @@ abstract class UploadBase {
                        if ( $this->mSVGNSError ) {
                                return array( 'uploadscriptednamespace', $this->mSVGNSError );
                        }
+
                        return array( 'uploadscripted' );
                }
+
                return false;
        }
 
@@ -1204,6 +1285,7 @@ abstract class UploadBase {
                if ( preg_match( '/xml-stylesheet/i', $target ) ) {
                        return true;
                }
+
                return false;
        }
 
@@ -1216,6 +1298,8 @@ abstract class UploadBase {
        public function checkSvgScriptCallback( $element, $attribs ) {
                list( $namespace, $strippedElement ) = $this->splitXmlNamespace( $element );
 
+               // We specifically don't include:
+               // http://www.w3.org/1999/xhtml (bug 60771)
                static $validNamespaces = array(
                        '',
                        'adobe:ns:meta/',
@@ -1246,17 +1330,21 @@ abstract class UploadBase {
                        'http://purl.org/dc/elements/1.1',
                        'http://schemas.microsoft.com/visio/2003/svgextensions/',
                        'http://sodipodi.sourceforge.net/dtd/sodipodi-0.dtd',
+                       'http://taptrix.com/inkpad/svg_extensions',
                        'http://web.resource.org/cc/',
                        'http://www.freesoftware.fsf.org/bkchem/cdml',
                        'http://www.inkscape.org/namespaces/inkscape',
+                       'http://www.opengis.net/gml',
                        '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" );
-                       // @TODO return a status object to a closure in XmlTypeCheck, for MW1.21+
+                       /** @todo Return a status object to a closure in XmlTypeCheck, for MW1.21+ */
                        $this->mSVGNSError = $namespace;
+
                        return true;
                }
 
@@ -1265,24 +1353,29 @@ abstract class UploadBase {
                 */
                if ( $strippedElement == 'script' ) {
                        wfDebug( __METHOD__ . ": Found script element '$element' in uploaded file.\n" );
+
                        return true;
                }
 
-               # e.g., <svg xmlns="http://www.w3.org/2000/svg"> <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>
+               # e.g., <svg xmlns="http://www.w3.org/2000/svg">
+               #  <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>
                if ( $strippedElement == 'handler' ) {
                        wfDebug( __METHOD__ . ": Found scriptable element '$element' in uploaded file.\n" );
+
                        return true;
                }
 
                # 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 true;
                }
 
                # Block iframes, in case they pass the namespace check
                if ( $strippedElement == 'iframe' ) {
                        wfDebug( __METHOD__ . ": iframe in uploaded file.\n" );
+
                        return true;
                }
 
@@ -1291,69 +1384,105 @@ abstract class UploadBase {
                        $value = strtolower( $value );
 
                        if ( substr( $stripped, 0, 2 ) == 'on' ) {
-                               wfDebug( __METHOD__ . ": Found event-handler attribute '$attrib'='$value' in uploaded file.\n" );
+                               wfDebug( __METHOD__
+                                       . ": Found event-handler attribute '$attrib'='$value' in uploaded file.\n" );
+
                                return true;
                        }
 
                        # href with javascript target
                        if ( $stripped == 'href' && strpos( strtolower( $value ), 'javascript:' ) !== false ) {
-                               wfDebug( __METHOD__ . ": Found script in href attribute '$attrib'='$value' in uploaded file.\n" );
+                               wfDebug( __METHOD__
+                                       . ": Found script in href attribute '$attrib'='$value' in uploaded file.\n" );
+
                                return true;
                        }
 
                        # href with embedded svg as target
                        if ( $stripped == 'href' && preg_match( '!data:[^,]*image/svg[^,]*,!sim', $value ) ) {
-                               wfDebug( __METHOD__ . ": Found href to embedded svg \"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
+                               wfDebug( __METHOD__ . ": Found href to embedded svg "
+                                       . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
+
                                return true;
                        }
 
                        # href with embedded (text/xml) svg as target
                        if ( $stripped == 'href' && preg_match( '!data:[^,]*text/xml[^,]*,!sim', $value ) ) {
-                               wfDebug( __METHOD__ . ": Found href to embedded svg \"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
+                               wfDebug( __METHOD__ . ": Found href to embedded svg "
+                                       . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
+
                                return true;
                        }
 
                        # use set/animate to add event-handler attribute to parent
-                       if ( ( $strippedElement == 'set' || $strippedElement == 'animate' ) && $stripped == 'attributename' && substr( $value, 0, 2 ) == 'on' ) {
-                               wfDebug( __METHOD__ . ": Found svg setting event-handler attribute with \"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
+                       if ( ( $strippedElement == 'set' || $strippedElement == 'animate' )
+                               && $stripped == 'attributename'
+                               && substr( $value, 0, 2 ) == 'on'
+                       ) {
+                               wfDebug( __METHOD__ . ": Found svg setting event-handler attribute with "
+                                       . "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
+
                                return true;
                        }
 
                        # use set to add href attribute to parent element
-                       if ( $strippedElement == 'set' && $stripped == 'attributename' && strpos( $value, 'href' ) !== false ) {
+                       if ( $strippedElement == 'set'
+                               && $stripped == 'attributename'
+                               && strpos( $value, 'href' ) !== false
+                       ) {
                                wfDebug( __METHOD__ . ": Found svg setting href attribute '$value' in uploaded file.\n" );
+
                                return true;
                        }
 
                        # use set to add a remote / data / script target to an element
-                       if ( $strippedElement == 'set' && $stripped == 'to' && preg_match( '!(http|https|data|script):!sim', $value ) ) {
+                       if ( $strippedElement == 'set'
+                               && $stripped == 'to'
+                               && preg_match( '!(http|https|data|script):!sim', $value )
+                       ) {
                                wfDebug( __METHOD__ . ": Found svg setting attribute to '$value' in uploaded file.\n" );
+
                                return true;
                        }
 
                        # use handler attribute with remote / data / script
                        if ( $stripped == 'handler' && preg_match( '!(http|https|data|script):!sim', $value ) ) {
-                               wfDebug( __METHOD__ . ": Found svg setting handler with remote/data/script '$attrib'='$value' in uploaded file.\n" );
+                               wfDebug( __METHOD__ . ": Found svg setting handler with remote/data/script "
+                                       . "'$attrib'='$value' in uploaded file.\n" );
+
                                return true;
                        }
 
                        # use CSS styles to bring in remote code
                        # catch url("http:..., url('http:..., url(http:..., but not url("#..., url('#..., url(#....
-                       if ( $stripped == 'style' && preg_match_all( '!((?:font|clip-path|fill|filter|marker|marker-end|marker-mid|marker-start|mask|stroke)\s*:\s*url\s*\(\s*["\']?\s*[^#]+.*?\))!sim', $value, $matches ) ) {
+                       $tagsList = "font|clip-path|fill|filter|marker|marker-end|marker-mid|marker-start|mask|stroke";
+                       if ( $stripped == 'style'
+                               && preg_match_all(
+                                       '!((?:' . $tagsList . ')\s*:\s*url\s*\(\s*["\']?\s*[^#]+.*?\))!sim',
+                                       $value,
+                                       $matches
+                               )
+                       ) {
                                foreach ( $matches[1] as $match ) {
-                                       if ( !preg_match( '!(?:font|clip-path|fill|filter|marker|marker-end|marker-mid|marker-start|mask|stroke)\s*:\s*url\s*\(\s*(#|\'#|"#)!sim', $match ) ) {
-                                               wfDebug( __METHOD__ . ": Found svg setting a style with remote url '$attrib'='$value' in uploaded file.\n" );
+                                       if ( !preg_match( '!(?:' . $tagsList . ')\s*:\s*url\s*\(\s*(#|\'#|"#)!sim', $match ) ) {
+                                               wfDebug( __METHOD__ . ": Found svg setting a style with "
+                                                       . "remote url '$attrib'='$value' in uploaded file.\n" );
+
                                                return true;
                                        }
                                }
                        }
 
                        # image filters can pull in url, which could be svg that executes scripts
-                       if ( $strippedElement == 'image' && $stripped == 'filter' && preg_match( '!url\s*\(!sim', $value ) ) {
-                               wfDebug( __METHOD__ . ": Found image filter with url: \"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
+                       if ( $strippedElement == 'image'
+                               && $stripped == 'filter'
+                               && preg_match( '!url\s*\(!sim', $value )
+                       ) {
+                               wfDebug( __METHOD__ . ": Found image filter with url: "
+                                       . "\"<$strippedElement $stripped='$value'...\" in uploaded file.\n" );
+
                                return true;
                        }
-
                }
 
                return false; //No scripts detected
@@ -1369,6 +1498,7 @@ abstract class UploadBase {
                $parts = explode( ':', strtolower( $element ) );
                $name = array_pop( $parts );
                $ns = implode( ':', $parts );
+
                return array( $ns, $name );
        }
 
@@ -1379,6 +1509,7 @@ abstract class UploadBase {
        private function stripXmlNamespace( $name ) {
                // 'http://www.w3.org/2000/svg:script' -> 'script'
                $parts = explode( ':', strtolower( $name ) );
+
                return array_pop( $parts );
        }
 
@@ -1399,6 +1530,7 @@ abstract class UploadBase {
                if ( !$wgAntivirus ) {
                        wfDebug( __METHOD__ . ": virus scanner disabled\n" );
                        wfProfileOut( __METHOD__ );
+
                        return null;
                }
 
@@ -1407,6 +1539,7 @@ abstract class UploadBase {
                        $wgOut->wrapWikiMsg( "<div class=\"error\">\n$1\n</div>",
                                array( 'virus-badscanner', $wgAntivirus ) );
                        wfProfileOut( __METHOD__ );
+
                        return wfMessage( 'virus-unknownscanner' )->text() . " $wgAntivirus";
                }
 
@@ -1451,7 +1584,9 @@ abstract class UploadBase {
                        # scan failed (code was mapped to false by $exitCodeMap)
                        wfDebug( __METHOD__ . ": failed to scan $file (code $exitCode).\n" );
 
-                       $output = $wgAntivirusRequired ? wfMessage( 'virus-scanfailed', array( $exitCode ) )->text() : null;
+                       $output = $wgAntivirusRequired
+                               ? wfMessage( 'virus-scanfailed', array( $exitCode ) )->text()
+                               : null;
                } elseif ( $mappedCode === AV_SCAN_ABORTED ) {
                        # scan failed because filetype is unknown (probably imune)
                        wfDebug( __METHOD__ . ": unsupported file type $file (code $exitCode).\n" );
@@ -1478,6 +1613,7 @@ abstract class UploadBase {
                }
 
                wfProfileOut( __METHOD__ );
+
                return $output;
        }
 
@@ -1589,7 +1725,7 @@ abstract class UploadBase {
 
                // Check for files with the same name but a different extension
                $similarFiles = RepoGroup::singleton()->getLocalRepo()->findFilesByPrefix(
-                               "{$partname}.", 1 );
+                       "{$partname}.", 1 );
                if ( count( $similarFiles ) ) {
                        return array(
                                'warning' => 'exists-normalized',
@@ -1600,7 +1736,10 @@ abstract class UploadBase {
 
                if ( self::isThumbName( $file->getName() ) ) {
                        # Check for filenames like 50px- or 180px-, these are mostly thumbnails
-                       $nt_thb = Title::newFromText( substr( $partname, strpos( $partname, '-' ) + 1 ) . '.' . $extension, NS_FILE );
+                       $nt_thb = Title::newFromText(
+                               substr( $partname, strpos( $partname, '-' ) + 1 ) . '.' . $extension,
+                               NS_FILE
+                       );
                        $file_thb = wfLocalFile( $nt_thb );
                        if ( $file_thb->exists() ) {
                                return array(
@@ -1639,11 +1778,12 @@ abstract class UploadBase {
        public static function isThumbName( $filename ) {
                $n = strrpos( $filename, '.' );
                $partname = $n ? substr( $filename, 0, $n ) : $filename;
+
                return (
-                                       substr( $partname, 3, 3 ) == 'px-' ||
-                                       substr( $partname, 2, 3 ) == 'px-'
-                               ) &&
-                               preg_match( "/[0-9]{2}/", substr( $partname, 0, 2 ) );
+                       substr( $partname, 3, 3 ) == 'px-' ||
+                       substr( $partname, 2, 3 ) == 'px-'
+               ) &&
+               preg_match( "/[0-9]{2}/", substr( $partname, 0, 2 ) );
        }
 
        /**
@@ -1670,23 +1810,28 @@ abstract class UploadBase {
                                $blacklist[] = trim( $line );
                        }
                }
+
                return $blacklist;
        }
 
        /**
         * Gets image info about the file just uploaded.
         *
-        * Also has the effect of setting metadata to be an 'indexed tag name' in returned API result if
-        * 'metadata' was requested. Oddly, we have to pass the "result" object down just so it can do that
-        * with the appropriate format, presumably.
+        * Also has the effect of setting metadata to be an 'indexed tag name' in
+        * returned API result if 'metadata' was requested. Oddly, we have to pass
+        * the "result" object down just so it can do that with the appropriate
+        * format, presumably.
         *
         * @param ApiResult $result
         * @return array Image info
         */
        public function getImageInfo( $result ) {
                $file = $this->getLocalFile();
-               // TODO This cries out for refactoring. We really want to say $file->getAllInfo(); here.
-               // Perhaps "info" methods should be moved into files, and the API should just wrap them in queries.
+               /** @todo This cries out for refactoring.
+                *  We really want to say $file->getAllInfo(); here.
+                * Perhaps "info" methods should be moved into files, and the API should
+                * just wrap them in queries.
+                */
                if ( $file instanceof UploadStashFile ) {
                        $imParam = ApiQueryStashImageInfo::getPropertyNames();
                        $info = ApiQueryStashImageInfo::getInfo( $file, array_flip( $imParam ), $result );
@@ -1694,6 +1839,7 @@ abstract class UploadBase {
                        $imParam = ApiQueryImageInfo::getPropertyNames();
                        $info = ApiQueryImageInfo::getInfo( $file, array_flip( $imParam ), $result );
                }
+
                return $info;
        }
 
@@ -1704,6 +1850,7 @@ abstract class UploadBase {
        public function convertVerifyErrorToStatus( $error ) {
                $code = $error['status'];
                unset( $code['status'] );
+
                return Status::newFatal( $this->getVerificationErrorCode( $code ), $error );
        }