<?php
/*------------------------------------------------------------------------------
  $Id$

  AbanteCart, Ideal OpenSource Ecommerce Solution
  http://www.AbanteCart.com

  Copyright © 2011-2020 Belavier Commerce LLC

  This source file is subject to Open Software License (OSL 3.0)
  License details is bundled with this package in the file LICENSE.txt.
  It is also available at this URL:
  <http://www.opensource.org/licenses/OSL-3.0>

 UPGRADE NOTE:
   Do not edit or add to this file if you wish to upgrade AbanteCart to newer
   versions in the future. If you wish to customize AbanteCart for your
   needs please refer to http://www.AbanteCart.com for more information.
------------------------------------------------------------------------------*/
if (!defined('DIR_CORE')) {
    header('Location: static_pages/');
}

final class Atargz
{
    public function infosTar($src, $data = true)
    {
        if ($this->is_tar($src)) {
            file_put_contents($tmp = '~tmp('.microtime().').tar', $src);
            $src = $tmp;
        }
        $ptr = fopen($src, 'r');
        while (!feof($ptr)) {
            $infos = $this->readTarHeader($ptr);
            if ($infos['name']) {
                if (!$data) {
                    unset($infos['data']);
                }
                $result[$infos['name']] = $infos;
            }
        }
        if (is_file($tmp)) {
            unlink($tmp);
        }
        return $result;
    }

    public function makeTar($src, $dest = false, $compress_level = 5)
    {
        $src = is_array($src) ? $src : array($src);
        $src = array_map('realpath', $src);
        $Tar = '';
        $compress_level = ($compress_level < 1 || $compress_level > 9) ? 5 : $compress_level;
        foreach ($src as $item) {
            $item = str_replace('\\', '/', $item);
        }
        $Tar .= $this->addTarItem(
            $item
                .((is_dir($item) && substr($item, -1) != '/')
                    ? '/'
                    : ''),
            dirname($item).'/');

        $Tar = str_pad($Tar, floor((strlen($Tar) + 10240 - 1) / 10240) * 10240, "\0");
        if (empty($dest)) {
            return $Tar;
        }

        $gz = gzopen($dest, 'w'.(int)$compress_level);
        if ($gz) {
            gzwrite($gz, $Tar);
            gzclose($gz);
        } else {
            $error = new AError('Targz-lib compressing error: Unable to create file '.$dest);
            $error->toLog();
            return false;
        }
    }

    public function extractTar($src, $dest)
    {
        if ($this->is_tar($src)) {
            file_put_contents($tmp = '~tmp('.microtime().').tar', $src);
            $src = $tmp;
        }
        $ptr = gzopen($src, 'r');
        while (!gzeof($ptr)) {
            $infos = $this->readTarHeader($ptr);
            if ($infos['type'] == '5') {
                if (!file_exists($dest.$infos['name'])) {
                    mkdir($dest.$infos['name'], 0777, true);
                }
                $result[] = $dest.$infos['name'];
            } elseif ($infos['type'] == '0' || $infos['type'] == chr(0)) {
                $dirPath = substr($infos['name'], 0, strrpos($infos['name'], '/'));
                if (!file_exists($dest.$dirPath)) {
                    mkdir($dest.$dirPath, 0777, true);
                }
                if (file_put_contents($dest.$infos['name'], $infos['data'])) {
                    $result[] = $dest.$infos['name'];
                }
            }
            if ($infos) {
                chmod($dest.$infos['name'], 0777);
            }
        }
        if (is_file($tmp)) {
            unlink($tmp);
        }
        return $result;
    }

    private function is_tar($str)
    {
        $block = substr($str, 0, 512);
        if (strlen($block) != 512) {
            return false;
        }
        $realchecksum = octdec(substr($str, 148, 8));
        $checksum = 0;
        $block = substr_replace($block, '        ', 148, 8);
        for ($i = 0; $i < 512; $i++) {
            $checksum += ord(substr($block, $i, 1));
        }
        if ($realchecksum == $checksum) {
            return true;
        }
        return false;
    }

