From: jenkins-bot Date: Tue, 3 Nov 2015 11:10:41 +0000 (+0000) Subject: Merge "Improve MIME detection in FileBackend" X-Git-Tag: 1.31.0-rc.0~9125 X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=b85aeec5e2d637fdeab9366df9a96abfba433778;hp=2d95b805c637be3c425ee02f43cba488f5086478 Merge "Improve MIME detection in FileBackend" --- diff --git a/includes/filebackend/FileBackendGroup.php b/includes/filebackend/FileBackendGroup.php index b6ddbad256..c043106acf 100644 --- a/includes/filebackend/FileBackendGroup.php +++ b/includes/filebackend/FileBackendGroup.php @@ -166,6 +166,7 @@ class FileBackendGroup { ? FileJournal::factory( $config['fileJournal'], $name ) : FileJournal::factory( array( 'class' => 'NullFileJournal' ), $name ); $config['wanCache'] = ObjectCache::getMainWANInstance(); + $config['mimeCallback'] = array( $this, 'guessMimeInternal' ); $this->backends[$name]['instance'] = new $class( $config ); } @@ -203,4 +204,27 @@ class FileBackendGroup { return null; } + + /** + * @param string $storagePath + * @param string|null $content + * @param string|null $fsPath + * @return string + * @since 1.27 + */ + public function guessMimeInternal( $storagePath, $content, $fsPath ) { + $magic = MimeMagic::singleton(); + // Trust the extension of the storage path (caller must validate) + $ext = FileBackend::extensionFromPath( $storagePath ); + $type = $magic->guessTypesForExtension( $ext ); + // For files without a valid extension (or one at all), inspect the contents + if ( !$type && $fsPath ) { + $type = $magic->guessMimeType( $fsPath, false ); + } elseif ( !$type && strlen( $content ) ) { + $tmpFile = TempFSFile::factory( 'mime_' ); + file_put_contents( $tmpFile->getPath(), $content ); + $type = $magic->guessMimeType( $tmpFile->getPath(), false ); + } + return $type ?: 'unknown/unknown'; + } } diff --git a/includes/filebackend/FileBackendStore.php b/includes/filebackend/FileBackendStore.php index 4ec81ecde1..1432bb95e9 100644 --- a/includes/filebackend/FileBackendStore.php +++ b/includes/filebackend/FileBackendStore.php @@ -58,7 +58,7 @@ abstract class FileBackendStore extends FileBackend { /** * @see FileBackend::__construct() * Additional $config params include: - * - wanCache : WANOBjectCache object to use for persistent caching. + * - wanCache : WANObjectCache object to use for persistent caching. * - mimeCallback : Callback that takes (storage path, content, file system path) and * returns the MIME type of the file or 'unknown/unknown'. The file * system path parameter should be used if the content one is null. @@ -69,10 +69,7 @@ abstract class FileBackendStore extends FileBackend { parent::__construct( $config ); $this->mimeCallback = isset( $config['mimeCallback'] ) ? $config['mimeCallback'] - : function ( $storagePath, $content, $fsPath ) { - // @todo handle the case of extension-less files using the contents - return StreamFile::contentTypeFromPath( $storagePath ) ?: 'unknown/unknown'; - }; + : null; $this->memCache = WANObjectCache::newEmpty(); // disabled by default $this->cheapCache = new ProcessCacheLRU( self::CACHE_CHEAP_SIZE ); $this->expensiveCache = new ProcessCacheLRU( self::CACHE_EXPENSIVE_SIZE ); @@ -1828,7 +1825,18 @@ abstract class FileBackendStore extends FileBackend { * @return string MIME type */ protected function getContentType( $storagePath, $content, $fsPath ) { - return call_user_func_array( $this->mimeCallback, func_get_args() ); + if ( $this->mimeCallback ) { + return call_user_func_array( $this->mimeCallback, func_get_args() ); + } + + $mime = null; + if ( $fsPath !== null && function_exists( 'finfo_file' ) ) { + $finfo = finfo_open( FILEINFO_MIME_TYPE ); + $mime = finfo_file( $finfo, $fsPath ); + finfo_close( $finfo ); + } + + return is_string( $mime ) ? $mime : 'unknown/unknown'; } } diff --git a/tests/phpunit/includes/filebackend/FileBackendTest.php b/tests/phpunit/includes/filebackend/FileBackendTest.php index e7d092fad0..95c6092b45 100644 --- a/tests/phpunit/includes/filebackend/FileBackendTest.php +++ b/tests/phpunit/includes/filebackend/FileBackendTest.php @@ -2397,6 +2397,42 @@ class FileBackendTest extends MediaWikiTestCase { "Scoped unlocking of files succeeded with OK status ($backendName)." ); } + /** + * @dataProvider provider_testGetContentType + */ + public function testGetContentType( $mimeCallback, $mimeFromString ) { + global $IP; + + $be = TestingAccessWrapper::newFromObject( new MemoryFileBackend( + array( + 'name' => 'testing', + 'class' => 'MemoryFileBackend', + 'wikiId' => 'meow', + 'mimeCallback' => $mimeCallback + ) + ) ); + + $dst = 'mwstore://testing/container/path/to/file_no_ext'; + $src = "$IP/tests/phpunit/data/media/srgb.jpg"; + $this->assertEquals( 'image/jpeg', $be->getContentType( $dst, null, $src ) ); + $this->assertEquals( + $mimeFromString ? 'image/jpeg' : 'unknown/unknown', + $be->getContentType( $dst, file_get_contents( $src ), null ) ); + + $src = "$IP/tests/phpunit/data/media/Png-native-test.png"; + $this->assertEquals( 'image/png', $be->getContentType( $dst, null, $src ) ); + $this->assertEquals( + $mimeFromString ? 'image/png' : 'unknown/unknown', + $be->getContentType( $dst, file_get_contents( $src ), null ) ); + } + + public static function provider_testGetContentType() { + return array( + array( null, false ), + array( array( FileBackendGroup::singleton(), 'guessMimeInternal' ), true ) + ); + } + public function testReadAffinity() { $be = TestingAccessWrapper::newFromObject( new FileBackendMultiWrite( array(