steffen: server/kolab-horde-framework/kolab-horde-framework/Compress/Compress dbx.php, NONE, 1.1 gzip.php, NONE, 1.1 tar.php, NONE, 1.1 tnef.php, NONE, 1.1 zip.php, NONE, 1.1

cvs at intevation.de cvs at intevation.de
Fri Oct 14 16:33:06 CEST 2005


Author: steffen

Update of /kolabrepository/server/kolab-horde-framework/kolab-horde-framework/Compress/Compress
In directory doto:/tmp/cvs-serv28903/kolab-horde-framework/kolab-horde-framework/Compress/Compress

Added Files:
	dbx.php gzip.php tar.php tnef.php zip.php 
Log Message:
Separated Horde Framework from kolab-resource-handlers

--- NEW FILE: dbx.php ---
<?php
/**
 * The Horde_Compress_dbx class allows dbx files (e.g. from Outlook Express)
 * to be read.
 *
 * This class is based on code by:
 * Anotny Raijekov <dev at strategma.bg>
 * http://uruds.gateway.bg/zeos/
 *
 * $Horde: framework/Compress/Compress/dbx.php,v 1.3 2004/01/01 15:14:12 jan Exp $
 *
 * Copyright 2003-2004 Jan Schneider <jan at 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 <jan at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Compress
 */
class Horde_Compress_dbx extends Horde_Compress
{
    var $mails;
    var $tmp;

