1
0
mirror of https://github.com/Ne-Lexa/php-zip.git synced 2025-08-29 17:59:55 +02:00

Added a new option for extracting unix symlinks.

Added new parameter to get the list of extracted files.
This commit is contained in:
Ne-Lexa
2020-01-22 12:49:45 +03:00
parent 47161bdb02
commit 8dcde47072
6 changed files with 201 additions and 65 deletions

View File

@@ -2,6 +2,9 @@
namespace PhpZip\Constants;
use PhpZip\IO\ZipReader;
use PhpZip\ZipFile;
/**
* Interface ZipOptions.
*/
@@ -10,20 +13,50 @@ interface ZipOptions
/**
* Boolean option for store just file names (skip directory names).
*
* @var string
* @see ZipFile::addFromFinder()
*/
const STORE_ONLY_FILES = 'only_files';
/** @var string */
/**
* Uses the specified compression method.
*
* @see ZipFile::addFromFinder()
* @see ZipFile::addSplFile()
*/
const COMPRESSION_METHOD = 'compression_method';
/** @var string */
/**
* Set the specified record modification time.
* The value can be {@see \DateTimeInterface}, integer timestamp
* or a string of any format.
*
* @see ZipFile::addFromFinder()
* @see ZipFile::addSplFile()
*/
const MODIFIED_TIME = 'mtime';
/**
* @var string
* Specifies the encoding of the record name for cases when the UTF-8
* usage flag is not set.
*
* The most commonly used encodings are compiled into the constants
* of the {@see DosCodePage} class.
*
* @see ZipFile::openFile()
* @see ZipFile::openFromString()
* @see ZipFile::openFromStream()
* @see ZipReader::getDefaultOptions()
* @see DosCodePage::getCodePages()
*/
const CHARSET = 'charset';
/**
* Allows ({@see true}) or denies ({@see false}) unpacking unix symlinks.
*
* This is a potentially dangerous operation for uncontrolled zip files.
* By default is ({@see false}).
*
* @see https://josipfranjkovic.blogspot.com/2014/12/reading-local-files-from-facebooks.html
*/
const EXTRACT_SYMLINKS = 'extract_symlinks';
}

View File

