diff --git a/lib/filestorage/tests/zip_packer_test.php b/lib/filestorage/tests/zip_packer_test.php index b9c8bba9378..ebdbdd1c46a 100644 --- a/lib/filestorage/tests/zip_packer_test.php +++ b/lib/filestorage/tests/zip_packer_test.php @@ -260,6 +260,8 @@ class core_files_zip_packer_testcase extends advanced_testcase implements file_p * @link https://bugs.php.net/bug.php?id=77214 */ public function test_zip_entry_path_having_folder_ending_with_dot() { + global $CFG; + $this->resetAfterTest(false); $packer = get_file_packer('application/zip'); @@ -276,6 +278,28 @@ class core_files_zip_packer_testcase extends advanced_testcase implements file_p 'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt' => [''], ]; + if ($CFG->ostype === 'WINDOWS') { + // File names cannot end with dots on Windows and trailing dots are replaced with underscore. + $filenamemap = [ + 'HOW.TO' => 'HOW.TO', + 'README.' => 'README_', + './Current time' => 'Current time', + 'Data/sub1./sub2/1221' => 'Data/sub1_/sub2/1221', + 'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt' => + 'Data/sub1_/sub2_/Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt', + ]; + + } else { + $filenamemap = [ + 'HOW.TO' => 'HOW.TO', + 'README.' => 'README.', + './Current time' => 'Current time', + 'Data/sub1./sub2/1221' => 'Data/sub1./sub2/1221', + 'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt' => + 'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt', + ]; + } + // Check that the archive can be created. $result = $packer->archive_to_pathname($zipcontents, $zippath, false); $this->assertTrue($result); @@ -298,8 +322,8 @@ class core_files_zip_packer_testcase extends advanced_testcase implements file_p foreach ($zipcontents as $filename => $filecontents) { $filecontents = reset($filecontents); - $this->assertTrue(is_readable($targetpath . '/' . $filename)); - $this->assertEquals($filecontents, file_get_contents($targetpath . '/' . $filename)); + $this->assertTrue(is_readable($targetpath . '/' . $filenamemap[$filename])); + $this->assertEquals($filecontents, file_get_contents($targetpath . '/' . $filenamemap[$filename])); } } diff --git a/lib/filestorage/zip_packer.php b/lib/filestorage/zip_packer.php index 0f22bc7c2ee..1f6efb6809b 100644 --- a/lib/filestorage/zip_packer.php +++ b/lib/filestorage/zip_packer.php @@ -297,11 +297,17 @@ class zip_packer extends file_packer { $size = $info->size; $name = $info->pathname; + $origname = $name; + + // File names cannot end with dots on Windows and trailing dots are replaced with underscore. + if ($CFG->ostype === 'WINDOWS') { + $name = preg_replace('~([^/]+)\.(/|$)~', '\1_\2', $name); + } if ($name === '' or array_key_exists($name, $processed)) { // Probably filename collisions caused by filename cleaning/conversion. continue; - } else if (is_array($onlyfiles) && !in_array($name, $onlyfiles)) { + } else if (is_array($onlyfiles) && !in_array($origname, $onlyfiles)) { // Skipping files which are not in the list. continue; } @@ -342,7 +348,7 @@ class zip_packer extends file_packer { $newfile = "$newdir/$filename"; - if (strpos($newfile, './') > 1) { + if (strpos($newfile, './') > 1 || $name !== $origname) { // The path to the entry contains a directory ending with dot. We cannot use extract_to() due to // upstream PHP bugs #69477, #74619 and #77214. Extract the file from its stream which is slower but // should work even in this case.