    /**
     * Decompresses a DBX file and gets information from it.
     *
     * @access public
     *
     * @param string $data  The dbx file data.
     *
     * @return mixed  The requested data.
     */
    function &decompress(&$data, $params = null)
    {
        $this->mails = array();
        $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);
            if (is_a($result, 'PEAR_Error')) {
                return $result;
            }
        }
        return $this->mails;
    }

    /**
     * Returns a null-terminated string from the specified data.
     */
    function readString(&$buf, $pos)
    {
        if ($len = strpos(substr($buf, $pos), chr(0))) {
            return substr($buf, $pos, $len);
        }
        return '';
    }

    function readMessage(&$data, $position)
    {
        $msg = '';
        if ($position > 0) {
            $IndexItemsCount = array_pop(unpack('S', substr($data, 0xC4, 4)));
            if ($IndexItemsCount > 0) {
                $msg = '';
                $part = 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);
                    //var_dump($msg_item);
                    if ($msg_item['FilePos'] != $position) {
                        return PEAR::raiseError(_("Invalid file format"));
                    }
                    $position += 528;
                    $msg .= substr($msg_item['Content'], 0, $msg_item['ItemSize']);
                    $position = $msg_item['NextItem'];
                    if ($position == 0) {
                        break;
                    }
                }
            }
        }
        return $msg;
    }

    function readMessageInfo(&$data, $position)
    {
        $message_info = array();
        $msg_header = unpack('Lposition/LDataLength/SHeaderLength/SFlagCount', substr($data, $position, 12));
        if ($msg_header['position'] != $position) {
            return PEAR::raiseError(_("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)));

            switch ($f & 0xFF) {
            case 0x1:
                $pos = $pos + ($f >> 8);
                $message_info['MsgFlags'] = array_pop(unpack('C', substr($DataBuffer, $pos, 1)));
                $pos++;
                $message_info['MsgFlags'] += array_pop(unpack('C', substr($DataBuffer, $pos, 1))) * 256;
                $pos++;
                $message_info['MsgFlags'] += array_pop(unpack('C', substr($DataBuffer, $pos, 1))) * 65536;
                break;

            case 0x2:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['Sent'] = array_pop(unpack('L', substr($DataBuffer, $pos, 4)));
                break;

            case 0x4:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['position'] = array_pop(unpack('L', substr($DataBuffer, $pos, 4)));
                break;

            case 0x7:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['MessageID'] = $this->readstring($DataBuffer, $pos);
                break;

            case 0x8:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['Subject'] = $this->readstring($DataBuffer, $pos);
                break;

            case 0x9:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['From_reply'] = $this->readstring($DataBuffer, $pos);
                break;

            case 0xA:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['References'] = $this->readstring($DataBuffer, $pos);
                break;

            case 0xB:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['Newsgroup'] = $this->readstring($DataBuffer, $pos);
                break;

            case 0xD:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['From'] = $this->readstring($DataBuffer, $pos);
                break;

            case 0xE:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['Reply_To'] = $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 0x13:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['Receipt'] = $this->readstring($DataBuffer, $pos);
                break;

            case 0x1A:
                $pos += array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                $message_info['Account'] = $this->readstring($DataBuffer, $pos);
                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:
                $message_info['Msg'] = array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                break;

            case 0x81:
                $message_info['MsgFlags'] = array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                break;

            case 0x84:
                $message_info['position'] = array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                break;

            case 0x91:
                $message_info['size'] = array_pop(unpack('L', substr($FlagsBuffer, $i * 4, 4))) >> 8;
                break;
            }
        }

        return $message_info;
    }

    function readIndex(&$data, $position)
    {
        //var_dump($position);
        $index_header = unpack('LFilePos/LUnknown1/LPrevIndex/LNextIndex/LCount/LUnknown', substr($data, $position, 24));
        //var_dump($index_header);
        if ($index_header['FilePos'] != $position) {
            return PEAR::raiseError(_("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;
        //var_dump($icount);
        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);
                //var_dump($IndexItem);
                if ($IndexItem['HeaderPos'] > 0) {
                    if (false && strtolower($this->fname) == 'folders.dbx')
                        //read_folder($fp,$IndexItem['HeaderPos']);
                        print 'Read folder not implemented in v1.0a<br>';
                    else {
                        $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']);
                }
            }
        }
    }

    //debug function to display human readble message flags (Just for debugging purpose)
    function decode_flags($x)
    {
        $decode_flag['DOWNLOADED']              = 0x1;
        $decode_flag['MARKED']                  = 0x20;
        $decode_flag['READED']                  = 0x80;
        $decode_flag['DOWNLOAD_LATER']          = 0x100;
        $decode_flag['NEWS_MSG']                = 0x800;  // to verify
        $decode_flag['ATTACHMENTS']             = 0x4000;
        $decode_flag['REPLY']                   = 0x80000;
        $decode_flag['INSPECT_CONVERSATION']    = 0x400000;
        $decode_flag['IGNORE_CONVERSATION']     = 0x800000;

        $decoded_flags = '';

        if(($x & $decode_flag['NEWS_MSG']) != 0) $decoded_flags .= "NEWS MESSAGE\n<br>";
        if(($x & $decode_flag['DOWNLOAD_LATER']) != 0) $decoded_flags .= "DOWNLOAD LATER\n<br>";
        if(($x & $decode_flag['DOWNLOADED']) != 0) $decoded_flags .= "DOWNLOADED\n<br>";
        if(($x & $decode_flag['READED']) != 0) $decoded_flags .= "READED\n<br>";
        if(($x & $decode_flag['MARKED']) != 0) $decoded_flags .= "MARKED\n<br>";
        if(($x & $decode_flag['ATTACHMENTS']) != 0) $decoded_flags .= "ATTACHMENTS\n<br>";
        if(($x & $decode_flag['REPLY']) != 0) $decoded_flags .= "REPLY\n<br>";
        if(($x & $decode_flag['INSPECT_CONVERSATION']) != 0) $decoded_flags .= "INSPECT CONVERSATION\n<br>";
        if(($x & $decode_flag['IGNORE_CONVERSATION']) != 0) $decoded_flags .= "IGNORE CONVERSATION\n<br>";

        return $decoded_flags;
    }

}

--- NEW FILE: gzip.php ---
<?php
/**
 * The Horde_Compress_gzip class allows gzip files to be read.
 *
 * $Horde: framework/Compress/Compress/gzip.php,v 1.7 2004/01/01 15:14:13 jan Exp $
 *
 * Copyright 2002-2004 Michael Cochrane <mike at graftonhall.co.nz>
 * Copyright 2003-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * 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  Michael Cochrane <mike at graftonhall.co.nz>
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Compress
 */
class Horde_Compress_gzip extends Horde_Compress {

    /**
     * Gzip file flags.
     *
     * @var array $_flags
     */
    var $_flags = array(
        'FTEXT'     =>  0x01,
        'FHCRC'     =>  0x02,
        'FEXTRA'    =>  0x04,
        'FNAME'     =>  0x08,
        'FCOMMENT'  =>  0x10
    );

    /**
     * Decompress a gzip file and get information from it.
     *
     * @access public
     *
     * @param string &$data  The tar file data.
     * @param array $params  The parameter array (Unused).
     *
     * @return string  The uncompressed data.
     */
    function &decompress(&$data, $params = array())
    {
        /* If gzip is not compiled into PHP, return now. */
        if (!Util::extensionExists('zlib')) {
            return PEAR::raiseError(_("This server can't uncompress zip and gzip files."));
        }

        /* Gzipped File - decompress it first. */
        $position = 0;
        $info = unpack('CCM/CFLG/VTime/CXFL/COS', substr($data, $position + 2));
        $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)) {
            return PEAR::raiseError(_("Unable to decompress data."));
        }

        return $result;
    }

}

