Commit 63ba8bea authored by Sebastian Kummer's avatar Sebastian Kummer

Merge pull request #111 in ZP/z-push from feature/ZP-797-rework-wbxml-handling to develop

* commit '52162fde':
  ZP-797 Fixed WBXML class constant.
  ZP-797 GetAttachment use fpassthru (more efficient).
  ZP-797 WBXMLDecoder is handling ActiveSync WBXML, which is only a subset of WBXML.
  ZP-797 WBXML{En,De}coder: use const instead of define for WBXML_* constants.
  ZP-797 WBXMLDecoder->getOpaque() use stream_get_contents().
  ZP-797 WBXMLDecoder->GetPlainInputStream() use stream_get_contents().
  ZP-797 WBXMLDecoder->_getToken() speedup.
  ZP-797 WBXMLDecoder->getTermStr(): use stream_get_line().
  ZP-797 WBXMLEncoder remove possible endless loop/improve multipart handling.
  ZP-797 WBXMLEncoder remove _outlog / use output buffering.
  ZP-797 WBXMLDecoder remove inlog / reread php://input.
parents f49acb93 52162fde
......@@ -66,20 +66,13 @@ class GetAttachment extends RequestProcessor {
throw new StatusException(sprintf("HandleGetAttachment(): No stream resource returned by backend for attachment: %s", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
header("Content-Type: application/octet-stream");
$l = 0;
while (!feof($stream)) {
$d = fgets($stream, 4096);
$l += strlen($d);
echo $d;
// announce an update every 100K
if (($l/1024) % 100 == 0)
self::$topCollector->AnnounceInformation(sprintf("Streaming attachment: %d KB sent", round($l/1024)));
}
self::$topCollector->AnnounceInformation("Starting attachment streaming", true);
$l = fpassthru($stream);
fclose($stream);
self::$topCollector->AnnounceInformation(sprintf("Streamed %d KB attachment", $l/1024), true);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleGetAttachment(): attachment with %d KB sent to mobile", $l/1024));
if ($l === false)
throw new FatalException("HandleGetAttachment(): fpassthru === false !!!");
self::$topCollector->AnnounceInformation(sprintf("Streamed %d KB attachment", round($l/1024)), true);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleGetAttachment(): attachment with %d KB sent to mobile", round($l/1024)));
}
catch (StatusException $s) {
// StatusException already logged so we just need to pass it upwards to send a HTTP error
......
......@@ -630,4 +630,19 @@ class Request {
return ($re) ? preg_replace($re, $replacevalue, $input) : '';
}
/**
* Returns base64 encoded "php://input"
* With POST request (our case), you can open and read
* multiple times "php://input"
*
* @access public
* @return string - base64 encoded wbxml
*/
public static function GetInputAsBase64() {
$input = fopen('php://input', 'r');
$wbxml = base64_encode(stream_get_contents($input));
fclose($input);
return $wbxml;
}
}
......@@ -129,17 +129,12 @@ abstract class RequestProcessor {
// if there is an error decoding wbxml, consume remaining data and include it in the WBXMLException
if (!$handler->Handle(Request::GetCommandCode())) {
$wbxmlLog = "no decoder";
if (self::$decoder) {
self::$decoder->readRemainingData();
$wbxmlLog = self::$decoder->getWBXMLLog();
}
throw new WBXMLException("Debug data: " . $wbxmlLog);
throw new WBXMLException("Debug data: " . Request::GetInputAsBase64());
}
// also log WBXML in happy case
if (self::$decoder && @constant('WBXML_DEBUG') === true) {
ZLog::Write(LOGLEVEL_WBXML, "WBXML-IN : ". self::$decoder->getWBXMLLog(), false);
if (@constant('WBXML_DEBUG') === true) {
ZLog::Write(LOGLEVEL_WBXML, "WBXML-IN : ". Request::GetInputAsBase64(), false);
}
}
......
This diff is collapsed.
......@@ -42,26 +42,7 @@
************************************************/
define('WBXML_SWITCH_PAGE', 0x00);
define('WBXML_END', 0x01);
define('WBXML_ENTITY', 0x02);
define('WBXML_STR_I', 0x03);
define('WBXML_LITERAL', 0x04);
define('WBXML_EXT_I_0', 0x40);
define('WBXML_EXT_I_1', 0x41);
define('WBXML_EXT_I_2', 0x42);
define('WBXML_PI', 0x43);
define('WBXML_LITERAL_C', 0x44);
define('WBXML_EXT_T_0', 0x80);
define('WBXML_EXT_T_1', 0x81);
define('WBXML_EXT_T_2', 0x82);
define('WBXML_STR_T', 0x83);
define('WBXML_LITERAL_A', 0x84);
define('WBXML_EXT_0', 0xC0);
define('WBXML_EXT_1', 0xC1);
define('WBXML_EXT_2', 0xC2);
define('WBXML_OPAQUE', 0xC3);
define('WBXML_LITERAL_AC', 0xC4);
define('EN_TYPE', 1);
define('EN_TAG', 2);
......@@ -77,6 +58,31 @@ define('EN_FLAGS_CONTENT', 1);
define('EN_FLAGS_ATTRIBUTES', 2);
class WBXMLDefs {
const WBXML_SWITCH_PAGE = 0x00;
const WBXML_END = 0x01;
const WBXML_ENTITY = 0x02; //not used in ActiveSync
const WBXML_STR_I = 0x03;
const WBXML_LITERAL = 0x04; //not used in ActiveSync
const WBXML_EXT_I_0 = 0x40; //not used in ActiveSync
const WBXML_EXT_I_1 = 0x41; //not used in ActiveSync
const WBXML_EXT_I_2 = 0x42; //not used in ActiveSync
const WBXML_PI = 0x43; //not used in ActiveSync
const WBXML_LITERAL_C = 0x44; //not used in ActiveSync
const WBXML_EXT_T_0 = 0x80; //not used in ActiveSync
const WBXML_EXT_T_1 = 0x81; //not used in ActiveSync
const WBXML_EXT_T_2 = 0x82; //not used in ActiveSync
const WBXML_STR_T = 0x83; //not used in ActiveSync
const WBXML_LITERAL_A = 0x84; //not used in ActiveSync
const WBXML_EXT_0 = 0xC0; //not used in ActiveSync
const WBXML_EXT_1 = 0xC1; //not used in ActiveSync
const WBXML_EXT_2 = 0xC2; //not used in ActiveSync
const WBXML_OPAQUE = 0xC3;
const WBXML_LITERAL_AC = 0xC4; //not used in ActiveSync
const WBXML_WITH_ATTRIBUTES = 0x80; //not used in ActiveSync
const WBXML_WITH_CONTENT = 0x40;
/**
* The WBXML DTDs
*/
......
......@@ -45,11 +45,10 @@
class WBXMLEncoder extends WBXMLDefs {
private $_dtd;
private $_out;
private $_outLog;
private $_tagcp;
private $_attrcp;
private $_tagcp = 0;
private $log = false;
private $logStack = array();
// We use a delayed output mechanism in which we only output a tag when it actually has something
......@@ -64,14 +63,9 @@ class WBXMLEncoder extends WBXMLDefs {
private $bodyparts;
public function WBXMLEncoder($output, $multipart = false) {
// make sure WBXML_DEBUG is defined. It should be at this point
if (!defined('WBXML_DEBUG')) define('WBXML_DEBUG', false);
$this->log = @constant('WBXML_DEBUG') === true;
$this->_out = $output;
$this->_outLog = StringStreamWrapper::Open("");
$this->_tagcp = 0;
$this->_attrcp = 0;
// reverse-map the DTD
foreach($this->dtd["namespaces"] as $nsid => $nsname) {
......@@ -126,7 +120,6 @@ class WBXMLEncoder extends WBXMLDefs {
if(!$nocontent) {
$stackelem['tag'] = $tag;
$stackelem['attributes'] = $attributes;
$stackelem['nocontent'] = $nocontent;
$stackelem['sent'] = false;
......@@ -136,7 +129,7 @@ class WBXMLEncoder extends WBXMLDefs {
// output of an empty tag, and we therefore output the stack here
} else {
$this->_outputStack();
$this->_startTag($tag, $attributes, $nocontent);
$this->_startTag($tag, $nocontent);
}
}
......@@ -225,6 +218,8 @@ class WBXMLEncoder extends WBXMLDefs {
* @return void
*/
public function addBodypartStream($bp) {
if (!is_resource($bp))
throw new WBXMLException("WBXMLEncoder->addBodypartStream(): trying to add a ".gettype($bp)." instead of a stream");
if ($this->multipart)
$this->bodyparts[] = $bp;
}
......@@ -252,7 +247,7 @@ class WBXMLEncoder extends WBXMLDefs {
private function _outputStack() {
for($i=0;$i<count($this->_stack);$i++) {
if(!$this->_stack[$i]['sent']) {
$this->_startTag($this->_stack[$i]['tag'], $this->_stack[$i]['attributes'], $this->_stack[$i]['nocontent']);
$this->_startTag($this->_stack[$i]['tag'], $this->_stack[$i]['nocontent']);
$this->_stack[$i]['sent'] = true;
}
}
......@@ -264,8 +259,9 @@ class WBXMLEncoder extends WBXMLDefs {
* @access private
* @return
*/
private function _startTag($tag, $attributes = false, $nocontent = false) {
$this->logStartTag($tag, $attributes, $nocontent);
private function _startTag($tag, $nocontent = false) {
if ($this->log)
$this->logStartTag($tag, $nocontent);
$mapping = $this->getMapping($tag);
......@@ -278,17 +274,11 @@ class WBXMLEncoder extends WBXMLDefs {
}
$code = $mapping["code"];
if(isset($attributes) && is_array($attributes) && count($attributes) > 0) {
$code |= 0x80;
}
if(!isset($nocontent) || !$nocontent)
$code |= 0x40;
$this->outByte($code);
if($code & 0x80)
$this->outAttributes($attributes);
}
/**
......@@ -299,8 +289,9 @@ class WBXMLEncoder extends WBXMLDefs {
* @return
*/
private function _content($content) {
$this->logContent($content);
$this->outByte(WBXML_STR_I);
if ($this->log)
$this->logContent($content);
$this->outByte(self::WBXML_STR_I);
$this->outTermStr($content);
}
......@@ -314,7 +305,7 @@ class WBXMLEncoder extends WBXMLDefs {
*/
private function _contentStream($stream, $asBase64) {
// write full stream, including the finalizing terminator to the output stream (stuff outTermStr() would do)
$this->outByte(WBXML_STR_I);
$this->outByte(self::WBXML_STR_I);
fseek($stream, 0, SEEK_SET);
if ($asBase64) {
$out_filter = stream_filter_append($this->_out, 'convert.base64-encode');
......@@ -325,14 +316,11 @@ class WBXMLEncoder extends WBXMLDefs {
}
fwrite($this->_out, chr(0));
// data is out, do some logging
$stat = fstat($stream);
$logContent = sprintf("<<< written %d of %d bytes of %s data >>>", $written, $stat['size'], $asBase64 ? "base64 encoded":"plain");
$this->logContent($logContent);
// write the meta data also to the _outLog stream, WBXML_STR_I was already written by outByte() above
fwrite($this->_outLog, $logContent);
fwrite($this->_outLog, chr(0));
if ($this->log) {
// data is out, do some logging
$stat = fstat($stream);
$this->logContent(sprintf("<<< written %d of %d bytes of %s data >>>", $written, $stat['size'], $asBase64 ? "base64 encoded":"plain"));
}
}
/**
......@@ -342,8 +330,9 @@ class WBXMLEncoder extends WBXMLDefs {
* @return
*/
private function _endTag() {
$this->logEndTag();
$this->outByte(WBXML_END);
if ($this->log)
$this->logEndTag();
$this->outByte(self::WBXML_END);
}
/**
......@@ -356,7 +345,6 @@ class WBXMLEncoder extends WBXMLDefs {
*/
private function outByte($byte) {
fwrite($this->_out, chr($byte));
fwrite($this->_outLog, chr($byte));
}
/**
......@@ -391,28 +379,6 @@ class WBXMLEncoder extends WBXMLDefs {
private function outTermStr($content) {
fwrite($this->_out, $content);
fwrite($this->_out, chr(0));
// truncate data bigger than 10 KB on outLog
$l = strlen($content);
if ($l > 10240) {
$content = substr($content, 0, 5120) . sprintf("...<data with total %d bytes truncated>...", $l) . substr($content, -5120);
}
fwrite($this->_outLog, $content);
fwrite($this->_outLog, chr(0));
}
/**
* Output attributes
* We don't actually support this, because to do so, we would have
* to build a string table before sending the data (but we can't
* because we're streaming), so we'll just send an END, which just
* terminates the attribute list with 0 attributes.
*
* @access private
* @return
*/
private function outAttributes() {
$this->outByte(WBXML_END);
}
/**
......@@ -424,7 +390,7 @@ class WBXMLEncoder extends WBXMLDefs {
* @return
*/
private function outSwitchPage($page) {
$this->outByte(WBXML_SWITCH_PAGE);
$this->outByte(self::WBXML_SWITCH_PAGE);
$this->outByte($page);
}
......@@ -488,16 +454,12 @@ class WBXMLEncoder extends WBXMLDefs {
* Logs a StartTag to ZLog
*
* @param $tag
* @param $attr
* @param $nocontent
*
* @access private
* @return
*/
private function logStartTag($tag, $attr, $nocontent) {
if(!WBXML_DEBUG)
return;
private function logStartTag($tag, $nocontent) {
$spaces = str_repeat(" ", count($this->logStack));
if($nocontent)
ZLog::Write(LOGLEVEL_WBXML,"O " . $spaces . " <$tag/>");
......@@ -514,9 +476,6 @@ class WBXMLEncoder extends WBXMLDefs {
* @return
*/
private function logEndTag() {
if(!WBXML_DEBUG)
return;
$spaces = str_repeat(" ", count($this->logStack));
$tag = array_pop($this->logStack);
ZLog::Write(LOGLEVEL_WBXML,"O " . $spaces . "</$tag>");
......@@ -531,9 +490,6 @@ class WBXMLEncoder extends WBXMLDefs {
* @return
*/
private function logContent($content) {
if(!WBXML_DEBUG)
return;
$spaces = str_repeat(" ", count($this->logStack));
ZLog::Write(LOGLEVEL_WBXML,"O " . $spaces . $content);
}
......@@ -551,28 +507,20 @@ class WBXMLEncoder extends WBXMLDefs {
$nrBodyparts = $this->getBodypartsCount();
$blockstart = (($nrBodyparts + 1) * 2) * 4 + 4;
$data = pack("iii", ($nrBodyparts + 1), $blockstart, $len);
ob_start(null, 1048576);
fwrite($this->_out, pack("iii", ($nrBodyparts + 1), $blockstart, $len));
foreach ($this->bodyparts as $bp) {
$blockstart = $blockstart + $len;
$len = fstat($bp);
$len = (isset($len['size'])) ? $len['size'] : 0;
$data .= pack("ii", $blockstart, $len);
fwrite($this->_out, pack("ii", $blockstart, $len));
}
fwrite($this->_out, $data);
fwrite($this->_out, $buffer);
fwrite($this->_outLog, $data);
fwrite($this->_outLog, $buffer);
foreach($this->bodyparts as $bp) {
while (!feof($bp)) {
$out = fread($bp, 4096);
fwrite($this->_out, $out);
fwrite($this->_outLog, $out);
}
stream_copy_to_stream($bp, $this->_out);
fclose($bp);
}
}
......@@ -583,11 +531,11 @@ class WBXMLEncoder extends WBXMLDefs {
* @return void
*/
private function writeLog() {
$stat = fstat($this->_outLog);
if ($stat['size'] < 524288) {
$data = base64_encode(stream_get_contents($this->_outLog, -1,0));
}
else {
if (ob_get_length() === false) {
$data = "output buffer disabled";
} elseif (ob_get_length() < 524288) {
$data = base64_encode(ob_get_contents());
} else {
$data = "more than 512K of data";
}
ZLog::Write(LOGLEVEL_WBXML, "WBXML-OUT: ". $data, false);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment