From: Michael M Slusarz Date: Mon, 13 Jul 2009 21:11:07 +0000 (-0600) Subject: Import Horde_Compress:: from CVS HEAD X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=1dc1114a54dbb8656bd7a106a0578e9d92fd3a71;p=horde.git Import Horde_Compress:: from CVS HEAD --- diff --git a/framework/Compress/lib/Horde/Compress.php b/framework/Compress/lib/Horde/Compress.php new file mode 100644 index 000000000..e7328e8d0 --- /dev/null +++ b/framework/Compress/lib/Horde/Compress.php @@ -0,0 +1,76 @@ + + * @package Horde_Compress + */ +class Horde_Compress +{ + /** + * Attempts to return a concrete instance based on $driver. + * + * @param mixed $driver The type of concrete subclass to + * return. If $driver is an array, then we will look + * in $driver[0]/lib/Compress/ for the subclass + * implementation named $driver[1].php. + * @param array $params A hash containing any additional configuration or + * parameters a subclass might need. + * + * @return Horde_Compress The newly created concrete instance. + * @throws Horde_Exception + */ + static public function factory($driver, $params = null) + { + if (is_array($driver)) { + list($app, $driv_name) = $driver; + $driver = basename($driv_name); + } else { + $driver = basename($driver); + } + + $class = (empty($app) ? 'Horde' : $app) . '_Auth_' . ucfirst($driver); + + if (class_exists($class)) { + return new $class($params); + } + + throw new Horde_Exception('Class definition of ' . $class . ' not found.'); + } + + /** + * Compress the data. + * + * @param string $data The data to compress. + * @param array $params An array of arguments needed to compress the data. + * + * @return mixed The compressed data. + * @throws Horde_Exception + */ + public function compress($data, $params = array()) + { + return $data; + } + + /** + * Decompress the data. + * + * @param string $data The data to decompress. + * @param array $params An array of arguments needed to decompress the + * data. + * + * @return array The decompressed data. + * @throws Horde_Exception + */ + public function decompress($data, $params = array()) + { + return $data; + } + +} diff --git a/framework/Compress/lib/Horde/Compress/Dbx.php b/framework/Compress/lib/Horde/Compress/Dbx.php new file mode 100644 index 000000000..39c6fc03d --- /dev/null +++ b/framework/Compress/lib/Horde/Compress/Dbx.php @@ -0,0 +1,271 @@ + + * http://uruds.gateway.bg/zeos/ + * + * Copyright 2003-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @author Jan Schneider + * @package Horde_Compress + */ +class Horde_Compress_Dbx extends Horde_Compress +{ + /** + * TODO + * + * @var array + */ + protected $_flagArray = array( + 0x1 => 'MsgFlags', + 0x2 => 'Sent', + 0x4 => 'position', + 0x7 => 'MessageID', + 0x8 => 'Subject', + 0x9 => 'From_reply', + 0xA => 'References', + 0xB => 'Newsgroup', + 0xD => 'From', + 0xE => 'Reply_To', + 0x12 => 'Received', + 0x13 => 'Receipt', + 0x1A => 'Account', + 0x1B => 'AccountID', + 0x80 => 'Msg', + 0x81 => 'MsgFlags', + 0x84 => 'position', + 0x91 => 'size', + ); + + /** + * TODO + * + * @var array + */ + protected $_mails = array(); + + /** + * TODO + * + * @var array + */ + protected $_tmp = array(); + + /** + * Decompresses a DBX file and gets information from it. + * + * @param string $data The dbx file data. + * @param array $params Not used. + * + * @return mixed The requested data. + * @throws Horde_Exception + */ + public function decompress($data, $params = null) + { + $this->_mails = $this->_tmp = array(); + + $position = 0xC4; + $header_info = unpack('Lposition/LDataLength/nHeaderLength/nFlagCount', substr($data, $position, 12)); + $position += 12; + + // Go to the first table offest and process it. + if ($header_info['position'] > 0) { + $position = 0x30; + $buf = unpack('Lposition', substr($data, $position, 4)); + $position = $buf['position']; + $result = $this->_readIndex($data, $position); + } + + return $this->_mails; + } + + /** + * Returns a null-terminated string from the specified data. + * + * @param string $buf TODO + * @param integer $pos TODO + * + * @return string TODO + */ + protected function _readString($buf, $pos) + { + return ($len = strpos(substr($buf, $pos), chr(0))) + ? substr($buf, $pos, $len) + : ''; + } + + /** + * TODO + * + * @param string $data TODO + * @param integer $position TODO + * + * @return string TODO + * @throws Horde_Exception + */ + protected function _readMessage($data, $position) + { + $msg = ''; + $part = 0; + + if ($position > 0) { + $IndexItemsCount = array_pop(unpack('S', substr($data, 0xC4, 4))); + if ($IndexItemsCount > 0) { + while ($position < strlen($data)) { + $part++; + $s = substr($data, $position, 528); + if (strlen($s) == 0) { + break; + } + $msg_item = unpack('LFilePos/LUnknown/LItemSize/LNextItem/a512Content', $s); + if ($msg_item['FilePos'] != $position) { + throw new Horde_Exception(_("Invalid file format")); + } + $position += 528; + $msg .= substr($msg_item['Content'], 0, $msg_item['ItemSize']); + $position = $msg_item['NextItem']; + if ($position == 0) { + break; + } + } + } + } + + return $msg; + } + + /** + * TODO + * + * @param string $data TODO + * @param integer $position TODO + * + * @return array TODO + * @throws Horde_Exception + */ + protected function _readMessageInfo($data, $position) + { + $message_info = array(); + $msg_header = unpack('Lposition/LDataLength/SHeaderLength/SFlagCount', substr($data, $position, 12)); + if ($msg_header['position'] != $position) { + throw new Horde_Exception(_("Invalid file format")); + } + $position += 12; + $message_info['HeaderPosition'] = $msg_header['position']; + $flags = $msg_header['FlagCount'] & 0xFF; + $DataSize = $msg_header['DataLength'] - $flags * 4; + $size = 4 * $flags; + $FlagsBuffer = substr($data, $position, $size); + $position += $size; + $size = $DataSize; + $DataBuffer = substr($data, $position, $size); + $position += $size; + $message_info = array(); + + /* Process flags */ + for ($i = 0; $i < $flags; ++$i) { + $pos = 0; + $f = array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))); + + $mask = $f & 0xFF; + switch ($mask) { + case 0x1: + $pos = $pos + ($f >> 8); + $message_info['MsgFlags'] = array_pop(unpack('C', substr($DataBuffer, $pos++, 1))); + $message_info['MsgFlags'] += array_pop(unpack('C', substr($DataBuffer, $pos++, 1))) * 256; + $message_info['MsgFlags'] += array_pop(unpack('C', substr($DataBuffer, $pos, 1))) * 65536; + break; + + case 0x2: + case 0x4: + $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8; + $message_info[$this->_flagArray[$mask]] = array_pop(unpack('L', substr($DataBuffer, $pos, 4))); + break; + + case 0x7: + case 0x8: + case 0x9: + case 0xA: + case 0xB: + case 0xD: + case 0xE: + case 0x13: + case 0x1A: + $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8; + $message_info[$this->_flagArray[$mask]] = $this->_readString($DataBuffer, $pos); + break; + + case 0x12: + $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8; + $message_info['Received'] = array_pop(unpack('L', substr($DataBuffer, $pos, 4))); + break; + + case 0x1B: + $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8; + $message_info['AccountID'] = intval($this->_readString($DataBuffer, $pos)); + break; + + case 0x80: + case 0x81: + case 0x84: + case 0x91: + $message_info[$this->_flagArray[$mask]] = array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8; + break; + } + } + + return $message_info; + } + + /** + * TODO + * + * @param string $data TODO + * @param integer $position TODO + * + * @throws Horde_Exception + */ + protected function _readIndex($data, $position) + { + $index_header = unpack('LFilePos/LUnknown1/LPrevIndex/LNextIndex/LCount/LUnknown', substr($data, $position, 24)); + if ($index_header['FilePos'] != $position) { + throw new Horde_Exception(_("Invalid file format")); + } + + // Push it into list of processed items. + $this->_tmp[$position] = true; + if (($index_header['NextIndex'] > 0) && + empty($this->_tmp[$index_header['NextIndex']])) { + $this->_readIndex($data, $index_header['NextIndex']); + } + if (($index_header['PrevIndex'] > 0) && + empty($this->_tmp[$index_header['PrevIndex']])) { + $this->_readIndex($data, $index_header['PrevIndex']); + } + $position += 24; + $icount = $index_header['Count'] >> 8; + if ($icount > 0) { + $buf = substr($data, $position, 12 * $icount); + for ($i = 0; $i < $icount; $i++) { + $hdr_buf = substr($buf, $i * 12, 12); + $IndexItem = unpack('LHeaderPos/LChildIndex/LUnknown', $hdr_buf); + if ($IndexItem['HeaderPos'] > 0) { + $mail['info'] = $this->_readMessageInfo($data, $IndexItem['HeaderPos']); + $mail['content'] = $this->_readMessage($data, $mail['info']['position']); + $this->_mails[] = $mail; + } + if (($IndexItem['ChildIndex'] > 0) && + empty($this->_tmp[$IndexItem['ChildIndex']])) { + $this->_readIndex($fp, $IndexItem['ChildIndex']); + } + } + } + } + +} diff --git a/framework/Compress/lib/Horde/Compress/Gzip.php b/framework/Compress/lib/Horde/Compress/Gzip.php new file mode 100644 index 000000000..39ffa0c04 --- /dev/null +++ b/framework/Compress/lib/Horde/Compress/Gzip.php @@ -0,0 +1,85 @@ + + * @author Michael Slusarz + * @package Horde_Compress + */ +class Horde_Compress_Gzip extends Horde_Compress +{ + /** + * Gzip file flags. + * + * @var array + */ + protected $_flags = array( + 'FTEXT' => 0x01, + 'FHCRC' => 0x02, + 'FEXTRA' => 0x04, + 'FNAME' => 0x08, + 'FCOMMENT' => 0x10 + ); + + /** + * Decompress a gzip file and get information from it. + * + * @param string $data The tar file data. + * @param array $params The parameter array (Unused). + * + * @return string The uncompressed data. + * @throws Horde_Exception + */ + public function decompress($data, $params = array()) + { + /* If gzip is not compiled into PHP, return now. */ + if (!Horde_Util::extensionExists('zlib')) { + throw new Horde_Exception(_("This server can't uncompress gzip files.")); + } + + /* Gzipped File - decompress it first. */ + $position = 0; + $info = @unpack('CCM/CFLG/VTime/CXFL/COS', substr($data, $position + 2)); + if (!$info) { + throw new Horde_Exception(_("Unable to decompress data.")); + } + $position += 10; + + if ($info['FLG'] & $this->_flags['FEXTRA']) { + $XLEN = unpack('vLength', substr($data, $position + 0, 2)); + $XLEN = $XLEN['Length']; + $position += $XLEN + 2; + } + + if ($info['FLG'] & $this->_flags['FNAME']) { + $filenamePos = strpos($data, "\x0", $position); + $filename = substr($data, $position, $filenamePos - $position); + $position = $filenamePos + 1; + } + + if ($info['FLG'] & $this->_flags['FCOMMENT']) { + $commentPos = strpos($data, "\x0", $position); + $comment = substr($data, $position, $commentPos - $position); + $position = $commentPos + 1; + } + + if ($info['FLG'] & $this->_flags['FHCRC']) { + $hcrc = unpack('vCRC', substr($data, $position + 0, 2)); + $hcrc = $hcrc['CRC']; + $position += 2; + } + + $result = @gzinflate(substr($data, $position, strlen($data) - $position)); + if (empty($result)) { + throw new Horde_Exception(_("Unable to decompress data.")); + } + + return $result; + } + +} diff --git a/framework/Compress/lib/Horde/Compress/Rar.php b/framework/Compress/lib/Horde/Compress/Rar.php new file mode 100644 index 000000000..6e77d9646 --- /dev/null +++ b/framework/Compress/lib/Horde/Compress/Rar.php @@ -0,0 +1,113 @@ + + * @author Michael Slusarz + * @package Horde_Compress + */ +class Horde_Compress_Rar extends Horde_Compress +{ + /** + * Rar compression methods + * + * @var array + */ + protected $_methods = array( + 0x30 => 'Store', + 0x31 => 'Fastest', + 0x32 => 'Fast', + 0x33 => 'Normal', + 0x34 => 'Good', + 0x35 => 'Best' + ); + + /** + * Decompress a rar file and get information from it. + * + * @param string $data The rar file data. + * @param array $params The parameter array (Unused). + * + * @return array The uncompressed data: + *
+     * 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
+     * 
+ * + * @throws Horde_Exception + */ + public function decompress($data, $params = array()) + { + $blockStart = strpos($data, "\x52\x61\x72\x21\x1a\x07\x00"); + if ($blockStart === false) { + throw new Horde_Exception(_("Invalid RAR data.")); + } + + $position = $blockStart + 7; + $return_array = array(); + + while ($position < strlen($data)) { + $head_crc = substr($data, $position + 0, 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)); + + $return_array[] = array( + 'name' => substr($data, $position + 25, $info['Length']), + '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), + ((($info['Time'] >> 25) & 0x7f) + 80)), + '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: + $position += $head_size; + if (isset($add_size)) { + $position += $add_size; + } + break; + } + } + + return $return_array; + } +} diff --git a/framework/Compress/lib/Horde/Compress/Tar.php b/framework/Compress/lib/Horde/Compress/Tar.php new file mode 100644 index 000000000..60c9beaf9 --- /dev/null +++ b/framework/Compress/lib/Horde/Compress/Tar.php @@ -0,0 +1,119 @@ + + * @author Michael Slusarz + * @package Horde_Compress + */ +class Horde_Compress_Tar extends Horde_Compress +{ + /** + * Tar file types. + * + * @var array + */ + protected $_types = array( + 0x0 => 'Unix file', + 0x30 => 'File', + 0x31 => 'Link', + 0x32 => 'Symbolic link', + 0x33 => 'Character special file', + 0x34 => 'Block special file', + 0x35 => 'Directory', + 0x36 => 'FIFO special file', + 0x37 => 'Contiguous file' + ); + + /** + * Tar file flags. + * + * @var array + */ + protected $_flags = array( + 'FTEXT' => 0x01, + 'FHCRC' => 0x02, + 'FEXTRA' => 0x04, + 'FNAME' => 0x08, + 'FCOMMENT' => 0x10 + ); + + /** + * Decompress a tar file and get information from it. + * + * @param string $data The tar file data. + * @param array $params The parameter array (Unused). + * + * @return array The requested data. + *
+     * KEY: Position in the array
+     * VALUES: 'attr'  --  File attributes
+     *         'data'  --  Raw file contents
+     *         'date'  --  File modification time
+     *         'name'  --  Filename
+     *         'size'  --  Original file size
+     *         'type'  --  File type
+     * 
+ * + * @throws Horde_Exception + */ + public function decompress($data, $params = array()) + { + $position = 0; + $return_array = array(); + + while ($position < strlen($data)) { + $info = @unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/Ctypeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", substr($data, $position)); + if (!$info) { + throw new Horde_Exception(_("Unable to decompress data.")); + } + + $position += 512; + $contents = substr($data, $position, octdec($info['size'])); + $position += ceil(octdec($info['size']) / 512) * 512; + + if ($info['filename']) { + $file = array( + 'attr' => null, + 'data' => null, + 'date' => octdec($info['mtime']), + 'name' => trim($info['filename']), + 'size' => octdec($info['size']), + 'type' => isset($this->_types[$info['typeflag']]) ? $this->_types[$info['typeflag']] : null + ); + + if (($info['typeflag'] == 0) || + ($info['typeflag'] == 0x30) || + ($info['typeflag'] == 0x35)) { + /* File or folder. */ + $file['data'] = $contents; + + $mode = hexdec(substr($info['mode'], 4, 3)); + $file['attr'] = + (($info['typeflag'] == 0x35) ? 'd' : '-') . + (($mode & 0x400) ? 'r' : '-') . + (($mode & 0x200) ? 'w' : '-') . + (($mode & 0x100) ? 'x' : '-') . + (($mode & 0x040) ? 'r' : '-') . + (($mode & 0x020) ? 'w' : '-') . + (($mode & 0x010) ? 'x' : '-') . + (($mode & 0x004) ? 'r' : '-') . + (($mode & 0x002) ? 'w' : '-') . + (($mode & 0x001) ? 'x' : '-'); + } else { + /* Some other type. */ + } + + $return_array[] = $file; + } + } + + return $return_array; + } + +} diff --git a/framework/Compress/lib/Horde/Compress/Tnef.php b/framework/Compress/lib/Horde/Compress/Tnef.php new file mode 100644 index 000000000..1ebe1fac2 --- /dev/null +++ b/framework/Compress/lib/Horde/Compress/Tnef.php @@ -0,0 +1,336 @@ + + * Original design by: + * Thomas Boll , Mark Simpson + * + * Copyright 2002-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @author Jan Schneider + * @author Michael Slusarz + * @package Horde_Compress + */ +class Horde_Compress_Tnef extends Horde_Compress +{ + const SIGNATURE = 0x223e9f78; + const LVL_MESSAGE = 0x01; + const LVL_ATTACHMENT = 0x02; + + const STRING = 0x00010000; + const BYTE = 0x00060000; + const WORD = 0x00070000; + const DWORD = 0x00080000; + + const ASUBJECT = self::DWORD | 0x8004; + const AMCLASS = self::WORD | 0x8008; + const ATTACHDATA = self::BYTE | 0x800f; + const AFILENAME = self::STRING | 0x8010; + const ARENDDATA = self::BYTE | 0x9002; + const AMAPIATTRS = self::BYTE | 0x9005; + const AVERSION = self::DWORD | 0x9006; + + const MAPI_NULL = 0x0001; + const MAPI_SHORT = 0x0002; + const MAPI_INT = 0x0003; + const MAPI_FLOAT = 0x0004; + const MAPI_DOUBLE = 0x0005; + const MAPI_CURRENCY = 0x0006; + const MAPI_APPTIME = 0x0007; + const MAPI_ERROR = 0x000a; + const MAPI_BOOLEAN = 0x000b; + const MAPI_OBJECT = 0x000d; + const MAPI_INT8BYTE = 0x0014; + const MAPI_STRING = 0x001e; + const MAPI_UNICODE_STRING = 0x001f; + const MAPI_SYSTIME = 0x0040; + const MAPI_CLSID = 0x0048; + const MAPI_BINARY = 0x0102; + + const MAPI_ATTACH_LONG_FILENAME = 0x3707; + const MAPI_ATTACH_MIME_TAG = 0x370E; + + const MAPI_NAMED_TYPE_ID = 0x0000; + const MAPI_NAMED_TYPE_STRING = 0x0001; + const MAPI_MV_FLAG = 0x1000; + + /** + * Decompress the data. + * + * @param string $data The data to decompress. + * @param array $params An array of arguments needed to decompress the + * data. + * + * @return mixed The decompressed data. + * @throws Horde_Exception + */ + public function decompress($data, $params = array()) + { + $out = array(); + + if ($this->_geti($data, 32) == self::SIGNATURE) { + $this->_geti($data, 16); + + while (strlen($data) > 0) { + switch ($this->_geti($data, 8)) { + case self::LVL_MESSAGE: + $this->_decodeMessage($data); + break; + + case self::LVL_ATTACHMENT: + $this->_decodeAttachment($data, $out); + break; + } + } + } + + return array_reverse($out); + } + + /** + * TODO + * + * @param string &$data The data string. + * @param integer $bits How many bits to retrieve. + * + * @return TODO + */ + protected function _getx(&$data, $bits) + { + $value = null; + + if (strlen($data) >= $bits) { + $value = substr($data, 0, $bits); + $data = substr_replace($data, '', 0, $bits); + } + + return $value; + } + + /** + * TODO + * + * @param string &$data The data string. + * @param integer $bits How many bits to retrieve. + * + * @return TODO + */ + protected function _geti(&$data, $bits) + { + $bytes = $bits / 8; + $value = null; + + if (strlen($data) >= $bytes) { + $value = ord($data[0]); + if ($bytes >= 2) { + $value += (ord($data[1]) << 8); + } + if ($bytes >= 4) { + $value += (ord($data[2]) << 16) + (ord($data[3]) << 24); + } + $data = substr_replace($data, '', 0, $bytes); + } + + return $value; + } + + /** + * TODO + * + * @param string &$data The data string. + * @param string $attribute TODO + */ + protected function _decodeAttribute(&$data, $attribute) + { + /* Data. */ + $this->_getx($data, $this->_geti($data, 32)); + + /* Checksum. */ + $this->_geti($data, 16); + } + + /** + * TODO + * + * @param string $data The data string. + * @param array &$attachment_data TODO + */ + protected function _extractMapiAttributes($data, &$attachment_data) + { + /* Number of attributes. */ + $number = $this->_geti($data, 32); + + while ((strlen($data) > 0) && $number--) { + $have_mval = false; + $num_mval = 1; + $named_id = $value = null; + $attr_type = $this->_geti($data, 16); + $attr_name = $this->_geti($data, 16); + + if (($attr_type & self::MAPI_MV_FLAG) != 0) { + $have_mval = true; + $attr_type = $attr_type & ~self::MAPI_MV_FLAG; + } + + if (($attr_name >= 0x8000) && ($attr_name < 0xFFFE)) { + $this->_getx($data, 16); + $named_type = $this->_geti($data, 32); + + switch ($named_type) { + case self::MAPI_NAMED_TYPE_ID: + $named_id = $this->_geti($data, 32); + $attr_name = $named_id; + break; + + case self::MAPI_NAMED_TYPE_STRING: + $attr_name = 0x9999; + $idlen = $this->_geti($data, 32); + $datalen = $idlen + ((4 - ($idlen % 4)) % 4); + $named_id = substr($this->_getx($data, $datalen), 0, $idlen); + break; + } + } + + if ($have_mval) { + $num_mval = $this->_geti($data, 32); + } + + switch ($attr_type) { + case self::MAPI_SHORT: + $value = $this->_geti($data, 16); + break; + + case self::MAPI_INT: + case self::MAPI_BOOLEAN: + for ($i = 0; $i < $num_mval; $i++) { + $value = $this->_geti($data, 32); + } + break; + + case self::MAPI_FLOAT: + case self::MAPI_ERROR: + $value = $this->_getx($data, 4); + break; + + case self::MAPI_DOUBLE: + case self::MAPI_APPTIME: + case self::MAPI_CURRENCY: + case self::MAPI_INT8BYTE: + case self::MAPI_SYSTIME: + $value = $this->_getx($data, 8); + break; + + case self::MAPI_STRING: + case self::MAPI_UNICODE_STRING: + case self::MAPI_BINARY: + case self::MAPI_OBJECT: + $num_vals = ($have_mval) ? $num_mval : $this->_geti($data, 32); + for ($i = 0; $i < $num_vals; $i++) { + $length = $this->_geti($data, 32); + + /* Pad to next 4 byte boundary. */ + $datalen = $length + ((4 - ($length % 4)) % 4); + + if ($attr_type == self::MAPI_STRING) { + $length -= 1; + } + + /* Read and truncate to length. */ + $value = substr($this->_getx($data, $datalen), 0, $length); + } + break; + } + + /* Store any interesting attributes. */ + switch ($attr_name) { + case self::MAPI_ATTACH_LONG_FILENAME: + /* Used in preference to AFILENAME value. */ + $attachment_data[0]['name'] = preg_replace('/.*[\/](.*)$/', '\1', $value); + $attachment_data[0]['name'] = str_replace("\0", '', $attachment_data[0]['name']); + break; + + case self::MAPI_ATTACH_MIME_TAG: + /* Is this ever set, and what is format? */ + $attachment_data[0]['type'] = preg_replace('/^(.*)\/.*/', '\1', $value); + $attachment_data[0]['subtype'] = preg_replace('/.*\/(.*)$/', '\1', $value); + $attachment_data[0]['subtype'] = str_replace("\0", '', $attachment_data[0]['subtype']); + break; + } + } + } + + /** + * TODO + * + * @param string &$data The data string. + */ + protected function _decodeMessage(&$data) + { + $this->_decodeAttribute($data, $this->_geti($data, 32)); + } + + /** + * TODO + * + * @param string &$data The data string. + * @param array &$attachment_data TODO + */ + protected function _decodeAttachment(&$data, &$attachment_data) + { + $attribute = $this->_geti($data, 32); + + switch ($attribute) { + case self::ARENDDATA: + /* Marks start of new attachment. */ + $this->_getx($data, $this->_geti($data, 32)); + + /* Checksum */ + $this->_geti($data, 16); + + /* Add a new default data block to hold details of this + attachment. Reverse order is easier to handle later! */ + array_unshift($attachment_data, array('type' => 'application', + 'subtype' => 'octet-stream', + 'name' => 'unknown', + 'stream' => '')); + break; + + case self::AFILENAME: + /* Strip path. */ + $attachment_data[0]['name'] = preg_replace('/.*[\/](.*)$/', '\1', $this->_getx($data, $this->_geti($data, 32))); + $attachment_data[0]['name'] = str_replace("\0", '', $attachment_data[0]['name']); + + /* Checksum */ + $this->_geti($data, 16); + break; + + case self::ATTACHDATA: + /* The attachment itself. */ + $length = $this->_geti($data, 32); + $attachment_data[0]['size'] = $length; + $attachment_data[0]['stream'] = $this->_getx($data, $length); + + /* Checksum */ + $this->_geti($data, 16); + break; + + case self::AMAPIATTRS: + $length = $this->_geti($data, 32); + $value = $this->_getx($data, $length); + + /* Checksum */ + $this->_geti($data, 16); + $this->_extractMapiAttributes($value, $attachment_data); + break; + + default: + $this->_decodeAttribute($data, $attribute); + } + } + +} diff --git a/framework/Compress/lib/Horde/Compress/Zip.php b/framework/Compress/lib/Horde/Compress/Zip.php new file mode 100644 index 000000000..bcc8ae755 --- /dev/null +++ b/framework/Compress/lib/Horde/Compress/Zip.php @@ -0,0 +1,359 @@ + + * http://www.zend.com/codex.php?id=535&single=1 + * + * Deins125 + * http://www.zend.com/codex.php?id=470&single=1 + * + * The ZIP compression date code is partially based on code from + * Peter Listiak + * + * Copyright 2000-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @author Chuck Hagenbuch + * @author Michael Cochrane + * @author Michael Slusarz + * @package Horde_Compress + */ +class Horde_Compress_Zip extends Horde_Compress +{ + /* Constants used with decompress(). */ + const ZIP_LIST = 1; + const ZIP_DATA = 2; + + /* Beginning of central directory record. */ + const CTRL_DIR_HEADER = "\x50\x4b\x01\x02"; + + /* End of central directory record. */ + const CTRL_DIR_END = "\x50\x4b\x05\x06\x00\x00\x00\x00"; + + /* Beginning of file contents. */ + const FILE_HEADER = "\x50\x4b\x03\x04"; + + /** + * ZIP compression methods. + * + * @var array + */ + protected $_methods = array( + 0x0 => 'None', + 0x1 => 'Shrunk', + 0x2 => 'Super Fast', + 0x3 => 'Fast', + 0x4 => 'Normal', + 0x5 => 'Maximum', + 0x6 => 'Imploded', + 0x8 => 'Deflated' + ); + + /** + * Create a ZIP compressed file from an array of file data. + * + * @param array $data The data to compress. + *
+     * Requires an array of arrays - each subarray should contain the
+     * following fields:
+     * 'data' - (string) The data to compress.
+     * 'name' - (string) The pathname to the file.
+     * 'time' - (integer) [optional] The timestamp to use for the file.
+     * 
+ * @param array $params The parameter array (unused). + * + * @return string The ZIP file. + */ + public function compress($data, $params = array()) + { + $contents = ''; + $ctrldir = array(); + + reset($data); + while (list(, $val) = each($data)) { + $ctrldir = $this->_addToZipFile($val, $contents, $ctrldir); + } + + return $this->_createZIPFile($contents, $ctrldir); + } + + /** + * Decompress a ZIP file and get information from it. + * + * @param string $data The zipfile data. + * @param array $params The parameter array. + *
+     * The following parameters are REQUIRED:
+     * 'action' - (integer) The action to take on the data.  Either
+     *                      self::ZIP_LIST or self::ZIP_DATA.
+     *
+     * The following parameters are REQUIRED for self::ZIP_DATA also:
+     * 'info' - (array) The zipfile list.
+     * 'key' - (integer) The position of the file in the archive list.
+     * 
+ * + * @return mixed The requested data. + * @throws Horde_Exception + */ + public function decompress($data, $params) + { + if (isset($params['action'])) { + switch ($params['action']) { + case self::ZIP_LIST: + return $this->_getZipInfo($data); + + case self::ZIP_DATA: + return $this->_getZipData($data, $params['info'], $params['key']); + } + } + } + + /** + * Get the list of files/data from the zip archive. + * + * @param string $data The zipfile data. + * + * @return array KEY: Position in zipfile + * VALUES: + *
+     * 'attr'    --  File attributes
+     * 'crc'     --  CRC checksum
+     * 'csize'   --  Compressed file size
+     * 'date'    --  File modification time
+     * 'name'    --  Filename
+     * 'method'  --  Compression method
+     * 'size'    --  Original file size
+     * 'type'    --  File type
+     * 
+ * + * @throws Horde_Exception + */ + protected function _getZipInfo($data) + { + $entries = array(); + + /* Get details from Central directory structure. */ + $fhStart = strpos($data, self::CTRL_DIR_HEADER); + + do { + if (strlen($data) < $fhStart + 31) { + throw new Horde_Exception(_("Invalid ZIP data")); + } + $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart + 10, 20)); + $name = substr($data, $fhStart + 46, $info['Length']); + + $entries[$name] = array( + 'attr' => null, + 'crc' => sprintf("%08s", dechex($info['CRC32'])), + 'csize' => $info['Compressed'], + 'date' => null, + '_dataStart' => null, + 'name' => $name, + 'method' => $this->_methods[$info['Method']], + '_method' => $info['Method'], + 'size' => $info['Uncompressed'], + 'type' => null + ); + + $entries[$name]['date'] = + mktime((($info['Time'] >> 11) & 0x1f), + (($info['Time'] >> 5) & 0x3f), + (($info['Time'] << 1) & 0x3e), + (($info['Time'] >> 21) & 0x07), + (($info['Time'] >> 16) & 0x1f), + ((($info['Time'] >> 25) & 0x7f) + 1980)); + + if (strlen($data) < $fhStart + 43) { + throw new Horde_Exception(_("Invalid ZIP data")); + } + $info = unpack('vInternal/VExternal', substr($data, $fhStart + 36, 6)); + + $entries[$name]['type'] = ($info['Internal'] & 0x01) ? 'text' : 'binary'; + $entries[$name]['attr'] = + (($info['External'] & 0x10) ? 'D' : '-') . + (($info['External'] & 0x20) ? 'A' : '-') . + (($info['External'] & 0x03) ? 'S' : '-') . + (($info['External'] & 0x02) ? 'H' : '-') . + (($info['External'] & 0x01) ? 'R' : '-'); + } while (($fhStart = strpos($data, self::CTRL_DIR_HEADER, $fhStart + 46)) !== false); + + /* Get details from local file header. */ + $fhStart = strpos($data, self::FILE_HEADER); + + do { + if (strlen($data) < $fhStart + 34) { + throw new Horde_Exception(_("Invalid ZIP data")); + } + $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $fhStart + 8, 25)); + $name = substr($data, $fhStart + 30, $info['Length']); + $entries[$name]['_dataStart'] = $fhStart + 30 + $info['Length'] + $info['ExtraLength']; + } while (strlen($data) > $fhStart + 30 + $info['Length'] && + ($fhStart = strpos($data, self::FILE_HEADER, $fhStart + 30 + $info['Length'])) !== false); + + return array_values($entries); + } + + /** + * Returns the data for a specific archived file. + * + * @param string $data The zip archive contents. + * @param array $info The information array from _getZipInfo(). + * @param integer $key The position of the file in the archive. + * + * @return string The file data. + */ + protected function _getZipData($data, $info, $key) + { + if (($info[$key]['_method'] == 0x8) && + Horde_Util::extensionExists('zlib')) { + /* If the file has been deflated, and zlib is installed, + then inflate the data again. */ + return @gzinflate(substr($data, $info[$key]['_dataStart'], $info[$key]['csize'])); + } elseif ($info[$key]['_method'] == 0x0) { + /* Files that aren't compressed. */ + return substr($data, $info[$key]['_dataStart'], $info[$key]['csize']); + } + + return ''; + } + + /** + * Checks to see if the data is a valid ZIP file. + * + * @param string $data The ZIP file data. + * + * @return boolean True if valid, false if invalid. + */ + public function checkZipData($data) + { + return (strpos($data, self::FILE_HEADER) !== false); + } + + /** + * Converts a UNIX timestamp to a 4-byte DOS date and time format + * (date in high 2-bytes, time in low 2-bytes allowing magnitude + * comparison). + * + * @param integer $unixtime The current UNIX timestamp. + * + * @return integer The current date in a 4-byte DOS format. + */ + protected function _unix2DOSTime($unixtime = null) + { + $timearray = (is_null($unixtime)) ? getdate() : getdate($unixtime); + + if ($timearray['year'] < 1980) { + $timearray['year'] = 1980; + $timearray['mon'] = 1; + $timearray['mday'] = 1; + $timearray['hours'] = 0; + $timearray['minutes'] = 0; + $timearray['seconds'] = 0; + } + + return (($timearray['year'] - 1980) << 25) | + ($timearray['mon'] << 21) | + ($timearray['mday'] << 16) | + ($timearray['hours'] << 11) | + ($timearray['minutes'] << 5) | + ($timearray['seconds'] >> 1); + } + + /** + * Adds a "file" to the ZIP archive. + * + * @param array $file See self::createZipFile(). + * @param string $contents The zip data. + * @param array $ctrldir An array of central directory information. + * + * @return array The updated value of $ctrldir. + */ + protected function _addToZipFile($file, $contents, $ctrldir) + { + $data = $file['data']; + $name = str_replace('\\', '/', $file['name']); + + /* See if time/date information has been provided. */ + $ftime = (isset($file['time'])) ? $file['time'] : null; + + /* Get the hex time. */ + $dtime = dechex($this->_unix2DosTime($ftime)); + $hexdtime = chr(hexdec($dtime[6] . $dtime[7])) . + chr(hexdec($dtime[4] . $dtime[5])) . + chr(hexdec($dtime[2] . $dtime[3])) . + chr(hexdec($dtime[0] . $dtime[1])); + + /* "Local file header" segment. */ + $unc_len = strlen($data); + $crc = crc32($data); + $zdata = gzcompress($data); + $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); + $c_len = strlen($zdata); + $old_offset = strlen($contents); + + /* Common data for the two entries. */ + $common = + "\x14\x00" . /* Version needed to extract. */ + "\x00\x00" . /* General purpose bit flag. */ + "\x08\x00" . /* Compression method. */ + $hexdtime . /* Last modification time/date. */ + pack('V', $crc) . /* CRC 32 information. */ + pack('V', $c_len) . /* Compressed filesize. */ + pack('V', $unc_len) . /* Uncompressed filesize. */ + pack('v', strlen($name)) . /* Length of filename. */ + pack('v', 0); /* Extra field length. */ + + /* Add this entry to zip data. */ + $contents .= + self::FILE_HEADER . /* Begin creating the ZIP data. */ + $common . /* Common data. */ + $name . /* File name. */ + $zdata; /* "File data" segment. */ + + /* Add to central directory record. */ + $cdrec = self::CTRL_DIR_HEADER . + "\x00\x00" . /* Version made by. */ + $common . /* Common data. */ + pack('v', 0) . /* File comment length. */ + pack('v', 0) . /* Disk number start. */ + pack('v', 0) . /* Internal file attributes. */ + pack('V', 32) . /* External file attributes - + 'archive' bit set. */ + pack('V', $old_offset) . /* Relative offset of local + header. */ + $name; /* File name. */ + + // Save to central directory array. */ + $ctrldir[] = $cdrec; + + return $ctrldir; + } + + /** + * Creates the ZIP file. + * Official ZIP file format: http://www.pkware.com/appnote.txt + * + * @return string The ZIP file. + */ + protected function _createZIPFile($contents, $ctrlDir) + { + $dir = implode('', $ctrlDir); + + return $contents . $dir . self::CTRL_DIR_END . + /* Total # of entries "on this disk". */ + pack('v', count($ctrlDir)) . + /* Total # of entries overall. */ + pack('v', count($ctrlDir)) . + /* Size of central directory. */ + pack('V', strlen($dir)) . + /* Offset to start of central dir. */ + pack('V', strlen($contents)) . + /* ZIP file comment length. */ + "\x00\x00"; + } + +} diff --git a/framework/Compress/package.xml b/framework/Compress/package.xml new file mode 100644 index 000000000..d537f17cb --- /dev/null +++ b/framework/Compress/package.xml @@ -0,0 +1,127 @@ + + + Compress + pear.horde.org + Horde Compression API + This package provides an API for various compression techniques. + + + Chuck Hagenbuch + chuck + chuck@horde.org + yes + + + Jan Schneider + jan + jan@horde.org + yes + + + Michael Slusarz + slusarz + slusarz@horde.org + yes + + 2009-07-13 + + 0.1.0 + 0.1.0 + + + beta + beta + + LGPL + * Initial Horde 4 package. + + + + + + + + + + + + + + + + + + + + + + 5.2.0 + + + 1.5.4 + + + Exception + pear.horde.org + + + Util + pear.horde.org + + + + + gettext + + + zlib + + + + + + + + + + + + + + + + + 2006-05-08 + + + 0.0.2 + 0.0.2 + + + alpha + alpha + + LGPL + * Added rar driver. + * Converted to package.xml 2.0 for pear.horde.org + + + + + 0.0.1 + 0.0.1 + + + alpha + alpha + + 2003-07-03 + LGPL + Initial release as a PEAR package + + + + diff --git a/framework/Mime/lib/Horde/Mime/Viewer/Ooo.php b/framework/Mime/lib/Horde/Mime/Viewer/Ooo.php index dbe39a9e1..fb9a4ba61 100644 --- a/framework/Mime/lib/Horde/Mime/Viewer/Ooo.php +++ b/framework/Mime/lib/Horde/Mime/Viewer/Ooo.php @@ -49,7 +49,7 @@ class Horde_Mime_Viewer_Ooo extends Horde_Mime_Viewer_Driver 'table:number-columns-spanned=' => 'colspan=' ); - $zip = Horde_Compress::singleton('zip'); + $zip = Horde_Compress::factory('zip'); $list = $zip->decompress($this->_mimepart->getContents(), array('action' => HORDE_COMPRESS_ZIP_LIST)); foreach ($list as $key => $file) { diff --git a/framework/Mime/lib/Horde/Mime/Viewer/Rar.php b/framework/Mime/lib/Horde/Mime/Viewer/Rar.php index 798a4d465..2c8c569ec 100644 --- a/framework/Mime/lib/Horde/Mime/Viewer/Rar.php +++ b/framework/Mime/lib/Horde/Mime/Viewer/Rar.php @@ -51,12 +51,8 @@ class Horde_Mime_Viewer_Rar extends Horde_Mime_Viewer_Driver { $contents = $this->_mimepart->getContents(); - $rar = Horde_Compress::singleton('rar'); - + $rar = Horde_Compress::factory('rar'); $rarData = $rar->decompress($contents); - if (is_a($rarData, 'PEAR_Error')) { - return array(); - } $charset = Horde_Nls::getCharset(); $fileCount = count($rarData); diff --git a/framework/Mime/lib/Horde/Mime/Viewer/Tgz.php b/framework/Mime/lib/Horde/Mime/Viewer/Tgz.php index 549508847..901b9164f 100644 --- a/framework/Mime/lib/Horde/Mime/Viewer/Tgz.php +++ b/framework/Mime/lib/Horde/Mime/Viewer/Tgz.php @@ -52,19 +52,13 @@ class Horde_Mime_Viewer_Tgz extends Horde_Mime_Viewer_Driver /* Decompress gzipped files. */ if (in_array($subtype, $this->_gzipSubtypes)) { - $gzip = Horde_Compress::singleton('gzip'); + $gzip = Horde_Compress::factory('gzip'); $contents = $gzip->decompress($contents); - if (is_a($contents, 'PEAR_Error') || empty($contents)) { - return array(); - } } /* Obtain the list of files/data in the tar file. */ - $tar = Horde_Compress::singleton('tar'); + $tar = Horde_Compress::factory('tar'); $tarData = $tar->decompress($contents); - if (is_a($tarData, 'PEAR_Error')) { - return array(); - } $charset = Horde_Nls::getCharset(); $fileCount = count($tarData); diff --git a/framework/Mime/lib/Horde/Mime/Viewer/Tnef.php b/framework/Mime/lib/Horde/Mime/Viewer/Tnef.php index e7f043164..71ca81e9b 100644 --- a/framework/Mime/lib/Horde/Mime/Viewer/Tnef.php +++ b/framework/Mime/lib/Horde/Mime/Viewer/Tnef.php @@ -49,11 +49,11 @@ class Horde_Mime_Viewer_Tnef extends Horde_Mime_Viewer_Driver */ protected function _renderInline() { - $tnef = Horde_Compress::singleton('tnef'); + $tnef = Horde_Compress::factory('tnef'); + $info = $tnef->decompress($this->_mimepart->getContents()); $data = ''; - $info = $tnef->decompress($this->_mimepart->getContents()); - if (empty($info) || is_a($info, 'PEAR_Error')) { + if (empty($info)) { $data .= ''; } else { $data .= ''; diff --git a/framework/Mime/lib/Horde/Mime/Viewer/Zip.php b/framework/Mime/lib/Horde/Mime/Viewer/Zip.php index d9637a80e..bf9354008 100644 --- a/framework/Mime/lib/Horde/Mime/Viewer/Zip.php +++ b/framework/Mime/lib/Horde/Mime/Viewer/Zip.php @@ -68,17 +68,9 @@ class Horde_Mime_Viewer_Zip extends Horde_Mime_Viewer_Driver { $contents = $this->_mimepart->getContents(); - $zip = Horde_Compress::singleton('zip'); - - /* Make sure this is a valid zip file. */ - if ($zip->checkZipData($contents) === false) { - return array(); - } - + $zip = Horde_Compress::factory('zip'); $zipInfo = $zip->decompress($contents, array('action' => HORDE_COMPRESS_ZIP_LIST)); - if (is_a($zipInfo, 'PEAR_Error')) { - return array(); - } + $fileCount = count($zipInfo); /* Determine maximum file name length. */
' . _("MS-TNEF Attachment contained no data.") . '
' . _("Name") . '' . _("Mime Type") . '