--- NEW FILE: tar.php ---
<?php
/**
 * The Horde_Compress_tar class allows tar files to be read.
 *
 * $Horde: framework/Compress/Compress/tar.php,v 1.4 2004/01/01 15:14:13 jan Exp $
 *
 * Copyright 2002-2004 Michael Cochrane <mike at graftonhall.co.nz>
 * Copyright 2003-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * 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  Michael Cochrane <mike at graftonhall.co.nz>
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Compress
 */
class Horde_Compress_tar extends Horde_Compress {

    /**
     * Tar file types.
     *
     * @var array $_types
     */
    var $_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 $_flags
     */
    var $_flags = array(
        'FTEXT'     =>  0x01,
        'FHCRC'     =>  0x02,
        'FEXTRA'    =>  0x04,
        'FNAME'     =>  0x08,
        'FCOMMENT'  =>  0x10
    );

    /**
     * Decompress a tar file and get information from it.
     *
     * @access public
     *
     * @param string &$data   The tar file data.
     * @param array $params  The parameter array (Unused).
     *
     * @return array  The requested data or PEAR_Error on error.
     * <pre>
     * 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
     * </pre>
     */
    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) {
                return PEAR::raiseError(_("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();

                $file['size'] = octdec($info['size']);
                $file['date'] = octdec($info['mtime']);
                $file['name'] = trim($info['filename']);

                if (array_key_exists($info['typeflag'], $this->_types)) {
                   $file['type'] = $this->_types[$info['typeflag']];
                } else {
                   $file['type'] = '';
                }
                $file['data'] = '';
                $file['attr'] = '';

                if (($info['typeflag'] == 0) ||
                    ($info['typeflag'] == 0x30) ||
                    ($info['typeflag'] == 0x35)) {
                    /* File or folder. */
                    $file['data'] = $contents;

                    $file['attr']  = ($info['typeflag'] == 0x35) ? 'd' : '-';
                    $file['attr'] .= (hexdec(substr($info['mode'], 4, 3)) & 0x400) ? 'r' : '-';
                    $file['attr'] .= (hexdec(substr($info['mode'], 4, 3)) & 0x200) ? 'w' : '-';
                    $file['attr'] .= (hexdec(substr($info['mode'], 4, 3)) & 0x100) ? 'x' : '-';
                    $file['attr'] .= (hexdec(substr($info['mode'], 4, 3)) & 0x040) ? 'r' : '-';
                    $file['attr'] .= (hexdec(substr($info['mode'], 4, 3)) & 0x020) ? 'w' : '-';
                    $file['attr'] .= (hexdec(substr($info['mode'], 4, 3)) & 0x010) ? 'x' : '-';
                    $file['attr'] .= (hexdec(substr($info['mode'], 4, 3)) & 0x004) ? 'r' : '-';
                    $file['attr'] .= (hexdec(substr($info['mode'], 4, 3)) & 0x002) ? 'w' : '-';
                    $file['attr'] .= (hexdec(substr($info['mode'], 4, 3)) & 0x001) ? 'x' : '-';
                } else {
                    /* Some other type. */
                }

                $return_array[] = $file;
            }
        }

        return $return_array;
    }

}

--- NEW FILE: tnef.php ---
<?php

define('TNEF_SIGNATURE',      0x223e9f78);
define('TNEF_LVL_MESSAGE',    0x01);
define('TNEF_LVL_ATTACHMENT', 0x02);

define('TNEF_STRING', 0x00010000);
define('TNEF_BYTE',   0x00060000);
define('TNEF_WORD',   0x00070000);
define('TNEF_DWORD',  0x00080000);

