Merge "Exclude redirects from Special:Fewestrevisions"
[lhc/web/wiklou.git] / includes / upload / UploadBase.php
index d905aa4..5b15e82 100644 (file)
@@ -20,7 +20,6 @@
  * @file
  * @ingroup Upload
  */
-use MediaWiki\MediaWikiServices;
 use MediaWiki\Shell\Shell;
 
 /**
@@ -42,13 +41,36 @@ abstract class UploadBase {
        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;
-       protected $mLocalFile, $mStashFile, $mFileSize, $mFileProps;
+       /** @var string|null */
+       protected $mDesiredDestName;
+       /** @var string|null */
+       protected $mDestName;
+       /** @var string|null */
+       protected $mRemoveTempFile;
+       /** @var string|null */
+       protected $mSourceType;
+       /** @var Title|bool */
+       protected $mTitle = false;
+       /** @var int */
+       protected $mTitleError = 0;
+       /** @var string|null */
+       protected $mFilteredName;
+       /** @var string|null */
+       protected $mFinalExtension;
+       /** @var LocalFile */
+       protected $mLocalFile;
+       /** @var UploadStashFile */
+       protected $mStashFile;
+       /** @var int|null */
+       protected $mFileSize;
+       /** @var array|null */
+       protected $mFileProps;
+       /** @var string[] */
        protected $mBlackListedExtensions;
-       protected $mJavaDetected, $mSVGNSError;
+       /** @var bool|null */
+       protected $mJavaDetected;
+       /** @var string|null */
+       protected $mSVGNSError;
 
        protected static $safeXmlEncodings = [
                'UTF-8',
@@ -357,13 +379,6 @@ abstract class UploadBase {
                        return $result;
                }
 
-               $error = '';
-               if ( !Hooks::run( 'UploadVerification',
-                       [ $this->mDestName, $this->mTempPath, &$error ], '1.28' )
-               ) {
-                       return [ 'status' => self::HOOK_ABORTED, 'error' => $error ];
-               }
-
                return [ 'status' => self::OK ];
        }
 
