142 lines
4.7 KiB
PHP
142 lines
4.7 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (LGPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
|
|
*
|
|
* @author Michael Cochrane <mike@graftonhall.co.nz>
|
|
* @author Michael Slusarz <slusarz@horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
|
|
* @package Compress
|
|
*/
|
|
|
|
/**
|
|
* This class allows rar files to be read.
|
|
*
|
|
* @author Michael Cochrane <mike@graftonhall.co.nz>
|
|
* @author Michael Slusarz <slusarz@horde.org>
|
|
* @category Horde
|
|
* @copyright 2008-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
|
|
* @package Compress
|
|
*/
|
|
class Horde_Compress_Rar extends Horde_Compress_Base
|
|
{
|
|
const BLOCK_START = "\x52\x61\x72\x21\x1a\x07\x00";
|
|
|
|
/**
|
|
*/
|
|
public $canDecompress = true;
|
|
|
|
/**
|
|
* Rar compression methods
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_methods = array(
|
|
0x30 => 'Store',
|
|
0x31 => 'Fastest',
|
|
0x32 => 'Fast',
|
|
0x33 => 'Normal',
|
|
0x34 => 'Good',
|
|
0x35 => 'Best'
|
|
);
|
|
|
|
/**
|
|
* @return array Info on the compressed file:
|
|
* <pre>
|
|
* KEY: Position in RAR archive
|
|
* VALUES:
|
|
* attr - File attributes
|
|
* date - File modification time
|
|
* csize - Compressed file size
|
|
* method - Compression method
|
|
* name - Filename
|
|
* size - Original file size
|
|
* </pre>
|
|
*
|
|
* @throws Horde_Compress_Exception
|
|
*/
|
|
public function decompress($data, array $params = array())
|
|
{
|
|
$blockStart = strpos($data, self::BLOCK_START);
|
|
if ($blockStart === false) {
|
|
throw new Horde_Compress_Exception(Horde_Compress_Translation::t("Invalid RAR data."));
|
|
}
|
|
|
|
$data_len = strlen($data);
|
|
$position = $blockStart + 7;
|
|
$return_array = array();
|
|
|
|
while ($position < $data_len) {
|
|
if ($position + 7 > $data_len) {
|
|
throw new Horde_Compress_Exception(Horde_Compress_Translation::t("Invalid RAR data."));
|
|
}
|
|
//$head_crc = substr($data, $position, 2);
|
|
$head_type = ord(substr($data, $position + 2, 1));
|
|
$head_flags = unpack('vFlags', substr($data, $position + 3, 2));
|
|
$head_flags = $head_flags['Flags'];
|
|
$head_size = unpack('vSize', substr($data, $position + 5, 2));
|
|
$head_size = $head_size['Size'];
|
|
|
|
$position += 7;
|
|
$head_size -= 7;
|
|
|
|
switch ($head_type) {
|
|
case 0x73:
|
|
/* Archive header */
|
|
$position += $head_size;
|
|
break;
|
|
|
|
case 0x74:
|
|
/* File Header */
|
|
$info = unpack(
|
|
'VPacked/VUnpacked/COS/VCRC32/VTime/CVersion/CMethod/vLength/vAttrib',
|
|
substr($data, $position)
|
|
);
|
|
$year = (($info['Time'] >> 25) & 0x7f) + 80;
|
|
$name = substr($data, $position + 25, $info['Length']);
|
|
if ($unicode = strpos($name, "\0")) {
|
|
$name = substr($name, 0, $unicode);
|
|
}
|
|
|
|
$return_array[] = array(
|
|
'name' => $name,
|
|
'size' => $info['Unpacked'],
|
|
'csize' => $info['Packed'],
|
|
'date' => mktime(
|
|
(($info['Time'] >> 11) & 0x1f),
|
|
(($info['Time'] >> 5) & 0x3f),
|
|
(($info['Time'] << 1) & 0x3e),
|
|
(($info['Time'] >> 21) & 0x07),
|
|
(($info['Time'] >> 16) & 0x1f),
|
|
$year < 1900 ? $year + 1900 : $year
|
|
),
|
|
'method' => $this->_methods[$info['Method']],
|
|
'attr' => (($info['Attrib'] & 0x10) ? 'D' : '-') .
|
|
(($info['Attrib'] & 0x20) ? 'A' : '-') .
|
|
(($info['Attrib'] & 0x03) ? 'S' : '-') .
|
|
(($info['Attrib'] & 0x02) ? 'H' : '-') .
|
|
(($info['Attrib'] & 0x01) ? 'R' : '-')
|
|
);
|
|
|
|
$position += $head_size + $info['Packed'];
|
|
break;
|
|
|
|
default:
|
|
if ($head_size == -7) {
|
|
/* We've already added 7 bytes above. If we remove those
|
|
* same 7 bytes, we will enter an infinite loop. */
|
|
throw new Horde_Compress_Exception(Horde_Compress_Translation::t("Invalid RAR data."));
|
|
}
|
|
$position += $head_size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $return_array;
|
|
}
|
|
}
|