define('TNEF_ASUBJECT',   TNEF_DWORD  | 0x8004);
define('TNEF_AMCLASS',    TNEF_WORD   | 0x8008);
define('TNEF_ATTACHDATA', TNEF_BYTE   | 0x800f);
define('TNEF_AFILENAME',  TNEF_STRING | 0x8010);
define('TNEF_ARENDDATA',  TNEF_BYTE   | 0x9002);
define('TNEF_AMAPIATTRS', TNEF_BYTE   | 0x9005);
define('TNEF_AVERSION',   TNEF_DWORD  | 0x9006);

define('TNEF_MAPI_NULL',           0x0001);
define('TNEF_MAPI_SHORT',          0x0002);
define('TNEF_MAPI_INT',            0x0003);
define('TNEF_MAPI_FLOAT',          0x0004);
define('TNEF_MAPI_DOUBLE',         0x0005);
define('TNEF_MAPI_CURRENCY',       0x0006);
define('TNEF_MAPI_APPTIME',        0x0007);
define('TNEF_MAPI_ERROR',          0x000a);
define('TNEF_MAPI_BOOLEAN',        0x000b);
define('TNEF_MAPI_OBJECT',         0x000d);
define('TNEF_MAPI_INT8BYTE',       0x0014);
define('TNEF_MAPI_STRING',         0x001e);
define('TNEF_MAPI_UNICODE_STRING', 0x001f);
define('TNEF_MAPI_SYSTIME',        0x0040);
define('TNEF_MAPI_CLSID',          0x0048);
define('TNEF_MAPI_BINARY',         0x0102);

define('TNEF_MAPI_ATTACH_MIME_TAG',      0x370E);
define('TNEF_MAPI_ATTACH_LONG_FILENAME', 0x3707);

/**
 * The Horde_Compress_tnef class allows MS-TNEF data to be displayed.
 *
 * The TNEF rendering is based on code by:
 *   Graham Norbury <gnorbury at bondcar.com>
 * Original design by:
 *   Thomas Boll <tb at boll.ch>, Mark Simpson <damned at world.std.com>
 *
 * $Horde: framework/Compress/Compress/tnef.php,v 1.6 2004/01/01 15:14:13 jan Exp $
 *
 * Copyright 2002-2004 Jan Schneider <jan at horde.org>
 * Copyright 2002-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * 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 <jan at horde.org>
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Compress
 */
class Horde_Compress_tnef extends Horde_Compress {

    /**
     * Decompress the data.
     *
     * @access public
     *
     * @param string $data            The data to decompress.
     * @param optional array $params  An array of arguments needed to
     *                                decompress the data.
     *
     *
     * @return mixed  The decompressed data.
     *                Returns a PEAR_Error object on error.
     */
    function &decompress($data, $params = array())
    {
        $out = array();

        if ($this->_geti($data, 32) == TNEF_SIGNATURE) {
            $this->_geti($data, 16);

            while (strlen($data) > 0) {
                switch ($this->_geti($data, 8)) {
                case TNEF_LVL_MESSAGE:
                    $this->_decodeMessage($data);
                    break;

                case TNEF_LVL_ATTACHMENT:
                    $this->_decodeAttachment($data, $out);
                    break;
                }
            }
        }

        return array_reverse($out);
    }

    /**
     * TODO
     *
     * @access private
     *
     * @param string &$data  The data string.
     * @param integer $bits  How many bits to retrieve.
     *
     * @return TODO
     */
    function _getx(&$data, $bits)
    {
        $value = null;

        if (strlen($data) >= $bits) {
            $value = substr($data, 0, $bits);
            $data = substr_replace($data, '', 0, $bits);
        }

        return $value;
    }

    /**
     * TODO
     *
     * @access private
     *
     * @param string &$data  The data string.
     * @param integer $bits  How many bits to retrieve.
     *
     * @return TODO
     */
    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
     *
     * @access private
     *
     * @param string &$data      The data string.
     * @param string $attribute  TODO
     */
    function _decodeAttribute(&$data, $attribute)
    {
        /* Data. */
        $this->_getx($data, $this->_geti($data, 32));

        /* Checksum. */
        $this->_geti($data, 16);
    }