@@ -43,9 +43,10 @@ final class FilesUtil
\RecursiveIteratorIterator::CHILD_FIRST
);
/** @var \SplFileInfo $fileInfo */
foreach ($files as $fileInfo) {
$function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
$function($fileInfo->getRealPath());
$function($fileInfo->getPathname());
}
rmdir($dir);
}
@@ -303,36 +304,19 @@ final class FilesUtil
}
/**
* @param string $linkPath
* @param string $target
* @param string $path
* @param bool $allowSymlink
*
* @return bool
*/
public static function symlink($target, $linkPath)
public static function symlink($target, $path, $allowSymlink)
{
if (\DIRECTORY_SEPARATOR === '\\') {
$linkPath = str_replace('/', '\\', $linkPath);
$target = str_replace('/', '\\', $target);
$abs = null;
if (!self::isAbsolutePath($target)) {
$abs = realpath(\dirname($linkPath) . \DIRECTORY_SEPARATOR . $target);
if (\is_string($abs)) {
$target = $abs;
}
}
if (\DIRECTORY_SEPARATOR === '\\' || !$allowSymlink) {
return file_put_contents($path, $target) !== false;
}
if (!symlink($target, $linkPath)) {
if (\DIRECTORY_SEPARATOR === '\\' && is_file($target)) {
return copy($target, $linkPath);
}
return false;
}
return true;
return symlink($target, $path);
}
/**

View File

@@ -6,6 +6,7 @@
namespace PhpZip;
use PhpZip\Constants\UnixStat;
use PhpZip\Constants\ZipCompressionLevel;
use PhpZip\Constants\ZipCompressionMethod;
use PhpZip\Constants\ZipEncryptionMethod;
@@ -374,15 +375,20 @@ class ZipFile implements ZipFileInterface
*
* Extract the complete archive or the given files to the specified destination.
*
* @param string $destDir location where to extract the files
* @param array|string|null $entries The entries to extract. It accepts either
* a single entry name or an array of names.
* @param string $destDir location where to extract the files
* @param array|string|null $entries entries to extract
* @param array $options extract options
* @param array $extractedEntries if the extractedEntries argument
* is present, then the specified
* array will be filled with
* information about the
* extracted entries
*
* @throws ZipException
*
* @return ZipFile
*/
public function extractTo($destDir, $entries = null)
public function extractTo($destDir, $entries = null, array $options = [], &$extractedEntries = [])
{
if (!file_exists($destDir)) {
throw new ZipException(sprintf('Destination %s not found', $destDir));
@@ -396,7 +402,14 @@ class ZipFile implements ZipFileInterface
throw new ZipException('Destination is not writable directory');
}
$extractedEntries = [];
if ($extractedEntries === null) {
$extractedEntries = [];
}
$defaultOptions = [
ZipOptions::EXTRACT_SYMLINKS => false,
];
$options += $defaultOptions;
$zipEntries = $this->zipContainer->getEntries();
@@ -497,9 +510,8 @@ class ZipFile implements ZipFileInterface
unlink($file);
throw $e;
} finally {
fclose($handle);
}
fclose($handle);
if ($unixMode === 0) {
$unixMode = 0644;
@@ -514,8 +526,10 @@ class ZipFile implements ZipFileInterface
}
}
$allowSymlink = (bool) $options[ZipOptions::EXTRACT_SYMLINKS];
foreach ($symlinks as $linkPath => $target) {
if (!FilesUtil::symlink($target, $linkPath)) {
if (!FilesUtil::symlink($target, $linkPath, $allowSymlink)) {
unset($extractedEntries[$linkPath]);
}
}
@@ -526,7 +540,7 @@ class ZipFile implements ZipFileInterface
touch($dir, $lastMod);
}
// ksort($extractedEntries);
ksort($extractedEntries);
return $this;
}
@@ -663,9 +677,24 @@ class ZipFile implements ZipFileInterface
$entryName = $file->isDir() ? rtrim($entryName, '/\\') . '/' : $entryName;
$zipEntry = new ZipEntry($entryName);
$zipData = null;
$zipEntry->setCreatedOS(ZipPlatform::OS_UNIX);
$zipEntry->setExtractedOS(ZipPlatform::OS_UNIX);
if ($file->isFile()) {
$zipData = null;
$filePerms = $file->getPerms();
if ($file->isLink()) {
$linkTarget = $file->getLinkTarget();
$lengthLinkTarget = \strlen($linkTarget);
$zipEntry->setCompressionMethod(ZipCompressionMethod::STORED);
$zipEntry->setUncompressedSize($lengthLinkTarget);
$zipEntry->setCompressedSize($lengthLinkTarget);
$zipEntry->setCrc(crc32($linkTarget));
$filePerms |= UnixStat::UNX_IFLNK;
$zipData = new ZipNewData($zipEntry, $linkTarget);
} elseif ($file->isFile()) {
if (isset($options[ZipOptions::COMPRESSION_METHOD])) {
$compressionMethod = $options[ZipOptions::COMPRESSION_METHOD];
} elseif ($file->getSize() < 512) {
@@ -685,21 +714,9 @@ class ZipFile implements ZipFileInterface
$zipEntry->setUncompressedSize(0);
$zipEntry->setCompressedSize(0);
$zipEntry->setCrc(0);
} elseif ($file->isLink()) {
$linkTarget = $file->getLinkTarget();
$lengthLinkTarget = \strlen($linkTarget);
$zipEntry->setCompressionMethod(ZipCompressionMethod::STORED);
$zipEntry->setUncompressedSize($lengthLinkTarget);
$zipEntry->setCompressedSize($lengthLinkTarget);
$zipEntry->setCrc(crc32($linkTarget));
$zipData = new ZipNewData($zipEntry, $linkTarget);
}
$zipEntry->setCreatedOS(ZipPlatform::OS_UNIX);
$zipEntry->setExtractedOS(ZipPlatform::OS_UNIX);
$zipEntry->setUnixMode($file->getPerms());
$zipEntry->setUnixMode($filePerms);
$timestamp = null;

View File

@@ -292,15 +292,20 @@ interface ZipFileInterface extends \Countable, \ArrayAccess, \Iterator
*
* Extract the complete archive or the given files to the specified destination.
*
* @param string $destDir location where to extract the files
* @param array|string|null $entries The entries to extract. It accepts either
* a single entry name or an array of names.
* @param string $destDir location where to extract the files
* @param array|string|null $entries entries to extract
* @param array $options extract options
* @param array $extractedEntries if the extractedEntries argument
* is present, then the specified
* array will be filled with
* information about the
* extracted entries
*
* @throws ZipException
*
* @return ZipFile
*/
public function extractTo($destDir, $entries = null);
public function extractTo($destDir, $entries = null, array $options = [], &$extractedEntries = []);
/**
* Add entry from the string.