diff --git a/lib/filestorage/file_packer.php b/lib/filestorage/file_packer.php index ad8e8c4aa25..76aeead83ca 100644 --- a/lib/filestorage/file_packer.php +++ b/lib/filestorage/file_packer.php @@ -63,9 +63,10 @@ abstract class file_packer { * * @param stored_file|string $archivefile full pathname of zip file or stored_file instance * @param string $pathname target directory + * @param array $onlyfiles only extract files present in the array * @return array|bool list of processed files; false if error */ - public abstract function extract_to_pathname($archivefile, $pathname); + public abstract function extract_to_pathname($archivefile, $pathname, array $onlyfiles = NULL); /** * Extract file to given file path (real OS filesystem), existing files are overwrited diff --git a/lib/filestorage/tests/zip_packer_test.php b/lib/filestorage/tests/zip_packer_test.php index ed0ff8e7eee..1d3e278587a 100644 --- a/lib/filestorage/tests/zip_packer_test.php +++ b/lib/filestorage/tests/zip_packer_test.php @@ -226,6 +226,46 @@ class zip_packer_testcase extends advanced_testcase { } } + /** + * @depends test_archive_to_storage + */ + public function test_extract_to_pathname_onlyfiles() { + global $CFG; + + $this->resetAfterTest(false); + + $packer = get_file_packer('application/zip'); + $fs = get_file_storage(); + $context = context_system::instance(); + + $target = "$CFG->tempdir/onlyfiles/"; + $testcontent = file_get_contents($this->testfile); + + @mkdir($target, $CFG->directorypermissions); + $this->assertTrue(is_dir($target)); + + $onlyfiles = array('test', 'test.test', 'Žluťoučký/Koníček.txt', 'Idontexist'); + $willbeextracted = array_intersect(array_keys($this->files), $onlyfiles); + $donotextract = array_diff(array_keys($this->files), $onlyfiles); + + $archive = "$CFG->tempdir/archive.zip"; + $this->assertTrue(file_exists($archive)); + $result = $packer->extract_to_pathname($archive, $target, $onlyfiles); + $this->assertTrue(is_array($result)); + $this->assertEquals(count($willbeextracted), count($result)); + + foreach($willbeextracted as $file) { + $this->assertTrue($result[$file]); + $this->assertTrue(file_exists($target.$file)); + $this->assertSame($testcontent, file_get_contents($target.$file)); + } + foreach($donotextract as $file) { + $this->assertFalse(isset($result[$file])); + $this->assertFalse(file_exists($target.$file)); + } + + } + /** * @depends test_archive_to_storage */ diff --git a/lib/filestorage/zip_packer.php b/lib/filestorage/zip_packer.php index 90afe711a7d..dcfba55cb55 100644 --- a/lib/filestorage/zip_packer.php +++ b/lib/filestorage/zip_packer.php @@ -218,9 +218,11 @@ class zip_packer extends file_packer { * @todo MDL-31048 localise messages * @param string|stored_file $archivefile full pathname of zip file or stored_file instance * @param string $pathname target directory + * @param array $onlyfiles only extract files present in the array. The path to files MUST NOT + * start with a /. Example: array('myfile.txt', 'directory/anotherfile.txt') * @return bool|array list of processed files; false if error */ - public function extract_to_pathname($archivefile, $pathname) { + public function extract_to_pathname($archivefile, $pathname, array $onlyfiles = null) { global $CFG; if (!is_string($archivefile)) { @@ -233,7 +235,7 @@ class zip_packer extends file_packer { if (!is_readable($archivefile)) { return false; } - $ziparch = new zip_archive(); + $ziparch = new zip_archive(); if (!$ziparch->open($archivefile, file_archive::OPEN)) { return false; } @@ -243,7 +245,10 @@ class zip_packer extends file_packer { $name = $info->pathname; if ($name === '' or array_key_exists($name, $processed)) { - //probably filename collisions caused by filename cleaning/conversion + // Probably filename collisions caused by filename cleaning/conversion. + continue; + } else if (is_array($onlyfiles) && !in_array($name, $onlyfiles)) { + // Skipping files which are not in the list. continue; }