From 7f507d94d9aa4cbbe553df6b1bd0b4c1513671eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mudr=C3=A1k?= Date: Mon, 19 Apr 2021 21:17:56 +0200 Subject: [PATCH] MDL-70864 files: Fix zip_packer extracting files with trailing dots File and folder names cannot end with dots on Windows. So replace the trailing dots with underscore consistently with how some other zip tools (such as 7-zip) handle this case. --- lib/filestorage/tests/zip_packer_test.php | 28 +++++++++++++++++++++-- lib/filestorage/zip_packer.php | 10 ++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) 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.