    /**
     * TODO
     *
     * @access private
     *
     * @param string $data             The data string.
     * @param array &$attachment_data  TODO
     */
    function _extractMapiAttributes($data, &$attachment_data)
    {
        /* Number of attributes. */
        $this->_geti($data, 32);

        while (strlen($data) > 0) {
            $value = null;
            $attr_type = $this->_geti($data, 16);
            $attr_name = $this->_geti($data, 16);

            switch ($attr_type) {
            case TNEF_MAPI_SHORT:
                $value = $this->_geti($data, 16);
                break;

            case TNEF_MAPI_INT:
            case TNEF_MAPI_BOOLEAN:
                $value = $this->_geti($data, 32);
                break;

            case TNEF_MAPI_FLOAT:
                $value = $this->_getx($data, 4);
                break;

            case TNEF_MAPI_DOUBLE:
            case TNEF_MAPI_SYSTIME:
                $value = $this->_getx($data, 8);
                break;

            case TNEF_MAPI_STRING:
            case TNEF_MAPI_UNICODE_STRING:
            case TNEF_MAPI_BINARY:
                $num_vals = $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);

                    /* Read and truncate to length. */
                    $value = substr($this->_getx($data, $datalen), 0, $length);
                }
                break;
            }

            /* Store any interesting attributes. */
            switch ($attr_name) {
            case TNEF_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 TNEF_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
     *
     * @access private
     *
     * @param string &$data  The data string.
     */
    function _decodeMessage(&$data)
    {
        $this->_decodeAttribute($data, $this->_geti($data, 32));
    }

    /**
     * TODO
     *
     * @access private
     *
     * @param string &$data            The data string.
     * @param array &$attachment_data  TODO
     */
    function _decodeAttachment(&$data, &$attachment_data)
    {
        $attribute = $this->_geti($data, 32);

        switch ($attribute) {
        case TNEF_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 TNEF_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 TNEF_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 TNEF_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);
        }
    }

}

--- NEW FILE: zip.php ---
<?php

/**
 * For decompress(), return a list of files/information about the
 * zipfile.
 */
define('HORDE_COMPRESS_ZIP_LIST', 1);

/**
 * For decompress(), return the data for an individual file in the
 * zipfile.
 */
define('HORDE_COMPRESS_ZIP_DATA', 2);

/**
 * The Horde_Compress_zip class allows ZIP files to be created and
 * read.
 *
 * $Horde: framework/Compress/Compress/zip.php,v 1.11 2004/01/01 15:14:13 jan Exp $
 *
 * The ZIP compression code is partially based on code from:
 *   Eric Mueller <eric at themepark.com>
 *   http://www.zend.com/codex.php?id=535&single=1
 *
 *   Deins125 <webmaster at atlant.ru>
 *   http://www.zend.com/codex.php?id=470&single=1
 *
 * The ZIP compression date code is partially based on code from
 *   Peter Listiak <mlady at users.sourceforge.net>
 *
 * Copyright 2000-2004 Chuck Hagenbuch <chuck at horde.org>
 * Copyright 2002-2004 Michael Cochrane <mike at graftonhall.co.nz>
 * Copyright 2003-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * 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 <chuck at horde.org>
 * @author  Michael Cochrane <mike at graftonhall.co.nz>
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Compress
 */
class Horde_Compress_zip extends Horde_Compress {

    /**
     * ZIP compression methods.
     *
     * @var array $_methods
     */
    var $_methods = array(
        0x0 => 'None',
        0x1 => 'Shrunk',
        0x2 => 'Super Fast',
        0x3 => 'Fast',
        0x4 => 'Normal',
        0x5 => 'Maximum',
        0x6 => 'Imploded',
        0x8 => 'Deflated'
    );

    /**
     * Beginning of central directory record.
     *
     * @var string $_ctrlDirHeader
     */
    var $_ctrlDirHeader = "\x50\x4b\x01\x02";

    /**
     * End of central directory record.
     *
     * @var string $_ctrlDirEnd
     */
    var $_ctrlDirEnd = "\x50\x4b\x05\x06\x00\x00\x00\x00";

    /**
     * Beginning of file contents.
     *
     * @var string $_fileHeader
     */
    var $_fileHeader = "\x50\x4b\x03\x04";

    /**
     * Create a ZIP compressed file from an array of file data.
     *
     * @access public
     *
     * @param array $data             The data to compress.
     * <pre>
     * 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.
     * </pre>
     * @param optional array $params  The parameter array (Unused).
     *
     * @return string  The ZIP file.
     */
    function &compress($data, $params = array())
    {
        $contents = $ctrldir = array();

        foreach ($data as $val) {
            $this->_addToZIPFile($val, $contents, $ctrldir);
        }

        return $this->_createZIPFile($contents, $ctrldir);
    }