    private function tarHeader512($infos)
    { /* http://www.mkssoftware.com/docs/man4/tar.4.asp */
        $bigheader = $header = '';
        if (strlen($infos['name100']) > 100) {
            $bigheader = pack("a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12",
                '././@LongLink', '0000000', '0000000', '0000000',
                sprintf("%011o", strlen($infos['name100'])), '00000000000',
                '        ', 'L', '', 'ustar ', '0',
                $infos['userName32'],
                $infos['groupName32'], '', '', '', '');

            $bigheader .= str_pad($infos['name100'], floor((strlen($infos['name100']) + 512 - 1) / 512) * 512, "\0");

            $checksum = 0;
            for ($i = 0; $i < 512; $i++) {
                $checksum += ord(substr($bigheader, $i, 1));
            }
            $bigheader = substr_replace($bigheader, sprintf("%06o", $checksum)."\0 ", 148, 8);
        }
        $header = pack("a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12", // book the memorie area
            substr($infos['name100'], 0, 100), //  0 	100 	File name
            str_pad(substr(sprintf("%07o", $infos['mode8']), -4), 7, '0', STR_PAD_LEFT), // 100 	8 		File mode
            sprintf("%07o", $infos['uid8']), // 108 	8 		Owner user ID
            sprintf("%07o", $infos['gid8']), // 116 	8 		Group user ID
            sprintf("%011o", $infos['size12']), // 124 	12 		File size in bytes
            sprintf("%011o", $infos['mtime12']), // 136 	12 		Last modification time
            '        ', // 148 	8 		Check sum for header block
            $infos['link1'], // 156 	1 		Link indicator / ustar Type flag
            $infos['link100'], // 157 	100 	Name of linked file
            'ustar ', // 257 	6 		USTAR indicator "ustar"
            ' ', // 263 	2 		USTAR version "00"
            $infos['userName32'], // 265 	32 		Owner user name
            $infos['groupName32'], // 297 	32 		Owner group name
            '', // 329 	8 		Device major number
            '', // 337 	8 		Device minor number
            $infos['prefix155'], // 345 	155 	Filename prefix
            ''); // 500 	12 		??

        $checksum = 0;
        for ($i = 0; $i < 512; $i++) {
            $checksum += ord(substr($header, $i, 1));
        }
        $header = substr_replace($header, sprintf("%06o", $checksum)."\0 ", 148, 8);

        return $bigheader.$header;
    }

    private function addTarItem($item, $racine)
    {
        if(!$item){
            return '';
        }
        $infos['name100'] = str_replace($racine, '', $item);
        list (, , $infos['mode8'], , $infos['uid8'], $infos['gid8'], , , , $infos['mtime12']) = stat($item);
        $infos['size12'] = is_dir($item) ? 0 : filesize($item);

        $infos['link1'] = is_link($item) ? 2 : is_dir($item);
        $infos['link1'] = $infos['link1'] === false ? 5 : 0;

        $infos['link100'] == 2 ? readlink($item) : "";

        $a = function_exists('posix_getpwuid') ? posix_getpwuid(fileowner($item)) : array('name' => 'Unknown');
        $infos['userName32'] = $a['name'];

        $a = function_exists('posix_getgrgid') ? posix_getgrgid(filegroup($item)) : array('name' => 'Unknown');
        $infos['groupName32'] = $a['name'];
        $infos['prefix155'] = '';

        $header = $this->tarHeader512($infos);
        $data = str_pad(file_get_contents($item), floor(($infos['size12'] + 512 - 1) / 512) * 512, "\0");
        $sub = '';
        if (is_dir($item)) {
            $lst = scandir($item);
            array_shift($lst); // remove  ./  of $lst
            array_shift($lst); // remove ../  of $lst
            foreach ($lst as $subitem) {
                $sub .= $this->addTarItem($item.$subitem.(is_dir($item.$subitem) ? '/' : ''), $racine);
            }
        }
        return $header.$data.$sub;
    }

    private function readTarHeader($ptr)
    {
        $block = fread($ptr, 512);
        if (strlen($block) != 512) {
            return false;
        }
        $hdr = unpack("a100name/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1type/a100symlink/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix/a12temp", $block);
        $hdr['mode'] = $hdr['mode'] + 0;
        $hdr['uid'] = octdec($hdr['uid']);
        $hdr['gid'] = octdec($hdr['gid']);
        $hdr['size'] = octdec($hdr['size']);
        $hdr['mtime'] = octdec($hdr['mtime']);
        $hdr['checksum'] = octdec($hdr['checksum']);
        $checksum = 0;
        $block = substr_replace($block, '        ', 148, 8);
        for ($i = 0; $i < 512; $i++) {
            $checksum += ord(substr($block, $i, 1));
        }
        if (isset($hdr['name']) && $hdr['checksum'] == $checksum) {
            if ($hdr['name'] == '././@LongLink' && $hdr['type'] == 'L') {
                $realName = substr(fread($ptr, floor(($hdr['size'] + 512 - 1) / 512) * 512), 0, $hdr['size'] - 1);
                $hdr2 = $this->readTarHeader($ptr);
                $hdr2['name'] = $realName;
                return $hdr2;
            } elseif (strtolower(substr($hdr['magic'], 0, 5) == 'ustar')) {
                if ($hdr['size'] > 0) {
                    $hdr['data'] = substr(fread($ptr, floor(($hdr['size'] + 512 - 1) / 512) * 512), 0, $hdr['size']);
                } else {
                    $hdr['data'] = '';
                }
                return $hdr;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
}
