<?php
/**
* @package RSForm! Pro
* @copyright (C) 2007-2019 www.rsjoomla.com
* @license GPL, http://www.gnu.org/copyleft/gpl.html
*/
defined('_JEXEC') or die;
class RSFormProTar
{
// Holds the path to the archive.
protected $path;
// File pointer.
protected $fh;
protected $chunk = 1048576;
public function __construct($path) {
$this->path = $path;
if (!file_exists($this->path) && file_put_contents($this->path, '') === false) {
throw new Exception(sprintf('Could not create TAR archive "%s"!', $this->path));
}
// Open the archive for appending - binary mode.
$this->fh = @fopen($this->path, 'r+b');
if (!$this->fh) {
throw new Exception(sprintf('Could not open TAR archive "%s" for appending!', $this->path));
}
// Always appending...
$this->seek(0, SEEK_END);
}
// Closes the archive.
public function close() {
return fclose($this->fh);
}
// Seek to a certain position.
public function seek($offset, $whence = SEEK_SET) {
return fseek($this->fh, $offset, $whence);
}
// Tell the current position.
public function tell() {
return ftell($this->fh);
}
// Write to the archive.
protected function write($string) {
return fwrite($this->fh, $string);
}
// Read from file.
protected function read($size) {
return fread($this->fh, $size);
}
// Add a string in the archive.
public function add($string) {
// Write the actual data.
return $this->write($string);
}
// Adds padding if size doesn't split in an exact number of 512 blocks.
public function addPadding($size) {
$padding = $size % 512 ? (512 - $size % 512) : 0;
$this->write(pack(sprintf('a%d', $padding), ''));
}
// Adds an empty header that can be populated later on.
public function addEmptyHeader() {
return $this->write(pack('a512', ''));
}
// Adds the file header
public function addHeader($size, $name) {
$size = decoct($size);
$info = stat($this->path);
$uid = sprintf('%6s ', decoct($info[4]));
$gid = sprintf('%6s ', decoct($info[5]));
$mode = sprintf('%6s ', decoct(fileperms($this->path)));
$mtime = sprintf('%11s', decoct(filemtime($this->path)));
// Compute the "before" & "after" checksum chunks.
$before = pack('a100a8a8a8a12A12', $name, $mode, $uid, $gid, $size, $mtime);
$after = pack('a1a100a6a2a32a32a8a8a155a12', '', '', '', '', '', '', '', '', '', '');
// Compute checksum
$checksum = 0;
for ($i = 0; $i < 148; $i++) {
$checksum += ord(substr($before, $i, 1));
}
for ($i = 148; $i < 156; $i++) {
$checksum += ord(' ');
}
for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
$checksum += ord(substr($after, $j, 1));
}
$checksum = sprintf('%6s ', decoct($checksum));
$checksum = pack('a8', $checksum);
$this->write($before.$checksum.$after);
}
// Adds the final footer to the TAR archive
public function addFooter() {
return $this->write(pack('a1024', ''));
}
// Compress the TAR using GZIP compression.
public function compress($seek = 0) {
if (!function_exists('gzopen') || !is_callable('gzopen')) {
throw new Exception('The gzopen() function is missing from your PHP installation.');
}
if (!function_exists('gzwrite') || !is_callable('gzwrite')) {
throw new Exception('The gzwrite() function is missing from your PHP installation.');
}
if (!function_exists('gzclose') || !is_callable('gzclose')) {
throw new Exception('The gzclose() function is missing from your PHP installation.');
}
$gzip = $this->getGzipPath();
$gz = @gzopen($gzip, 'a1');
if (!$gz) {
throw new Exception(sprintf('Could not create compressed archive %s', $gzip));
}
// Seek to requested position
$this->seek($seek);
// Read chunk
$data = $this->read($this->getChunkSize());
// Remember our position
$position = $this->tell();
// Write data
if ($data !== false) {
gzwrite($gz, $data);
}
if (feof($this->fh)) {
$position = 0;
}
// Close
gzclose($gz);
return $position;
}
// Gets the chunk size (used for compression)
public function getChunkSize() {
return $this->chunk;
}
public function setChunkSize($size) {
$this->chunk = $size;
}
// Gets current archive size.
public function getSize() {
return filesize($this->path);
}
public function getGzipPath()
{
return substr($this->path, 0, -3) . 'tgz';
}
}