    /**
     * Decompress a ZIP file and get information from it.
     *
     * @access public
     *
     * @param string $data   The zipfile data.
     * @param array $params  The parameter array.
     * <pre>
     * The following parameters are REQUIRED:
     * 'action' (integer)  =>  The action to take on the data.  Either
     *                         HORDE_COMPRESS_ZIP_LIST or
     *                         HORDE_COMPRESS_ZIP_DATA.
     *
     * The following parameters are REQUIRED for HORDE_COMPRESS_ZIP_DATA also:
     * 'info' (array)   =>  The zipfile list.
     * 'key' (integer)  =>  The position of the file in the archive list.
     * </pre>
     *
     * @return mixed  The requested data.
     */
    function &decompress($data, $params)
    {
        if (array_key_exists('action', $params)) {
            if ($params['action'] == HORDE_COMPRESS_ZIP_LIST) {
                return $this->_getZipInfo($data);
            } elseif ($params['action'] == HORDE_COMPRESS_ZIP_DATA) {
                // TODO: Check for parameters.
                return $this->_getZipData($data, $params['info'], $params['key']);
            } else {
                return PEAR::raiseError(_("Incorrect action code given."), 'horde.error');
            }
        } else {
            return PEAR::raiseError(_("You must specify what action to perform."), 'horde.error');
        }
    }

    /**
     * Get the list of files/data from the zip archive.
     *
     * @access private
     *
     * @param string &$data  The zipfile data.
     *
     * @return array  KEY: Position in zipfile
     *                VALUES: 'attr'    --  File attributes
     *                        'csize'   --  Compressed file size
     *                        'date'    --  File modification time
     *                        'name'    --  Filename
     *                        'method'  --  Compression method
     *                        'size'    --  Original file size
     *                        'type'    --  File type
     */
    function &_getZipInfo(&$data)
    {
        $entries = array();

        /* Get details from local file header. */
        $fhStart = strpos($data, $this->_fileHeader);

        do {
            $file = array();

            $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $fhStart + 8, 25));

            $file['name'] = substr($data, $fhStart + 30, $info['Length']);
            $file['csize'] = $info['Compressed'];
            $file['size'] = $info['Uncompressed'];
            $file['method'] = $this->_methods[$info['Method']];
            $file['crc'] = dechex($info['CRC32']);

            $modDate = $info['Time'];
            $file['date'] = mktime((($modDate >> 11) & 0x1f),
                                   (($modDate >> 5) & 0x3f),
                                   (($modDate << 1) & 0x3e),
                                   (($modDate >> 21) & 0x07),
                                   (($modDate >> 16) & 0x1f),
                                   ((($modDate >> 25) & 0x7f) + 80));

            $file['_dataStart'] = $fhStart + 30 + $info['Length'] + $info['ExtraLength'];
            $file['_method'] = $info['Method'];

