diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index d83272f564000..5fd9278d42412 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -110,6 +110,13 @@ static char * php_zip_make_relative_path(char *path, size_t path_len) /* {{{ */ return path + 1; } +#ifdef PHP_WIN32 + /* Windows relative path ^A-Z: */ + if (path_len > 2 && path[1] == ':' && !IS_SLASH(path[2])) { + return path + 2; + } +#endif /* PHP_WIN32 */ + i = path_len; while (1) { @@ -121,8 +128,17 @@ static char * php_zip_make_relative_path(char *path, size_t path_len) /* {{{ */ return path; } - if (i >= 2 && (path[i -1] == '.' || path[i -1] == ':')) { - /* i is the position of . or :, add 1 for / */ + /* relative path indicators: ^../ ^./ /../ /./ and A-Z:/ for windows only */ + if ( + (i >= 3 && path[i-1] == '.' && path[i-2] == '.' && IS_SLASH(path[i-3])) /* /../ */ + || (i >= 2 && path[i-1] == '.' && IS_SLASH(path[i-2])) /* /./ */ + || (i == 2 && path[i-1] == '.' && path[i-2] == '.' ) /* ^../ */ + || (i == 1 && path[i-1] == '.') /* ^./ */ +#ifdef PHP_WIN32 + || (i == 2 && path[i-1] == ':') /* ^A-Z:/ */ + || (i >= 2 && path[i-1] == '.' && path[i-2] == '.' ) /* ../ */ +#endif /* PHP_WIN32 */ + ) { path_begin = path + i + 1; break; } diff --git a/ext/zip/tests/bug77978_unix.phpt b/ext/zip/tests/bug77978_unix.phpt new file mode 100644 index 0000000000000..ad8d0a4c83e83 --- /dev/null +++ b/ext/zip/tests/bug77978_unix.phpt @@ -0,0 +1,151 @@ +--TEST-- +Bug #77978 Wrong relative path for :/ unix tests +--SKIPIF-- + +--FILE-- +open($file, ZIPARCHIVE::CREATE); +foreach($pathList as $path) { + $zipWriter->addFromString($path, "contents"); +} +$zipWriter->close(); + +$zipReader = new ZipArchive(); + +$i = 0; +while($zipReader->open($file) !== true && $i < 30) { + ++$i; + if($i == 30) { + die("Can't open zip file {$file} for read."); + } + sleep(1); +} + +foreach($pathList as $path) { + $zipReader->extractTo($target, $path); + $result = file_exists($target . DIRECTORY_SEPARATOR . $path) ? 'found' : 'not found'; + printf("%s \t%s%s", $path, $result, PHP_EOL); +} +$zipReader->close(); + +unlink($file); + +$pathListAdditional = [ + "abc/filename6.txt", + "filename16.txt", + "dir/filename17.txt", +]; + +foreach($pathListAdditional as $path) { + $file = $target . DIRECTORY_SEPARATOR . $path; + $result = file_exists($file) ? 'found' : 'not found'; + printf("%s \t%s%s", $path, $result, PHP_EOL); +} + +?> +--CLEAN-- + +--EXPECT-- +dir/test:/filename1.txt found +dir/test:a/filename2.txt found +dir./test/filename3.txt found +dir../test/filename4.txt found +dir/test/filename5.txt found +../abc/filename6.txt not found +./abc/filename7.txt found +/abc/filename8.txt found +abc/filename9.txt found +:abc/filename10.txt found +ab:c/filename11.txt found +abc:/filename12.txt found +abc/.filename13.txt found +abc/..filename14.txt found +abc/../filename15.txt found +abc/../../filename16.txt not found +abc/../../dir/filename17.txt not found +abc/./filename18.txt found +abc/file:name19.txt found +abc/file.name20.txt found +abc//filename21.txt found +C:abc/filename22.txt found +C:\abc/filename23.txt found +C:/abc/filename24.txt found +abc/filename6.txt found +filename16.txt found +dir/filename17.txt found \ No newline at end of file diff --git a/ext/zip/tests/bug77978_windows.phpt b/ext/zip/tests/bug77978_windows.phpt new file mode 100644 index 0000000000000..b97151d7bcc71 --- /dev/null +++ b/ext/zip/tests/bug77978_windows.phpt @@ -0,0 +1,158 @@ +--TEST-- +Bug #77978 Wrong relative path for :/ windows tests +--SKIPIF-- + +--FILE-- +open($file, ZIPARCHIVE::CREATE); +foreach($pathList as $path) { + $zipWriter->addFromString($path, "contents"); +} +$zipWriter->close(); + +$zipReader = new ZipArchive(); + +$i = 0; +while($zipReader->open($file) !== true && $i < 30) { + ++$i; + if($i == 30) { + die("Can't open zip file {$file} for read."); + } + sleep(1); +} + +foreach($pathList as $path) { + $zipReader->extractTo($target, $path); + $result = file_exists($target . DIRECTORY_SEPARATOR . $path) ? 'found' : 'not found'; + printf("%s \t%s%s", $path, $result, PHP_EOL); +} +$zipReader->close(); + +unlink($file); + +$pathListAdditional = [ + "test/filename4.txt", + "abc/filename6.txt", + "filename16.txt", + "dir/filename17.txt", + "abc/filename22.txt", + "abc/filename23.txt", +]; + +foreach($pathListAdditional as $path) { + $file = $target . DIRECTORY_SEPARATOR . $path; + $result = file_exists($file) ? 'found' : 'not found'; + printf("%s \t%s%s", $path, $result, PHP_EOL); +} + +?> +--CLEAN-- +