mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-11-02 10:28:50 +03:00
Use custom TAR builder instead of PharData
This commit is contained in:
parent
61a16e2e72
commit
2de4ebac54
2 changed files with 282 additions and 5 deletions
|
|
@ -2,6 +2,7 @@
|
|||
<?php
|
||||
define('ROOT_DIR', dirname(__DIR__));
|
||||
chdir(ROOT_DIR);
|
||||
require(ROOT_DIR . '/snappymail/v/0.0.0/app/libraries/snappymail/tar.php');
|
||||
|
||||
$options = getopt('', ['aur','docker','plugins','skip-gulp','debian','nextcloud','owncloud','cpanel','sign']);
|
||||
|
||||
|
|
@ -98,7 +99,9 @@ if (!$zip->open($zip_destination, ZIPARCHIVE::CREATE)) {
|
|||
exit("Failed to create {$zip_destination}");
|
||||
}
|
||||
|
||||
$tar = new PharData($tar_destination);
|
||||
$tar_destination .= '.gz';
|
||||
$tar = new \SnappyMail\TAR($tar_destination);
|
||||
//$tar = new PharData($tar_destination);
|
||||
|
||||
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('snappymail/v'), RecursiveIteratorIterator::SELF_FIRST);
|
||||
foreach ($files as $file) {
|
||||
|
|
@ -158,10 +161,13 @@ $zip->addFile('README.md');
|
|||
$tar->addFile('README.md');
|
||||
|
||||
$zip->close();
|
||||
|
||||
$tar->compress(Phar::GZ);
|
||||
unlink($tar_destination);
|
||||
$tar_destination .= '.gz';
|
||||
if ($tar instanceof PharData) {
|
||||
$tar->compress(Phar::GZ);
|
||||
unlink($tar_destination);
|
||||
$tar_destination .= '.gz';
|
||||
} else {
|
||||
$tar->close();
|
||||
}
|
||||
|
||||
echo "{$zip_destination} created\n{$tar_destination} created\n";
|
||||
|
||||
|
|
|
|||
|
|
@ -98,4 +98,275 @@ class TAR
|
|||
\gzclose($fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
const
|
||||
TYPE_FILE = 0,
|
||||
TYPE_LINK = 2,
|
||||
TYPE_DIR = 5;
|
||||
|
||||
protected
|
||||
$gzip = array('w' => null, 'h' => null, 'l' => 0),
|
||||
$stream = null,
|
||||
$stream_info = null;
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
if ($this->stream) {
|
||||
return;
|
||||
}
|
||||
$this->stream = \fopen($this->filename, 'wb');
|
||||
if (\str_ends_with($this->filename, '.gz')) {
|
||||
// Write gzip header, see http://www.zlib.org/rfc-gzip.html#member-format
|
||||
if (!\fwrite($this->stream, "\x1F\x8B\x08\x00".\pack('V', \time())."\0\x03", 10)) {
|
||||
throw new \Exception('Failed to write to stream');
|
||||
}
|
||||
// Start compression
|
||||
$this->gzip['w'] = \stream_filter_append($this->stream, 'zlib.deflate', STREAM_FILTER_WRITE, array(
|
||||
'level' => 9,
|
||||
// 'window' => 32768,
|
||||
'memory' => 9,
|
||||
));
|
||||
// Start CRC32 hashing
|
||||
$this->gzip['h'] = \hash_init('crc32b');
|
||||
}
|
||||
$this->stream_info = new \SplFileInfo($this->filename);
|
||||
}
|
||||
|
||||
function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
public function close(): bool
|
||||
{
|
||||
if (!$this->stream) {
|
||||
return false;
|
||||
}
|
||||
// Write tar footer
|
||||
$this->write(\pack('a1024', ''));
|
||||
// Stop compression
|
||||
if ($this->gzip['w']) {
|
||||
\stream_filter_remove($this->gzip['w']);
|
||||
// hash_final is a string, not an integer
|
||||
$crc = \hash_final($this->gzip['h'], 1);
|
||||
// write the little endian CRC32 and uncompressed file size
|
||||
\fwrite($this->stream, $crc[3].$crc[2].$crc[1].$crc[0].\pack('V', $this->gzip['l']), 8);
|
||||
}
|
||||
\fclose($this->stream);
|
||||
$this->stream = $this->stream_info = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function addFile(string $name, ?string $localName = null, ?\SplFileInfo $fileinfo = null): void
|
||||
{
|
||||
$this->start();
|
||||
if (!$fileinfo) {
|
||||
$fileinfo = new \SplFileInfo($name);
|
||||
}
|
||||
if (!$localName) {
|
||||
$localName = $name;
|
||||
}
|
||||
if ($fileinfo->isLink()) {
|
||||
$stat = \lstat($fileinfo);
|
||||
$target = $fileinfo->getLinkTarget();
|
||||
if (\dirname($fileinfo->getPathname()) === \dirname($fileinfo->getRealPath())) {
|
||||
$target = \basename($target);
|
||||
}
|
||||
$this->writeEntryHeader(
|
||||
$localName,
|
||||
static::TYPE_LINK,
|
||||
0,
|
||||
$stat['mode'],
|
||||
$target
|
||||
);
|
||||
} else if ($fileinfo->isDir()) {
|
||||
$this->writeEntryHeader(
|
||||
\rtrim($localName, '/') . '/',
|
||||
static::TYPE_DIR,
|
||||
0,
|
||||
$fileinfo->getPerms()
|
||||
);
|
||||
} else {
|
||||
$file = $fileinfo->openFile('rb');
|
||||
$this->writeEntryHeader(
|
||||
$localName,
|
||||
static::TYPE_FILE,
|
||||
$file->getSize(),
|
||||
$fileinfo->getPerms()
|
||||
);
|
||||
while (!$file->eof()) {
|
||||
$data = $file->fread(4096);
|
||||
if (false === $data || '' === $data) {
|
||||
break;
|
||||
}
|
||||
$this->write($data);
|
||||
}
|
||||
if ($l = $file->ftell() % 512) {
|
||||
$l = 512 - $l;
|
||||
$this->write(\pack("a{$l}", ''));
|
||||
}
|
||||
unset($file);
|
||||
}
|
||||
}
|
||||
|
||||
public function addFromString(string $name, string $contents): void
|
||||
{
|
||||
$this->start();
|
||||
$this->writeEntryHeader(
|
||||
$name,
|
||||
static::TYPE_FILE,
|
||||
\strlen($contents),
|
||||
$this->stream_info->getPerms()
|
||||
);
|
||||
$this->write($contents);
|
||||
if ($l = \strlen($contents) % 512) {
|
||||
$l = 512 - $l;
|
||||
$this->write(\pack("a{$l}", ''));
|
||||
}
|
||||
}
|
||||
|
||||
public function buildFromDirectory(string $dir, string $pattern = ''): array
|
||||
{
|
||||
$this->start();
|
||||
\clearstatcache();
|
||||
$dir = \rtrim($dir,'\\/') . '/';
|
||||
$dirl = \strlen($dir);
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS /*| \FilesystemIterator::FOLLOW_SYMLINKS*/),
|
||||
\RecursiveIteratorIterator::SELF_FIRST,
|
||||
\RecursiveIteratorIterator::CATCH_GET_CHILD);
|
||||
$ignore_paths = array();
|
||||
foreach ($iterator as $name => $fileinfo) {
|
||||
if ($fileinfo->isDir() && \is_file("{$fileinfo}/CACHEDIR.TAG")) {
|
||||
// like: tar --exclude-caches -czf file.tgz *
|
||||
$ignore_paths[] = \preg_quote($name . '/', '#');
|
||||
continue;
|
||||
}
|
||||
if ($ignore_paths && \preg_match('#'.\implode('|',$ignore_paths).'#', $name)) {
|
||||
continue;
|
||||
}
|
||||
if (!$pattern || \preg_match($pattern, $name)) {
|
||||
$name = \substr($name, $dirl);
|
||||
$this->addFile($name, null, $fileinfo);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function write(string $string): int
|
||||
{
|
||||
$length = \strlen($string);
|
||||
$written = 0;
|
||||
while ($written < $length) {
|
||||
$bytes = \fwrite($this->stream, $written ? \substr($string, $written) : $string);
|
||||
if (!$bytes) {
|
||||
return $written ?: false;
|
||||
}
|
||||
$written += $bytes;
|
||||
}
|
||||
|
||||
if ($this->gzip['h']) {
|
||||
$this->gzip['l'] += $length;
|
||||
\hash_update($this->gzip['h'], $string);
|
||||
}
|
||||
|
||||
return $written;
|
||||
}
|
||||
|
||||
protected function writeEntryHeader(string $name, int $type, int $size,
|
||||
int $perm = 0, string $link = '', string $prefix = ''
|
||||
): void
|
||||
{
|
||||
// handle long filename length
|
||||
$paxdata = $paxname = '';
|
||||
if (100 < \strlen($link)) {
|
||||
$length = \strlen($link) + 11;
|
||||
$length += \strlen($length);
|
||||
$paxdata = "{$length} linkpath={$link}\n";
|
||||
$link = '././@LongSymLink';
|
||||
}
|
||||
$l = \strlen($name);
|
||||
if (($paxdata?90:100) < $l) {
|
||||
// split into name and prefix
|
||||
$p = \strpos($name, '/', \max(0, $l - 90));
|
||||
if ($p && $p < $l-1) {
|
||||
$file = \substr($name, $p+1);
|
||||
$prefix = \substr($name, 0, $p);
|
||||
$paxname = \preg_replace('#(^|/)([^/]+)$#', '$1PaxHeader/$2', $file);
|
||||
} else {
|
||||
$file = \basename($name);
|
||||
if (static::TYPE_DIR === $type) {
|
||||
$file .= '/';
|
||||
}
|
||||
$prefix = \dirname($name);
|
||||
$paxname = 'PaxHeader/' . $file;
|
||||
}
|
||||
if (100 < \strlen($file) || 155 < \strlen($prefix)) {
|
||||
// POSIX.1-2001/pax
|
||||
$length = $l + 7;
|
||||
$length += \strlen($length);
|
||||
$paxdata = "{$length} path={$name}\n{$paxdata}";
|
||||
if (static::TYPE_DIR === $type) {
|
||||
$name = \substr($file, 0, 98) . '/';
|
||||
} else {
|
||||
$name = \substr($file, 0, 99);
|
||||
}
|
||||
} else {
|
||||
// POSIX ustar
|
||||
$name = $file;
|
||||
}
|
||||
$paxname = \substr($paxname, 0, 98);
|
||||
}
|
||||
if ($paxdata) {
|
||||
/* Add?
|
||||
$data .= "30 mtime=1461056595.149922432\n"
|
||||
$data .= "30 ctime=1461056595.149922432\n"
|
||||
*/
|
||||
$this->writeUStarEntryHeader(
|
||||
$paxname ?: \preg_replace('#(^|/)([^/]+)$#', '$1PaxHeader/$2', $name),
|
||||
'x',
|
||||
\strlen($paxdata),
|
||||
$perm,
|
||||
'',
|
||||
$prefix
|
||||
);
|
||||
$l = 512 * \ceil(\strlen($paxdata) / 512);
|
||||
$this->write(\pack("a{$l}", $paxdata));
|
||||
}
|
||||
|
||||
$this->writeUStarEntryHeader($name, $type, $size, $perm, $link, $prefix);
|
||||
}
|
||||
|
||||
// Writes Pre-POSIX.1-1988 (i.e. v7) and POSIX UStar headers
|
||||
protected function writeUStarEntryHeader(string $name, int $type, int $size,
|
||||
int $perm = 0, string $link = '', string $prefix = ''
|
||||
): void
|
||||
{
|
||||
$uid = 0; // $this->stream_info->getOwner(),
|
||||
$gid = 0; // $this->stream_info->getGroup(),
|
||||
$mtime = $this->stream_info->getMTime();
|
||||
$data = \pack('a100a8a8a8a12A12',
|
||||
$name,
|
||||
// values in octal
|
||||
\sprintf("%06u ", \substr(\decoct($perm),-3)),
|
||||
\sprintf("%06u ", \decoct($uid)),
|
||||
\sprintf("%06u ", \decoct($gid)),
|
||||
\sprintf("%011u ", \decoct($size)),
|
||||
\sprintf("%011u", \decoct($mtime)));
|
||||
$this->write($data);
|
||||
$checksum = 0;
|
||||
$i = 148;
|
||||
while ($i--) {
|
||||
$checksum += \ord($data[$i]);
|
||||
}
|
||||
|
||||
$data = \pack('a1a100a6a2a32a32a8a8a155a12', "{$type}", $link, 'ustar', '00', '', '', '000000 ', '000000 ', $prefix, '');
|
||||
$checksum += 256;
|
||||
$i = 356;
|
||||
while ($i--) {
|
||||
$checksum += \ord($data[$i]);
|
||||
}
|
||||
|
||||
$this->write(\pack('a8', \sprintf('%06u ', \decoct($checksum))) . $data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue