<?PHP
	// By Magnus Staehelin 2006 - tajgoren@tajgoren.mine.nu
	//
	// A set of functions for working with tarballs
	// Developed using tar (GNU tar) 1.15.91 and PHP 5.1.4-0.1

	// This function prints detailed information for a tar arcihve
	// Excpects a file pointer to a tar archive

	function taropen($filePath, $mode)
	{
		if($fp = fopen($filePath, $mode))
			return $fp;
		return false;
	}

	function tarclose($tarpointer)
	{
		if(fclose($tarpointer))
			return true;
		return false;
	}

	// Analyzing function
	function examine_tar($filePointer)
	{
		$n = 0;
		echo "<table border=0 cellpadding=0 cellspacing=0 width = 600>\n";
		echo "<tr>\n<td>Byte #</td>\n<td>ASCI value</td>\n<td>ASCI symbol</td>\n</tr>\n";
		while(!feof($filePointer))
		{
			echo "<tr>\n";
			$character = fgetc($filePointer);
			echo "<td>" . ++$n . "</td>\n<td>" . ord($character) . "</td>\n<td>" . $character . "</td>\n";
			echo "</tr>\n";
		}
		echo "</table>\n<br>\n";
	}

	// suggestions for functions that create tarballs:
	// tarwrite(<filepointer, destination file>, <resource, logical tar stream>)
	// taraddfile(<filepointer, destination file>, <filepointer, file to add>)

	function tarextractfilelist($archive)
	{
		if($tp = taropen($archive));
		{
			while(!feof($tp))
			{
				$line = fgets($tp, 512);
				// Check if this is a header block, if so, extract the filename from it and add to the filelist
				if(false)
					$filelist = $filelist . "";
			}
			tarclose($archive);
			return $filelist;
		}
		return false;
	}

	function buildfilelist($path)
	{
		if(is_dir($path))
		{
			if(substr($path, -1) != '/')
				$path = $path . '/';
			$list = array($path);
			$dirp = opendir($path);
			while(false !== ($file = readdir($dirp)))
			{
				if($file == '.' || $file == '..');
				elseif(is_dir($path . $file))
					$list = array_merge($list, buildfilelist($path . $file));
				else
					$list = array_merge($list, (array)($path . $file));
			}
			closedir($dirp);
		}
		return $list;
	}

	function tarcreatefromfile($filepath)
	{
		if(file_exists($filepath))
		{
			$filepermissions = substr(base_convert(fileperms($filepath), 10, 8), -4);
			$mtime = filemtime($filepath);
			$owner = fileowner($filepath);
			$group = filegroup($filepath);
				// determine file type and set some values accordingly
			if(is_file($filepath))
			{
				$filemode = '0';
			}
			elseif(is_dir($filepath))
			{
				$filemode = '5';
			}
			elseif(is_link($filepath))
			{
				$filemode = '2';
				$linktarget = readlink($filepath);
			}
			else
				die("Could not determine filetype!");
		}
		else
			die("File not found or not readable!");

		if(is_dir($filepath)
		{
			$directorycontents = $directorycontents . tarcreatefromfile($subfile);
			return tarcreatefilepost($filepath, $mtime, '', $filepermissions, $owner, $group, $filemode, $linktarget) . $directorycontents;
		}
		else
			return tarcreatefilepost($filepath, $mtime, '', $filepermissions, $owner, $group, $filemode, $linktarget);
	}

	function taraddtoarchive($archive, $files)
	{
		$destination = taropen($archive, 'r+');
		// find the ending of the last real 512 byte datablock of the file and set the file position to this+1

		if(is_array($files))
		{
			foreach($files as $file)
			{
				fwrite($destination, tarcreatefromfile($file));
			}
			// get file size and pad to next block of 10240 bytes.
		}
		else
		{
			fwrite($destination, tarcreatefromfile($files));
			// get file size and pad to next block of 10240 bytes.
		}
	}

	// Creates the header for a file instance in a tarball, attaches the contents of the file to the header and then returns it, possibly for writing to a tarball or the direct streaming to a connected web client.
	// Expects at least two arguments:
	// $filename as a string
	// $mtime as a unix timestamp

	// other input values are optional and defaults to the following:
	// $contents is the file contents, defaults to an empty file.
	// $permissions are the file permissions in octal, defaults to 644
	// $owner is the file owner id in octal, defaults to 1750 (1000 in decimal)
	// $group is the files group id in octal, defaults to 1750 (1000 in decimal)
	// $mode is a description of the file type, defaults to 0 which is a regular file
	// $linktarget contains the destination of the link if the file is a link (mode = 2)
	function tarcreatefilepost($filename, $mtime, $contents = '', $permissions = '0000644', $owner = '0001750', $group = '0001750', $mode = '0', $linktarget = '')
	{
		if($contents == '' && file_exists($filename))
		{
			$contents = file_get_contents($filename);
			$filesize = filesize($filename);
		}
		
		if(strlen($filename) < 100)
			$header = $filename;
		else
		{
			$header = "././@LongLink";
			$longfilenameheader = true;
			$filemode = 'L';
		}
		// Pad the filename with ASCI value 0 to the 100'th byte
		$header = str_pad($header, 100, chr(0));		

		// Add permissions, 7 bytes
		$header = $header . str_pad($permissions, 7, '0', STR_PAD_LEFT);
		$header = $header . chr(0);

		// Add file owner id in octal
		$header = $header . str_pad($owner, 7, '0', STR_PAD_LEFT);
		$header = $header . chr(0);

		// Add file group id in octal
		$header = $header . str_pad($group, 7, '0', STR_PAD_LEFT);
		$header = $header . chr(0);

		// Add file size in octal
		if(isset($filesize))
			$length = base_convert($filesize, 10, 8);
		else
			$length = base_convert(strlen($contents), 10, 8);
		$length = str_pad($length, 11, '0', STR_PAD_LEFT);
		$header = $header . $length;
		$header = $header . chr(0);

		// Add UNIX timestamp in octal
		$timestamp = base_convert($mtime, 10, 8);
		$timestamp = str_pad($timestamp, 11, '0');
		$header = $header . $timestamp;
		$header = $header . chr(0);

		// add a fake checksum of 8 ascii spaces (since the actual checksum is calculated with these bytes as ascii spaces anyway)
		$header = $header . "        ";

		// Add byte for type of file (directory, link, regular file etc)		
		$header = $header . $mode;

		// If this header describes a link, specify the target now.
		if($mode == '2')
			$header = $header . $linktarget;

		// Pad up to and including the 257th byte
		$header = str_pad($header, 257, chr(0));

		// Add USTAR compatibility field
		$header = $header . "ustar  ";
		$header = $header . chr(0);

		// Add user name
		$header = $header . "tajgoren";
		// Pad to and including 297th byte
		$header = str_pad($header, 297, chr(0));

		// Add group name
		$header = $header . "tajgoren";
		// Pad to 512th byte
		$header = str_pad($header, 512, chr(0));

		// Calculate the actual checksum for the header and replace the fake one with this real value.
		$checksum = 0;
		for($n = 0; $n < 512; $n++)
			$checksum += ord(substr($header, $n, 1));
		$checksum = base_convert($checksum, 10, 8);
		$checksum = str_pad($checksum, 6, '0', STR_PAD_LEFT);
		$checksum = $checksum . chr(0) . " ";
		$header = substr_replace($header, $checksum, 148, 8);

		// If this was a regular file we attach the contents of the file to the header but first we pad it to a full block of 512 bytes
		if($mode == '0')
		{
			$padding = strlen($contents) + 512 - (strlen($contents) % 512);
			$contents = str_pad($contents, $padding, chr(0));
			$header = $header . $contents;
		}
		// The header is now created but if this was a LongLink header we have to add two more frames to it, do this now.
		elseif($longfilenameheader)
		{
			// The first frame is 512 bytes and concists of only the filename padded to the 512th byte with ASCI value 0
			$frame1 = $filename;
			$frame1 = str_pad($frame1, 512, chr(0));

			// Create the second frame, this is just the same as the normal header but replace the 100 first bytes with the 100 first bytes of the filename.
			$filename2 = substr($filename, 0, 100);
			$frame2 = substr($frame2, 100);
			$frame2 = $filename2 . $frame2;

			// Attach the frames to the header
			$header = $header . $frame1;
			$header = $header . $frame2;
		}

		return $header;
	}

	// excpects the contents to be written and a writeable filepointer which to write to
	// returns true if write was successfull and false otherwise.
	function tarsavetofile($source, $destination)
	{
		$padding = strlen($source) + 10240 - (strlen($source) % 10240);
		$source = str_pad($source, $padding, chr(0));
		if(fwrite($destination, $source) === true)
			return true;
		return false;
	}
PHP?>