            /* Filenames are unique in zips so this can be done. */
            $entries[$file['name']] = $file;
        } while (($fhStart = strpos($data, $this->_fileHeader, $fhStart + 30 + $info['Length'])) !== false);

        /* Get details from Central directory structure. */
        $fhStart = strpos($data, $this->_ctrlDirHeader);

        do {
            $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart + 10, 20));
            $name = substr($data, $fhStart + 46, $info['Length']);
            $entries[$name]['csize'] = $info['Compressed'];
            $entries[$name]['size'] = $info['Uncompressed'];
            $entries[$name]['crc'] = dechex($info['CRC32']);

            $info = unpack('vInternal/VExternal', substr($data, $fhStart + 36, 6));

            if (array_key_exists($name, $entries)) {
                $entries[$name]['type']  = ($info['Internal'] & 0x01) ? 'text' : 'binary';
                $entries[$name]['attr']  = '';
                $entries[$name]['attr'] .= ($info['External'] & 0x10) ? 'D' : '-';
                $entries[$name]['attr'] .= ($info['External'] & 0x20) ? 'A' : '-';
                $entries[$name]['attr'] .= ($info['External'] & 0x03) ? 'S' : '-';
                $entries[$name]['attr'] .= ($info['External'] & 0x02) ? 'H' : '-';
                $entries[$name]['attr'] .= ($info['External'] & 0x01) ? 'R' : '-';
            }

        } while (($fhStart = strpos($data, $this->_ctrlDirHeader, $fhStart + 46)) !== false);

        return array_values($entries);
    }

    /**
     * Returns the data for a specific archived file.
     *
     * @access private
     *
     * @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.
     */
    function &_getZipData(&$data, &$info, $key)
    {
        if (($info[$key]['_method'] == 0x8) && 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']);
        } else {
            return '';
        }
    }

    /**
     * Checks to see if the data is a valid ZIP file.
     *
     * @access public
     *
     * @param string &$data  The ZIP file data.
     *
     * @return boolean  True if valid, false if invalid.
     */
    function checkZipData(&$data)
    {
        if (strpos($data, $this->_fileHeader) === false) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 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).
     *
     * @access private
     *
     * @param optional integer $unixtime  The current UNIX timestamp.
     *
     * @return integer  The current date in a 4-byte DOS format.
     */
    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.
     *
     * @access private
     *
     * @param array &$file      See Horde_Compress_zip::createZipFile().
     * @param array &$contents  An array of existing zipped files.
     * @param array &$ctrldir   An array of central directory information.
     */
    function _addToZIPFile(&$file, &$contents, &$ctrldir)
    {
        $data = &$file['data'];
        $name = str_replace('\\', '/', $file['name']);

        /* See if time/date information has been provided. */
        $ftime = null;
        if (array_key_exists('time', $file)) {
            $ftime = $file['time'];
        }

        /* 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]));

        $fr  = $this->_fileHeader;   /* Begin creating the ZIP data. */
        $fr .= "\x14\x00";           /* Version needed to extract. */
        $fr .= "\x00\x00";           /* General purpose bit flag. */
        $fr .= "\x08\x00";           /* Compression method. */
        $fr .= $hexdtime;            /* Last modification time/date. */

        /* "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);

        $fr .= pack('V', $crc);            /* CRC 32 information. */
        $fr .= pack('V', $c_len);          /* Compressed filesize. */
        $fr .= pack('V', $unc_len);        /* Uncompressed filesize. */
        $fr .= pack('v', strlen($name));   /* Length of filename. */
        $fr .= pack('v', 0);               /* Extra field length. */
        $fr .= $name;                      /* File name. */

        /* "File data" segment. */
        $fr .= $zdata;

        /* "Data descriptor" segment (optional but necessary if archive is
           not served as file). */
        $fr .= pack('V', $crc);
        $fr .= pack('V', $c_len);
        $fr .= pack('V', $unc_len);

        /* Add this entry to array. */
        $old_offset = strlen(implode('', $contents));
        $contents[] = &$fr;

        /* Add to central directory record. */
        $cdrec  = $this->_ctrlDirHeader;
        $cdrec .= "\x00\x00";                /* Version made by. */
        $cdrec .= "\x14\x00";                /* Version needed to extract */
        $cdrec .= "\x00\x00";                /* General purpose bit flag */
        $cdrec .= "\x08\x00";                /* Compression method */
        $cdrec .= $hexdtime;                 /* Last mod time/date. */
        $cdrec .= pack('V', $crc);           /* CRC 32 information. */
        $cdrec .= pack('V', $c_len);         /* Compressed filesize. */
        $cdrec .= pack('V', $unc_len);       /* Uncompressed filesize. */
        $cdrec .= pack('v', strlen($name));  /* Length of filename. */
        $cdrec .= pack('v', 0 );             /* Extra field length. */
        $cdrec .= pack('v', 0 );             /* File comment length. */
        $cdrec .= pack('v', 0 );             /* Disk number start. */
        $cdrec .= pack('v', 0 );             /* Internal file attributes. */
        $cdrec .= pack('V', 32 );            /* External file attributes -
                                                'archive' bit set. */
        $cdrec .= pack('V', $old_offset);    /* Relative offset of local
                                                header. */
        $cdrec .= $name;                     /* File name. */
        /* Optional extra field, file comment goes here. */

        // Save to central directory array. */
        $ctrldir[] = &$cdrec;
    }

    /**
     * Creates the ZIP file.
     * Official ZIP file format: http://www.pkware.com/appnote.txt
     *
     * @access private
     *
     * @return string  The ZIP file.
     */
    function &_createZIPFile(&$contents, &$ctrlDir)
    {
        $data = implode('', $contents);
        $dir  = implode('', $ctrlDir);

        return $data .
               $dir .
               $this->_ctrlDirEnd .
               /* 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($data)) .
               /* ZIP file comment length. */
               "\x00\x00";
    }

}





More information about the commits mailing list