From a545a31a8794154ddc868169777660dbb9d841fa Mon Sep 17 00:00:00 2001 From: Hugo Ankarloo Date: Wed, 12 Aug 2020 20:48:55 +0200 Subject: [PATCH 1/5] Excl. leading dir: tmpdir function Add function createTmpDir in Unzipper. Function creates a temporary directory. This is a part of a bigger update to add the feature of excluding a leading directory in an archive when extracting. --- unzipper.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/unzipper.php b/unzipper.php index b7b1bbc..fa8f141 100644 --- a/unzipper.php +++ b/unzipper.php @@ -223,6 +223,22 @@ public static function extractRarArchive($archive, $destination) { } } + /** + * Create a temporary writable directory with a random name. + */ + private static function createTmpDir() { + // Decide where to create temp directory + $dir = is_writeable('./') ? '.' : sys_get_temp_dir(); + + // Create a random temp directory name + do { + $tmp_dir = $dir.'/'.mt_rand(); + } + while (!@mkdir($tmp_dir)); + + return $tmp_dir; + } + } /** From e7db034bc6037913575c0f2d8351894fa5ca1b25 Mon Sep 17 00:00:00 2001 From: Hugo Ankarloo Date: Wed, 12 Aug 2020 21:14:57 +0200 Subject: [PATCH 2/5] Excl. leading dir: directory content move func Add function moveFilesExcludingLeadingDir in Unzipper. Function moves all files and folders from one directory to another, but if the source folder only contains a singe directory, only the content of tha single directory will be moved, not the directory itself. This is a part of a bigger update to add the feature of excluding a leading directory in an archive when extracting. --- unzipper.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/unzipper.php b/unzipper.php index fa8f141..30d0022 100644 --- a/unzipper.php +++ b/unzipper.php @@ -239,6 +239,36 @@ private static function createTmpDir() { return $tmp_dir; } + /** + * Move all files and folders from one directory to another, but if source directory only contains a lonely folder it will be excluded. + * + * @param string $source + * All files and folders will be moved from this directory. + * @param string $destination + * All files and folders will be moved to this directory. + */ + private static function moveFilesExcludingLeadingDir($source, $destination) { + $dir_content = array_values(array_diff(scandir($source), ['.', '..'])); + + // Check if source directory only contains a single folder + if (count($dir_content) == 1 && is_dir($source.'/'.$dir_content[0])) { + $from_dir = $source.'/'.$dir_content[0]; + $dir_content = array_values(array_diff(scandir($from_dir), ['.', '..'])); + } + else { + $from_dir = $source; + } + + // Move all files and folders + foreach($dir_content as $item) { + rename($from_dir.'/'.$item, $destination.'/'.$item); + } + + if ($from_dir != $source) { + rmdir($from_dir); + } + } + } /** From df9b01d47db5ca475b26b4c6cb000b39fed950d3 Mon Sep 17 00:00:00 2001 From: Hugo Ankarloo Date: Wed, 12 Aug 2020 21:17:53 +0200 Subject: [PATCH 3/5] Fix description of extractZipArchive() in Unzipper The function extractZipArchive() in Unzipper is the only function that doesn't have a proper description of its parameters. This commit aims to correct this. --- unzipper.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/unzipper.php b/unzipper.php index 30d0022..16614cd 100644 --- a/unzipper.php +++ b/unzipper.php @@ -115,8 +115,10 @@ public static function extract($archive, $destination) { /** * Decompress/extract a zip archive using ZipArchive. * - * @param $archive - * @param $destination + * @param string $archive + * The archive name including file extension. E.g. my_archive.zip. + * @param string $destination + * The relative destination path where to extract files. */ public static function extractZipArchive($archive, $destination) { // Check if webserver supports unzipping. From 8ad42cd31acf8a5fa14f2b6d2ac84785ad657454 Mon Sep 17 00:00:00 2001 From: Hugo Ankarloo Date: Wed, 12 Aug 2020 21:21:26 +0200 Subject: [PATCH 4/5] Excl. leading dir: implement feature in Unzipper Fully implements the feature in Unzipper of excluding a leading directory in an archive when extracting. The implementation is made to be easily incorporated with any archive format. --- unzipper.php | 65 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/unzipper.php b/unzipper.php index 16614cd..3f94628 100644 --- a/unzipper.php +++ b/unzipper.php @@ -69,8 +69,10 @@ public function __construct() { * The archive name including file extension. E.g. my_archive.zip. * @param string $destination * The relative destination path where to extract files. + * @param boolean $exclude_leading_dir + * If true, check for a leading directory in the archive and exclude it in the extraction. */ - public function prepareExtraction($archive, $destination = '') { + public function prepareExtraction($archive, $destination = '', $exclude_leading_dir = false) { // Determine paths. if (empty($destination)) { $extpath = $this->localdir; @@ -84,7 +86,7 @@ public function prepareExtraction($archive, $destination = '') { } // Only local existing archives are allowed to be extracted. if (in_array($archive, $this->zipfiles)) { - self::extract($archive, $extpath); + self::extract($archive, $extpath, $exclude_leading_dir); } } @@ -95,18 +97,20 @@ public function prepareExtraction($archive, $destination = '') { * The archive name including file extension. E.g. my_archive.zip. * @param string $destination * The relative destination path where to extract files. + * @param boolean $exclude_leading_dir + * If true, check for a leading directory in the archive and exclude it in the extraction. */ - public static function extract($archive, $destination) { + public static function extract($archive, $destination, $exclude_leading_dir) { $ext = pathinfo($archive, PATHINFO_EXTENSION); switch ($ext) { case 'zip': - self::extractZipArchive($archive, $destination); + self::extractZipArchive($archive, $destination, $exclude_leading_dir); break; case 'gz': - self::extractGzipFile($archive, $destination); + self::extractGzipFile($archive, $destination, $exclude_leading_dir); break; case 'rar': - self::extractRarArchive($archive, $destination); + self::extractRarArchive($archive, $destination, $exclude_leading_dir); break; } @@ -119,8 +123,10 @@ public static function extract($archive, $destination) { * The archive name including file extension. E.g. my_archive.zip. * @param string $destination * The relative destination path where to extract files. + * @param boolean $exclude_leading_dir + * If true, check for a leading directory in the archive and exclude it in the extraction. */ - public static function extractZipArchive($archive, $destination) { + public static function extractZipArchive($archive, $destination, $exclude_leading_dir) { // Check if webserver supports unzipping. if (!class_exists('ZipArchive')) { $GLOBALS['status'] = array('error' => 'Error: Your PHP version does not support unzip functionality.'); @@ -133,7 +139,18 @@ public static function extractZipArchive($archive, $destination) { if ($zip->open($archive) === TRUE) { // Check if destination is writable if (is_writeable($destination . '/')) { - $zip->extractTo($destination); + // Decide where to extract files + $exclude_leading_dir ? $extract_dir = self::createTmpDir() + : $extract_dir = $destination; + + $zip->extractTo($extract_dir); + + // Check if leading dir should be excluded + if ($exclude_leading_dir) { + self::moveFilesExcludingLeadingDir($extract_dir, $destination); + rmdir($extract_dir); + } + $zip->close(); $GLOBALS['status'] = array('success' => 'Files unzipped successfully'); } @@ -153,8 +170,10 @@ public static function extractZipArchive($archive, $destination) { * The archive name including file extension. E.g. my_archive.zip. * @param string $destination * The relative destination path where to extract files. + * @param boolean $exclude_leading_dir + * If true, check for a leading directory in the archive and exclude it in the extraction. */ - public static function extractGzipFile($archive, $destination) { + public static function extractGzipFile($archive, $destination, $exclude_leading_dir) { // Check if zlib is enabled if (!function_exists('gzopen')) { $GLOBALS['status'] = array('error' => 'Error: Your PHP has no zlib support enabled.'); @@ -178,7 +197,16 @@ public static function extractGzipFile($archive, $destination) { // If we had a tar.gz file, let's extract that tar file. if (pathinfo($destination . '/' . $filename, PATHINFO_EXTENSION) == 'tar') { $phar = new PharData($destination . '/' . $filename); - if ($phar->extractTo($destination)) { + + // Decide where to extract files + $exclude_leading_dir ? $extract_dir = self::createTmpDir() + : $extract_dir = $destination; + + if ($phar->extractTo($extract_dir)) { + if ($exclude_leading_dir) { + self::moveFilesExcludingLeadingDir($extract_dir, $destination); + rmdir($extract_dir); + } $GLOBALS['status'] = array('success' => 'Extracted tar.gz archive successfully.'); // Delete .tar. unlink($destination . '/' . $filename); @@ -198,8 +226,10 @@ public static function extractGzipFile($archive, $destination) { * The archive name including file extension. E.g. my_archive.zip. * @param string $destination * The relative destination path where to extract files. + * @param boolean $exclude_leading_dir + * If true, check for a leading directory in the archive and exclude it in the extraction. */ - public static function extractRarArchive($archive, $destination) { + public static function extractRarArchive($archive, $destination, $exclude_leading_dir) { // Check if webserver supports unzipping. if (!class_exists('RarArchive')) { $GLOBALS['status'] = array('error' => 'Error: Your PHP version does not support .rar archive functionality. How to install RarArchive'); @@ -209,10 +239,21 @@ public static function extractRarArchive($archive, $destination) { if ($rar = RarArchive::open($archive)) { // Check if destination is writable if (is_writeable($destination . '/')) { + // Decide where to extract files + $exclude_leading_dir ? $extract_dir = self::createTmpDir() + : $extract_dir = $destination; + $entries = $rar->getEntries(); foreach ($entries as $entry) { - $entry->extract($destination); + $entry->extract($extract_dir); } + + // Check if leading dir should be excluded + if ($exclude_leading_dir) { + self::moveFilesExcludingLeadingDir($extract_dir, $destination); + rmdir($extract_dir); + } + $rar->close(); $GLOBALS['status'] = array('success' => 'Files extracted successfully.'); } From 3f10a84bb3a41a481ad507856398380791ddcb1c Mon Sep 17 00:00:00 2001 From: Hugo Ankarloo Date: Wed, 12 Aug 2020 21:24:25 +0200 Subject: [PATCH 5/5] Excl. leading dir: Add checkbox in frontend Adds a checkbox to the html for controlling the usage of the new feature of excluding a leading directory in an archive when extracting. --- unzipper.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/unzipper.php b/unzipper.php index 3f94628..6e67650 100644 --- a/unzipper.php +++ b/unzipper.php @@ -20,7 +20,8 @@ // Check if an archive was selected for unzipping. $archive = isset($_POST['zipfile']) ? strip_tags($_POST['zipfile']) : ''; $destination = isset($_POST['extpath']) ? strip_tags($_POST['extpath']) : ''; - $unzipper->prepareExtraction($archive, $destination); + $exclude_leading_dir = isset($_POST['exclude_leading_dir']) ? true : false; + $unzipper->prepareExtraction($archive, $destination, $exclude_leading_dir); } if (isset($_POST['dozip'])) { @@ -495,6 +496,9 @@ public static function zipDir($sourcePath, $outZipPath) {

Enter extraction path without leading or trailing slashes (e.g. "mypath"). If left empty current directory will be used.

+ + +

For archives made of only one single directory containing all files and folders. Check this checkbox to extract archive without including this single directory.