@@ -404,7 +419,7 @@ abstract class UploadBase {
         * @return mixed True if the file is verified, an array otherwise
         */
        protected function verifyMimeType( $mime ) {
-               global $wgVerifyMimeType;
+               global $wgVerifyMimeType, $wgVerifyMimeTypeIE;
                if ( $wgVerifyMimeType ) {
                        wfDebug( "mime: <$mime> extension: <{$this->mFinalExtension}>\n" );
                        global $wgMimeTypeBlacklist;
@@ -412,17 +427,19 @@ abstract class UploadBase {
                                return [ 'filetype-badmime', $mime ];
                        }
 
-                       # Check what Internet Explorer would detect
-                       $fp = fopen( $this->mTempPath, 'rb' );
-                       $chunk = fread( $fp, 256 );
-                       fclose( $fp );
-
-                       $magic = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
-                       $extMime = $magic->guessTypesForExtension( $this->mFinalExtension );
-                       $ieTypes = $magic->getIEMimeTypes( $this->mTempPath, $chunk, $extMime );
-                       foreach ( $ieTypes as $ieType ) {
-                               if ( $this->checkFileExtension( $ieType, $wgMimeTypeBlacklist ) ) {
-                                       return [ 'filetype-bad-ie-mime', $ieType ];
+                       if ( $wgVerifyMimeTypeIE ) {
+                               # Check what Internet Explorer would detect
+                               $fp = fopen( $this->mTempPath, 'rb' );
+                               $chunk = fread( $fp, 256 );
+                               fclose( $fp );
+
+                               $magic = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
+                               $extMime = $magic->guessTypesForExtension( $this->mFinalExtension );
+                               $ieTypes = $magic->getIEMimeTypes( $this->mTempPath, $chunk, $extMime );
+                               foreach ( $ieTypes as $ieType ) {
+                                       if ( $this->checkFileExtension( $ieType, $wgMimeTypeBlacklist ) ) {
+                                               return [ 'filetype-bad-ie-mime', $ieType ];
+                                       }
                                }
                        }
                }
@@ -1105,6 +1122,8 @@ abstract class UploadBase {
         * @throws UploadStashNotLoggedInException
         */
        public function stashFile( User $user = null ) {
+               wfDeprecated( __METHOD__, '1.28' );
+
                return $this->doStashFile( $user );
        }
 
@@ -1122,29 +1141,6 @@ abstract class UploadBase {
                return $file;
        }
 
-       /**
-        * Stash a file in a temporary directory, returning a key which can be used
-        * to find the file again. See stashFile().
-        *
-        * @deprecated since 1.28
-        * @return string File key
-        */
-       public function stashFileGetKey() {
-               wfDeprecated( __METHOD__, '1.28' );
-               return $this->doStashFile()->getFileKey();
-       }
-
-       /**
-        * alias for stashFileGetKey, for backwards compatibility
-        *
-        * @deprecated since 1.28
-        * @return string File key
-        */
-       public function stashSession() {
-               wfDeprecated( __METHOD__, '1.28' );
-               return $this->doStashFile()->getFileKey();
-       }
-
        /**
         * If we've modified the upload file we need to manually remove it
         * on exit to clean up.
@@ -1262,12 +1258,11 @@ abstract class UploadBase {
         * @return bool True if the file contains something looking like embedded scripts
         */
        public static function detectScript( $file, $mime, $extension ) {
-               global $wgAllowTitlesInSVG;
-
                # ugly hack: for text files, always look at the entire file.
                # For binary field, just check the first K.
 
-               if ( strpos( $mime, 'text/' ) === 0 ) {
+               $isText = strpos( $mime, 'text/' ) === 0;
+               if ( $isText ) {
                        $chunk = file_get_contents( $file );
                } else {
                        $fp = fopen( $file, 'rb' );
@@ -1312,36 +1307,19 @@ abstract class UploadBase {
                        }
                }
 
-               /**
-                * Internet Explorer for Windows performs some really stupid file type
-                * autodetection which can cause it to interpret valid image files as HTML
-                * and potentially execute JavaScript, creating a cross-site scripting
-                * attack vectors.
-                *
-                * Apple's Safari browser also performs some unsafe file type autodetection
-                * which can cause legitimate files to be interpreted as HTML if the
-                * web server is not correctly configured to send the right content-type
-                * (or if you're really uploading plain text and octet streams!)
-                *
-                * Returns true if IE is likely to mistake the given file for HTML.
-                * Also returns true if Safari would mistake the given file for HTML
-                * when served with a generic content-type.
-                */
+               // Quick check for HTML heuristics in old IE and Safari.
+               //
+               // The exact heuristics IE uses are checked separately via verifyMimeType(), so we
+               // don't need them all here as it can cause many false positives.
+               //
+               // Check for `<script` and such still to forbid script tags and embedded HTML in SVG:
                $tags = [
-                       '<a href',
                        '<body',
                        '<head',
                        '<html', # also in safari
-                       '<img',
-                       '<pre',
                        '<script', # also in safari
-                       '<table'
                ];
 
-               if ( !$wgAllowTitlesInSVG && $extension !== 'svg' && $mime !== 'image/svg' ) {
-                       $tags[] = '<title';
-               }
-
                foreach ( $tags as $tag ) {
                        if ( strpos( $chunk, $tag ) !== false ) {
                                wfDebug( __METHOD__ . ": found something that may make it be mistaken for html: $tag\n" );
@@ -1524,7 +1502,7 @@ abstract class UploadBase {
         * @todo Replace this with a whitelist filter!
         * @param string $element
         * @param array $attribs
-        * @param array|null $data
+        * @param string|null $data
         * @return bool|array
         */
        public function checkSvgScriptCallback( $element, $attribs, $data = null ) {
@@ -2085,10 +2063,10 @@ abstract class UploadBase {
                $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 ) );
        }
 
        /**
@@ -2207,10 +2185,10 @@ abstract class UploadBase {
         * @return Status[]|bool
         */
        public static function getSessionStatus( User $user, $statusKey ) {
-               $cache = MediaWikiServices::getInstance()->getMainObjectStash();
-               $key = $cache->makeKey( 'uploadstatus', $user->getId() ?: md5( $user->getName() ), $statusKey );
+               $store = self::getUploadSessionStore();
+               $key = self::getUploadSessionKey( $store, $user, $statusKey );
 
-               return $cache->get( $key );
+               return $store->get( $key );
        }
 
        /**
@@ -2218,19 +2196,42 @@ abstract class UploadBase {
         *
         * The value will be set in cache for 1 day
         *
+        * Avoid triggering this method on HTTP GET/HEAD requests
+        *
         * @param User $user
         * @param string $statusKey
         * @param array|bool $value
         * @return void
         */
        public static function setSessionStatus( User $user, $statusKey, $value ) {
-               $cache = MediaWikiServices::getInstance()->getMainObjectStash();
-               $key = $cache->makeKey( 'uploadstatus', $user->getId() ?: md5( $user->getName() ), $statusKey );
+               $store = self::getUploadSessionStore();
+               $key = self::getUploadSessionKey( $store, $user, $statusKey );
 
                if ( $value === false ) {
-                       $cache->delete( $key );
+                       $store->delete( $key );
                } else {
-                       $cache->set( $key, $value, $cache::TTL_DAY );
+                       $store->set( $key, $value, $store::TTL_DAY );
                }
        }
+
+       /**
+        * @param BagOStuff $store
+        * @param User $user
+        * @param string $statusKey
+        * @return string
+        */
+       private static function getUploadSessionKey( BagOStuff $store, User $user, $statusKey ) {
+               return $store->makeKey(
+                       'uploadstatus',
+                       $user->getId() ?: md5( $user->getName() ),
+                       $statusKey
+               );
+       }
+
+       /**
+        * @return BagOStuff
+        */
+       private static function getUploadSessionStore() {
+               return ObjectCache::getInstance( 'db-replicated' );
+       }
 }