|
- <?php
- //
- // +----------------------------------------------------------------------+
- // | PHP Version 4 |
- // +----------------------------------------------------------------------+
- // | Copyright (c) 1997-2002 The PHP Group |
- // +----------------------------------------------------------------------+
- // | This source file is subject to version 2.02 of the PHP license, |
- // | that is bundled with this package in the file LICENSE, and is |
- // | available at through the world-wide-web at |
- // | http://www.php.net/license/2_02.txt. |
- // | If you did not receive a copy of the PHP license and are unable to |
- // | obtain it through the world-wide-web, please send a note to |
- // | license@php.net so we can mail you a copy immediately. |
- // +----------------------------------------------------------------------+
- // | Authors: Shane Caraveo <shane@caraveo.com> |
- // | Ralf Hofmann <ralf.hofmann@verdisoft.com> |
- // +----------------------------------------------------------------------+
- //
- // $Id: DIME.php,v 1.5 2002/09/29 01:55:16 shane Exp $
- //
-
- require_once 'PEAR.php';
- /**
- *
- * DIME Encoding/Decoding
- *
- * What is it?
- * This class enables you to manipulate and build
- * a DIME encapsulated message.
- *
- * http://www.ietf.org/internet-drafts/draft-nielsen-dime-02.txt
- *
- * 09/18/02 Ralf - A huge number of changes to be compliant
- * with the DIME Specification Release 17 June 2002
- *
- * TODO: lots of stuff needs to be tested.
- * Definitily have to go through DIME spec and
- * make things work right, most importantly, sec 3.3
- * make examples, document
- *
- * see test/dime_mesage_test.php for example of usage
- *
- * @author Shane Caraveo <shane@caraveo.com>,
- * Ralf Hofmann <ralf.hofmann@verdisoft.com>
- * @version $Revision: 1.5 $
- * @package Net_DIME
- */
- define('NET_DIME_TYPE_UNCHANGED',0x00);
- define('NET_DIME_TYPE_MEDIA',0x01);
- define('NET_DIME_TYPE_URI',0x02);
- define('NET_DIME_TYPE_UNKNOWN',0x03);
- define('NET_DIME_TYPE_NONE',0x04);
-
- define('NET_DIME_VERSION',0x0001);
-
- define('NET_DIME_RECORD_HEADER',12);
-
- define('NET_DIME_FLAGS', 0);
- define('NET_DIME_OPTS_LEN', 1);
- define('NET_DIME_ID_LEN', 2);
- define('NET_DIME_TYPE_LEN', 3);
- define('NET_DIME_DATA_LEN', 4);
- define('NET_DIME_OPTS', 5);
- define('NET_DIME_ID', 6);
- define('NET_DIME_TYPE', 7);
- define('NET_DIME_DATA', 8);
-
- class Net_DIME_Record extends PEAR
- {
- // these are used to hold the padded length
- var $OPTS_LENGTH = 0;
- var $ID_LENGTH = 0;
- var $TYPE_LENGTH = 0;
- var $DATA_LENGTH = 0;
- var $_haveOpts = FALSE;
- var $_haveID = FALSE;
- var $_haveType = FALSE;
- var $_haveData = FALSE;
- var $debug = FALSE;
- var $padstr = "\0";
- /**
- * Elements
- * [NET_DIME_FLAGS], 16 bits: VERSION:MB:ME:CF:TYPE_T
- * [NET_DIME_OPTS_LEN], 16 bits: OPTIONS_LENGTH
- * [NET_DIME_ID_LEN], 16 bits: ID_LENGTH
- * [NET_DIME_TYPE_LEN], 16 bits: TYPE_LENGTH
- * [NET_DIME_DATA_LEN], 32 bits: DATA_LENGTH
- * [NET_DIME_OPTS] : OPTIONS
- * [NET_DIME_ID] : ID
- * [NET_DIME_TYPE] : TYPE
- * [NET_DIME_DATA] : DATA
- */
- var $Elements = array(NET_DIME_FLAGS => 0, NET_DIME_OPTS_LEN => 0,
- NET_DIME_ID_LEN => 0, NET_DIME_TYPE_LEN => 0,
- NET_DIME_DATA_LEN => 0,
- NET_DIME_OPTS => '',
- NET_DIME_ID => '',
- NET_DIME_TYPE => '',
- NET_DIME_DATA => '');
-
- function Net_DIME_Record($debug = FALSE)
- {
- $this->debug = $debug;
- if ($debug) $this->padstr = '*';
- }
-
- function setMB()
- {
- $this->Elements[NET_DIME_FLAGS] |= 0x0400;
- }
-
- function setME()
- {
- $this->Elements[NET_DIME_FLAGS] |= 0x0200;
- }
-
- function setCF()
- {
- $this->Elements[NET_DIME_FLAGS] |= 0x0100;
- }
-
- function isChunk()
- {
- return $this->Elements[NET_DIME_FLAGS] & 0x0100;
- }
-
- function isEnd()
- {
- return $this->Elements[NET_DIME_FLAGS] & 0x0200;
- }
-
- function isStart()
- {
- return $this->Elements[NET_DIME_FLAGS] & 0x0400;
- }
-
- function getID()
- {
- return $this->Elements[NET_DIME_ID];
- }
-
- function getType()
- {
- return $this->Elements[NET_DIME_TYPE];
- }
-
- function getData()
- {
- return $this->Elements[NET_DIME_DATA];
- }
-
- function getDataLength()
- {
- return $this->Elements[NET_DIME_DATA_LEN];
- }
-
- function setType($typestring, $type=NET_DIME_TYPE_UNKNOWN)
- {
- $typelen = strlen($typestring) & 0xFFFF;
- $type = $type << 4;
- $this->Elements[NET_DIME_FLAGS] = ($this->Elements[NET_DIME_FLAGS] & 0xFF0F) | $type;
- $this->Elements[NET_DIME_TYPE_LEN] = $typelen;
- $this->TYPE_LENGTH = $this->_getPadLength($typelen);
- $this->Elements[NET_DIME_TYPE] = $typestring;
- }
-
- function generateID()
- {
- $id = md5(time());
- $this->setID($id);
- return $id;
- }
-
- function setID($id)
- {
- $idlen = strlen($id) & 0xFFFF;
- $this->Elements[NET_DIME_ID_LEN] = $idlen;
- $this->ID_LENGTH = $this->_getPadLength($idlen);
- $this->Elements[NET_DIME_ID] = $id;
- }
-
- function setData($data, $size=0)
- {
- $datalen = $size?$size:strlen($data);
- $this->Elements[NET_DIME_DATA_LEN] = $datalen;
- $this->DATA_LENGTH = $this->_getPadLength($datalen);
- $this->Elements[NET_DIME_DATA] = $data;
- }
-
- function encode()
- {
- // insert version
- $this->Elements[NET_DIME_FLAGS] = ($this->Elements[NET_DIME_FLAGS] & 0x07FF) | (NET_DIME_VERSION << 11);
-
- // the real dime encoding
- $format = '%c%c%c%c%c%c%c%c%c%c%c%c'.
- '%'.$this->OPTS_LENGTH.'s'.
- '%'.$this->ID_LENGTH.'s'.
- '%'.$this->TYPE_LENGTH.'s'.
- '%'.$this->DATA_LENGTH.'s';
- return sprintf($format,
- ($this->Elements[NET_DIME_FLAGS]&0x0000FF00)>>8,
- ($this->Elements[NET_DIME_FLAGS]&0x000000FF),
- ($this->Elements[NET_DIME_OPTS_LEN]&0x0000FF00)>>8,
- ($this->Elements[NET_DIME_OPTS_LEN]&0x000000FF),
- ($this->Elements[NET_DIME_ID_LEN]&0x0000FF00)>>8,
- ($this->Elements[NET_DIME_ID_LEN]&0x000000FF),
- ($this->Elements[NET_DIME_TYPE_LEN]&0x0000FF00)>>8,
- ($this->Elements[NET_DIME_TYPE_LEN]&0x000000FF),
- ($this->Elements[NET_DIME_DATA_LEN]&0xFF000000)>>24,
- ($this->Elements[NET_DIME_DATA_LEN]&0x00FF0000)>>16,
- ($this->Elements[NET_DIME_DATA_LEN]&0x0000FF00)>>8,
- ($this->Elements[NET_DIME_DATA_LEN]&0x000000FF),
- str_pad($this->Elements[NET_DIME_OPTS], $this->OPTS_LENGTH, $this->padstr),
- str_pad($this->Elements[NET_DIME_ID], $this->ID_LENGTH, $this->padstr),
- str_pad($this->Elements[NET_DIME_TYPE], $this->TYPE_LENGTH, $this->padstr),
- str_pad($this->Elements[NET_DIME_DATA], $this->DATA_LENGTH, $this->padstr));
- }
-
- function _getPadLength($len)
- {
- $pad = 0;
- if ($len) {
- $pad = $len % 4;
- if ($pad) $pad = 4 - $pad;
- }
- return $len + $pad;
- }
-
- function decode(&$data)
- {
- // REAL DIME decoding
- $this->Elements[NET_DIME_FLAGS] = (hexdec(bin2hex($data[0]))<<8) + hexdec(bin2hex($data[1]));
- $this->Elements[NET_DIME_OPTS_LEN] = (hexdec(bin2hex($data[2]))<<8) + hexdec(bin2hex($data[3]));
- $this->Elements[NET_DIME_ID_LEN] = (hexdec(bin2hex($data[4]))<<8) + hexdec(bin2hex($data[5]));
- $this->Elements[NET_DIME_TYPE_LEN] = (hexdec(bin2hex($data[6]))<<8) + hexdec(bin2hex($data[7]));
- $this->Elements[NET_DIME_DATA_LEN] = (hexdec(bin2hex($data[8]))<<24) +
- (hexdec(bin2hex($data[9]))<<16) +
- (hexdec(bin2hex($data[10]))<<8) +
- hexdec(bin2hex($data[11]));
- $p = 12;
-
- $version = (($this->Elements[NET_DIME_FLAGS]>>11) & 0x001F);
-
- if ($version == NET_DIME_VERSION)
- {
- $this->OPTS_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_OPTS_LEN]);
- $this->ID_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_ID_LEN]);
- $this->TYPE_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_TYPE_LEN]);
- $this->DATA_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_DATA_LEN]);
-
- $datalen = strlen($data);
- $this->Elements[NET_DIME_OPTS] = substr($data,$p,$this->Elements[NET_DIME_OPTS_LEN]);
- $this->_haveOpts = (strlen($this->Elements[NET_DIME_OPTS]) == $this->Elements[NET_DIME_OPTS_LEN]);
- if ($this->_haveOpts) {
- $p += $this->OPTS_LENGTH;
- $this->Elements[NET_DIME_ID] = substr($data,$p,$this->Elements[NET_DIME_ID_LEN]);
- $this->_haveID = (strlen($this->Elements[NET_DIME_ID]) == $this->Elements[NET_DIME_ID_LEN]);
- if ($this->_haveID) {
- $p += $this->ID_LENGTH;
- $this->Elements[NET_DIME_TYPE] = substr($data,$p,$this->Elements[NET_DIME_TYPE_LEN]);
- $this->_haveType = (strlen($this->Elements[NET_DIME_TYPE]) == $this->Elements[NET_DIME_TYPE_LEN]);
- if ($this->_haveType) {
- $p += $this->TYPE_LENGTH;
- $this->Elements[NET_DIME_DATA] = substr($data,$p,$this->Elements[NET_DIME_DATA_LEN]);
- $this->_haveData = (strlen($this->Elements[NET_DIME_DATA]) == $this->Elements[NET_DIME_DATA_LEN]);
- if ($this->_haveData) {
- $p += $this->DATA_LENGTH;
- } else {
- $p += strlen($this->Elements[NET_DIME_DATA]);
- }
- } else {
- $p += strlen($this->Elements[NET_DIME_TYPE]);
- }
- } else {
- $p += strlen($this->Elements[NET_DIME_ID]);
- }
- } else {
- $p += strlen($this->Elements[NET_DIME_OPTS]);
- }
- }
- return substr($data, $p);
- }
-
- function addData(&$data)
- {
- $datalen = strlen($data);
- $p = 0;
- if (!$this->_haveOpts) {
- $have = strlen($this->Elements[NET_DIME_OPTS]);
- $this->Elements[NET_DIME_OPTS] .= substr($data,$p,$this->Elements[NET_DIME_OPTS_LEN]-$have);
- $this->_haveOpts = (strlen($this->Elements[NET_DIME_OPTS]) == $this->Elements[DIME_OTPS_LEN]);
- if (!$this->_haveOpts) return NULL;
- $p += $this->OPTS_LENGTH-$have;
- }
- if (!$this->_haveID) {
- $have = strlen($this->Elements[NET_DIME_ID]);
- $this->Elements[NET_DIME_ID] .= substr($data,$p,$this->Elements[NET_DIME_ID_LEN]-$have);
- $this->_haveID = (strlen($this->Elements[NET_DIME_ID]) == $this->Elements[NET_DIME_ID_LEN]);
- if (!$this->_haveID) return NULL;
- $p += $this->ID_LENGTH-$have;
- }
- if (!$this->_haveType && $p < $datalen) {
- $have = strlen($this->Elements[NET_DIME_TYPE]);
- $this->Elements[NET_DIME_TYPE] .= substr($data,$p,$this->Elements[NET_DIME_TYPE_LEN]-$have);
- $this->_haveType = (strlen($this->Elements[NET_DIME_TYPE]) == $this->Elements[NET_DIME_TYPE_LEN]);
- if (!$this->_haveType) return NULL;
- $p += $this->TYPE_LENGTH-$have;
- }
- if (!$this->_haveData && $p < $datalen) {
- $have = strlen($this->Elements[NET_DIME_DATA]);
- $this->Elements[NET_DIME_DATA] .= substr($data,$p,$this->Elements[NET_DIME_DATA_LEN]-$have);
- $this->_haveData = (strlen($this->Elements[NET_DIME_DATA]) == $this->Elements[NET_DIME_DATA_LEN]);
- if (!$this->_haveData) return NULL;
- $p += $this->DATA_LENGTH-$have;
- }
- return substr($data,$p);
- }
- }
-
-
- class Net_DIME_Message extends PEAR
- {
-
- var $record_size = 4096;
- #var $records =array();
- var $parts = array();
- var $currentPart = -1;
- var $stream = NULL;
- var $_currentRecord;
- var $_proc = array();
- var $type;
- var $typestr;
- var $mb = 1;
- var $me = 0;
- var $cf = 0;
- var $id = NULL;
- var $debug = FALSE;
- /**
- * constructor
- *
- * this currently takes a file pointer as provided
- * by fopen
- *
- * TODO: integrate with the php streams stuff
- */
- function Net_DIME_Message($stream=NULL, $record_size = 4096, $debug = FALSE)
- {
- $this->stream = $stream;
- $this->record_size = $record_size;
- $this->debug = $debug;
- }
-
- function _makeRecord(&$data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN)
- {
- $record = new Net_DIME_Record($this->debug);
- if ($this->mb) {
- $record->setMB();
- // all subsequent records are not message begin!
- $this->mb = 0;
- }
- if ($this->me) $record->setME();
- if ($this->cf) $record->setCF();
- $record->setData($data);
- $record->setType($typestr,$type);
- if ($id) $record->setID($id);
- #if ($this->debug) {
- # print str_replace('\0','*',$record->encode());
- #}
- return $record->encode();
- }
-
- function startChunk(&$data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN)
- {
- $this->me = 0;
- $this->cf = 1;
- $this->type = $type;
- $this->typestr = $typestr;
- if ($id) {
- $this->id = $id;
- } else {
- $this->id = md5(time());
- }
- return $this->_makeRecord($data, $this->typestr, $this->id, $this->type);
- }
-
- function doChunk(&$data)
- {
- $this->me = 0;
- $this->cf = 1;
- return $this->_makeRecord($data, NULL, NULL, NET_DIME_TYPE_UNCHANGED);
- }
-
- function endChunk()
- {
- $this->cf = 0;
- $data = NULL;
- $rec = $this->_makeRecord($data, NULL, NULL, NET_DIME_TYPE_UNCHANGED);
- $this->id = 0;
- $this->cf = 0;
- $this->id = 0;
- $this->type = NET_DIME_TYPE_UNKNOWN;
- $this->typestr = NULL;
- return $rec;
- }
-
- function endMessage()
- {
- $this->me = 1;
- $data = NULL;
- $rec = $this->_makeRecord($data, NULL, NULL, NET_DIME_TYPE_NONE);
- $this->me = 0;
- $this->mb = 1;
- $this->id = 0;
- return $rec;
- }
-
- /**
- * sendRecord
- *
- * given a chunk of data, it creates DIME records
- * and writes them to the stream
- *
- */
- function sendData(&$data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN)
- {
- $len = strlen($data);
- if ($len > $this->record_size) {
- $chunk = substr($data, 0, $this->record_size);
- $p = $this->record_size;
- $rec = $this->startChunk($chunk,$typestr,$id,$type);
- fwrite($this->stream, $rec);
- while ($p < $len) {
- $chunk = substr($data, $p, $this->record_size);
- $p += $this->record_size;
- $rec = $this->doChunk($chunk);
- fwrite($this->stream, $rec);
- }
- $rec = $this->endChunk();
- fwrite($this->stream, $rec);
- return;
- }
- $rec = $this->_makeRecord($data, $typestr,$id,$type);
- fwrite($this->stream, $rec);
- }
-
- function sendEndMessage()
- {
- $rec = $this->endMessage();
- fwrite($this->stream, $rec);
- }
-
- /**
- * sendFile
- *
- * given a filename, it reads the file,
- * creates records and writes them to the stream
- *
- */
- function sendFile($filename, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN)
- {
- $f = fopen($filename, "rb");
- if ($f) {
- if ($data = fread($f, $this->record_size)) {
- $this->startChunk($data,$typestr,$id,$type);
- }
- while ($data = fread($f, $this->record_size)) {
- $this->doChunk($data,$typestr,$id,$type);
- }
- $this->endChunk();
- fclose($f);
- }
- }
-
- /**
- * encodeData
- *
- * given data, encode it in DIME
- *
- */
- function encodeData($data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN)
- {
- $len = strlen($data);
- $resp = '';
- if ($len > $this->record_size) {
- $chunk = substr($data, 0, $this->record_size);
- $p = $this->record_size;
- $resp .= $this->startChunk($chunk,$typestr,$id,$type);
- while ($p < $len) {
- $chunk = substr($data, $p, $this->record_size);
- $p += $this->record_size;
- $resp .= $this->doChunk($chunk);
- }
- $resp .= $this->endChunk();
- } else {
- $resp .= $this->_makeRecord($data, $typestr,$id,$type);
- }
- return $resp;
- }
-
- /**
- * sendFile
- *
- * given a filename, it reads the file,
- * creates records and writes them to the stream
- *
- */
- function encodeFile($filename, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN)
- {
- $f = fopen($filename, "rb");
- if ($f) {
- if ($data = fread($f, $this->record_size)) {
- $resp = $this->startChunk($data,$typestr,$id,$type);
- }
- while ($data = fread($f, $this->record_size)) {
- $resp = $this->doChunk($data,$typestr,$id,$type);
- }
- $resp = $this->endChunk();
- fclose($f);
- }
- return $resp;
- }
-
- /**
- * _processData
- *
- * creates Net_DIME_Records from provided data
- *
- */
- function _processData(&$data)
- {
- $leftover = NULL;
- if (!$this->_currentRecord) {
- $this->_currentRecord = new Net_DIME_Record($this->debug);
- $data = $this->_currentRecord->decode($data);
- } else {
- $data = $this->_currentRecord->addData($data);
- }
-
- if ($this->_currentRecord->_haveData) {
- if (count($this->parts)==0 && !$this->_currentRecord->isStart()) {
- // raise an error!
- return PEAR::raiseError('First Message is not a DIME begin record!');
- }
-
- if ($this->_currentRecord->isEnd() && $this->_currentRecord->getDataLength()==0) {
- return NULL;
- }
-
- if ($this->currentPart < 0 && !$this->_currentRecord->isChunk()) {
- $this->parts[] = array();
- $this->currentPart = count($this->parts)-1;
- $this->parts[$this->currentPart]['id'] = $this->_currentRecord->getID();
- $this->parts[$this->currentPart]['type'] = $this->_currentRecord->getType();
- $this->parts[$this->currentPart]['data'] = $this->_currentRecord->getData();
- $this->currentPart = -1;
- } else {
- if ($this->currentPart < 0) {
- $this->parts[] = array();
- $this->currentPart = count($this->parts)-1;
- $this->parts[$this->currentPart]['id'] = $this->_currentRecord->getID();
- $this->parts[$this->currentPart]['type'] = $this->_currentRecord->getType();
- $this->parts[$this->currentPart]['data'] = $this->_currentRecord->getData();
- } else {
- $this->parts[$this->currentPart]['data'] .= $this->_currentRecord->getData();
- if (!$this->_currentRecord->isChunk()) {
- // we reached the end of the chunk
- $this->currentPart = -1;
- }
- }
- }
- #$this->records[] = $this->_currentRecord;
- if (!$this->_currentRecord->isEnd()) $this->_currentRecord = NULL;
- }
- return NULL;
- }
-
- /**
- * decodeData
- *
- * decodes a DIME encrypted string of data
- *
- */
- function decodeData(&$data) {
- while (strlen($data) >= NET_DIME_RECORD_HEADER) {
- $err = $this->_processData($data);
- if (PEAR::isError($err)) {
- return $err;
- }
- }
- }
-
- /**
- * read
- *
- * reads the stream and creates
- * an array of records
- *
- * it can accept the start of a previously read buffer
- * this is usefull in situations where you need to read
- * headers before discovering that the data is DIME encoded
- * such as in the case of reading an HTTP response.
- */
- function read($buf=NULL)
- {
- while ($data = fread($this->stream, 8192)) {
- if ($buf) {
- $data = $buf.$data;
- $buf = NULL;
- }
- if ($this->debug)
- echo "read: ".strlen($data)." bytes\n";
- $err = $this->decodeData($data);
- if (PEAR::isError($err)) {
- return $err;
- }
-
- // store any leftover data to be used again
- // should be < NET_DIME_RECORD_HEADER bytes
- $buf = $data;
- }
- if (!$this->_currentRecord || !$this->_currentRecord->isEnd()) {
- return PEAR::raiseError('reached stream end without end record');
- }
- return NULL;
- }
- }
- ?>
|