%PDF- %PDF-
Direktori : /home/qgbqkvz/www/wp-content/plugins/duplicator/src/Libs/DupArchive/ |
Current File : /home/qgbqkvz/www/wp-content/plugins/duplicator/src/Libs/DupArchive/DupArchiveExpandBasicEngine.php |
<?php /** * * @package Duplicator * @copyright (c) 2021, Snapcreek LLC * */ namespace Duplicator\Libs\DupArchive; use Duplicator\Libs\DupArchive\Headers\DupArchiveReaderDirectoryHeader; use Duplicator\Libs\DupArchive\Headers\DupArchiveReaderFileHeader; use Duplicator\Libs\DupArchive\Headers\DupArchiveReaderGlobHeader; use Duplicator\Libs\DupArchive\Headers\DupArchiveReaderHeader; use Duplicator\Libs\DupArchive\Info\DupArchiveExpanderInfo; use Exception; class DupArchiveExpandBasicEngine extends DupArchive { protected static $logCallback = null; protected static $chmodCallback = null; protected static $mkdirCallback = null; /** * Set callabcks function * * @param null|callable $log function callback * @param null|callable $chmod function callback * @param null|callable $mkdir function callback * @return void */ public static function setCallbacks($log, $chmod, $mkdir) { self::$logCallback = (is_callable($log) ? $log : null); self::$chmodCallback = (is_callable($chmod) ? $chmod : null); self::$mkdirCallback = (is_callable($mkdir) ? $mkdir : null); } /** * Write log * * @param string $s string * @param bool $flush if true flush file * * @return void */ public static function log($s, $flush = false) { if (self::$logCallback == null) { return; } call_user_func(self::$logCallback, "MINI EXPAND:$s", $flush); } /** * Expand folder * * @param string $archivePath archive path * @param string $relativePath relative path * @param string $destPath dest path * @param bool $ignoreErrors if true ignore errors * @param int $offset start scan location * * @return void */ public static function expandDirectory($archivePath, $relativePath, $destPath, $ignoreErrors = false, $offset = 0) { self::expandItems($archivePath, $relativePath, $destPath, $ignoreErrors, $offset); } /** * Expand items * * @param string $archivePath archive path * @param string[] $inclusionFilter filters * @param string $destDirectory dest path * @param bool $ignoreErrors if true ignore errors * @param int $offset start scan location * * @return void */ private static function expandItems($archivePath, $inclusionFilter, $destDirectory, $ignoreErrors = false, $offset = 0) { $archiveHandle = fopen($archivePath, 'rb'); if ($archiveHandle === false) { throw new Exception("Can’t open archive at $archivePath!"); } $archiveHeader = DupArchiveReaderHeader::readFromArchive($archiveHandle); $writeInfo = new DupArchiveExpanderInfo(); $writeInfo->destDirectory = $destDirectory; $writeInfo->isCompressed = $archiveHeader->isCompressed; if ($offset > 0) { fseek($archiveHandle, $offset); } $moreToRead = true; while ($moreToRead) { if ($writeInfo->currentFileHeader != null) { try { if (self::passesInclusionFilter($inclusionFilter, $writeInfo->currentFileHeader->relativePath)) { self::writeToFile($archiveHandle, $writeInfo); $writeInfo->fileWriteCount++; } elseif ($writeInfo->currentFileHeader->fileSize > 0) { self::skipFileInArchive($archiveHandle, $writeInfo->currentFileHeader); } $writeInfo->currentFileHeader = null; // Expand state taken care of within the write to file to ensure consistency } catch (Exception $ex) { if (!$ignoreErrors) { throw $ex; } } } else { $headerType = self::getNextHeaderType($archiveHandle); switch ($headerType) { case self::HEADER_TYPE_FILE: $writeInfo->currentFileHeader = DupArchiveReaderFileHeader::readFromArchive($archiveHandle, false, true); break; case self::HEADER_TYPE_DIR: $directoryHeader = DupArchiveReaderDirectoryHeader::readFromArchive($archiveHandle, true); // self::log("considering $inclusionFilter and {$directoryHeader->relativePath}"); if (self::passesInclusionFilter($inclusionFilter, $directoryHeader->relativePath)) { // self::log("passed"); $directory = "{$writeInfo->destDirectory}/{$directoryHeader->relativePath}"; // $mode = $directoryHeader->permissions; // rodo handle this more elegantly @mkdir($directory, $directoryHeader->permissions, true); if (is_callable(self::$mkdirCallback)) { call_user_func(self::$mkdirCallback, $directory, 'u+rwx', true); } else { mkdir($directory, 0755, true); } $writeInfo->directoryWriteCount++; } else { // self::log("didnt pass"); } break; case self::HEADER_TYPE_NONE: $moreToRead = false; } } } fclose($archiveHandle); } /** * Write to file * * @param resource $archiveHandle archive file handle * @param DupArchiveExpanderInfo $writeInfo write info * * @return void */ private static function writeToFile($archiveHandle, DupArchiveExpanderInfo $writeInfo) { $destFilePath = $writeInfo->getCurrentDestFilePath(); if ($writeInfo->currentFileHeader->fileSize > 0) { $parentDir = dirname($destFilePath); if (!file_exists($parentDir)) { if (is_callable(self::$mkdirCallback)) { $res = call_user_func(self::$mkdirCallback, $parentDir, 'u+rwx', true); } else { $res = mkdir($parentDir, 0755, true); } if (!$res) { throw new Exception("Couldn't create {$parentDir}"); } } $destFileHandle = fopen($destFilePath, 'wb+'); if ($destFileHandle === false) { throw new Exception("Couldn't open {$destFilePath} for writing."); } do { self::appendGlobToFile($archiveHandle, $destFileHandle, $writeInfo); $currentFileOffset = ftell($destFileHandle); $moreGlobstoProcess = $currentFileOffset < $writeInfo->currentFileHeader->fileSize; } while ($moreGlobstoProcess); fclose($destFileHandle); if (is_callable(self::$chmodCallback)) { call_user_func(self::$chmodCallback, $destFilePath, 'u+rw'); } else { chmod($destFilePath, 0644); } self::validateExpandedFile($writeInfo); } else { if (touch($destFilePath) === false) { throw new Exception("Couldn't create $destFilePath"); } if (is_callable(self::$chmodCallback)) { call_user_func(self::$chmodCallback, $destFilePath, 'u+rw'); } else { chmod($destFilePath, 0644); } } } /** * Validate file * * @param DupArchiveExpanderInfo $writeInfo write info * * @return void */ private static function validateExpandedFile(DupArchiveExpanderInfo $writeInfo) { if ($writeInfo->currentFileHeader->hash !== '00000000000000000000000000000000') { $hash = hash_file('crc32b', $writeInfo->getCurrentDestFilePath()); if ($hash !== $writeInfo->currentFileHeader->hash) { throw new Exception("MD5 validation fails for {$writeInfo->getCurrentDestFilePath()}"); } } } /** * Undocumented function * Assumption is that archive handle points to a glob header on this call * * @param resource $archiveHandle archive handle * @param resource $destFileHandle dest file handle * @param DupArchiveExpanderInfo $writeInfo write info * * @return void */ private static function appendGlobToFile($archiveHandle, $destFileHandle, DupArchiveExpanderInfo $writeInfo) { $globHeader = DupArchiveReaderGlobHeader::readFromArchive($archiveHandle, false); $globContents = fread($archiveHandle, $globHeader->storedSize); if ($globContents === false) { throw new Exception("Error reading glob from " . $writeInfo->getCurrentDestFilePath()); } if ($writeInfo->isCompressed) { $globContents = gzinflate($globContents); } if (fwrite($destFileHandle, $globContents) !== strlen($globContents)) { throw new Exception("Unable to write all bytes of data glob to storage."); } } /** * Check filter * * @param string $filter filter * @param string $candidate candidate * * @return bool */ private static function passesInclusionFilter($filter, $candidate) { return (substr($candidate, 0, strlen($filter)) == $filter); } }