Page MenuHomePhorge

No OneTemporary

Size
60 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/ext/Syncroton/Wbxml/Encoder.php b/lib/ext/Syncroton/Wbxml/Encoder.php
index 9e88db2..706ae22 100644
--- a/lib/ext/Syncroton/Wbxml/Encoder.php
+++ b/lib/ext/Syncroton/Wbxml/Encoder.php
@@ -1,372 +1,435 @@
<?php
/**
* Syncroton
*
* @package Wbxml
* @subpackage Wbxml
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2009 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
* @version $Id:Encoder.php 4968 2008-10-17 09:09:33Z l.kneschke@metaways.de $
*/
/**
* class to convert XML to WBXML
*
* @package Wbxml
* @subpackage Wbxml
*/
class Syncroton_Wbxml_Encoder extends Syncroton_Wbxml_Abstract
{
/**
* stack of dtd objects
*
* @var array
*/
protected $_dtdStack = array();
/**
* stack of stream resources
*
* @var array
*/
protected $_streamStack = array();
/**
* stack of levels when to pop data from the other stacks
*
* @var array
*/
protected $_popStack = array();
/**
* count level of tags
*
* @var string
*/
protected $_level = 0;
/**
* when to take data next time from the different stacks
*
* @var unknown_type
*/
protected $_nextStackPop = NULL;
/**
* collect data trough different calls to _handleCharacters
*
* @var string
*/
protected $_currentTagData = NULL;
/**
* the current tag as read by the parser
*
* @var string
*/
protected $_currentTag = NULL;
/**
* the constructor
*
* @param resource $_stream
* @param string $_charSet
* @param integer $_version
*/
public function __construct($_stream, $_charSet = 'UTF-8', $_version = 2)
{
$this->_stream = $_stream;
$this->_charSet = $_charSet;
$this->_version = $_version;
}
/**
* initialize internal variables and write wbxml header to stream
*
* @param string $_urn
* @todo check if dpi > 0, instead checking the urn
*/
protected function _initialize($_dom)
{
$this->_dtd = Syncroton_Wbxml_Dtd_Factory::factory($_dom->doctype->name);
$this->_codePage = $this->_dtd->getCurrentCodePage();
// the WBXML version
$this->_writeByte($this->_version);
if($this->_codePage->getDPI() === NULL) {
// the document public identifier
$this->_writeMultibyteUInt(1);
} else {
// the document public identifier
// defined in string table
$this->_writeMultibyteUInt(0);
// the offset of the DPI in the string table
$this->_writeByte(0);
}
// write the charSet
$this->_writeCharSet($this->_charSet);
if($this->_codePage->getDPI() === NULL) {
// the length of the string table
$this->_writeMultibyteUInt(0);
} else {
// the length of the string table
$this->_writeMultibyteUInt(strlen($this->_codePage->getDPI()));
// the dpi
$this->_writeString($this->_codePage->getDPI());
}
}
/**
* write charset to stream
*
* @param string $_charSet
* @todo add charset lookup table. currently only utf-8 is supported
*/
protected function _writeCharSet($_charSet)
{
switch(strtoupper($_charSet)) {
case 'UTF-8':
$this->_writeMultibyteUInt(106);
break;
default:
throw new Syncroton_Wbxml_Exception('unsuported charSet ' . strtoupper($_charSet));
break;
}
}
/**
* start encoding of xml to wbxml
*
* @param string $_xml the xml string
* @return resource stream
*/
public function encode(DOMDocument $_dom)
{
$_dom->formatOutput = false;
$tempStream = tmpfile();
$meta_data = stream_get_meta_data($tempStream);
$filename = $meta_data["uri"];
$_dom->save($filename);
rewind($tempStream);
$this->_initialize($_dom);
-
- $parser = xml_parser_create_ns($this->_charSet, ';');
- xml_set_object($parser, $this);
- xml_set_element_handler($parser, '_handleStartTag', '_handleEndTag');
- xml_set_character_data_handler($parser, '_handleCharacters');
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
-
- while (!feof($tempStream)) {
- if (!xml_parse($parser, fread($tempStream, 1048576), feof($tempStream))) {
- // uncomment to write xml document to file
- #rewind($tempStream);
- #$xmlStream = fopen(tempnam(sys_get_temp_dir(), "xmlerrors"), 'r+');
- #stream_copy_to_stream($tempStream, $xmlStream);
- #fclose($xmlStream);
+
+ if (false) {
+ $parser = xml_parser_create_ns($this->_charSet, ';');
+ xml_set_object($parser, $this);
+ xml_set_element_handler($parser, '_handleStartTag', '_handleEndTag');
+ xml_set_character_data_handler($parser, '_handleCharacters');
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
+ xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
+
+ while (!feof($tempStream)) {
+ if (!xml_parse($parser, fread($tempStream, 1048576), feof($tempStream))) {
+ // uncomment to write xml document to file
+ #rewind($tempStream);
+ #$xmlStream = fopen(tempnam(sys_get_temp_dir(), "xmlerrors"), 'r+');
+ #stream_copy_to_stream($tempStream, $xmlStream);
+ #fclose($xmlStream);
+
+ throw new Syncroton_Wbxml_Exception(sprintf('XML error: %s at line %d',
+ xml_error_string(xml_get_error_code($parser)),
+ xml_get_current_line_number($parser)
+ ));
+ }
+ }
+
+ fclose($tempStream);
+ xml_parser_free($parser);
+ } else {
+ $this->_traverseDom($_dom);
+ }
+
- throw new Syncroton_Wbxml_Exception(sprintf('XML error: %s at line %d',
- xml_error_string(xml_get_error_code($parser)),
- xml_get_current_line_number($parser)
- ));
+ }
+
+ private function getAttributes($node)
+ {
+ $attributes = array();
+ if ($node->attributes) {
+ for ($i = 0; $i < $node->attributes->length; ++$i) {
+ $attributes[$node->attributes->item($i)->name] = $node->attributes->item($i)->value;
}
}
+ return $attributes;
+ }
+
+ private function writeNode($node, $withContent = false, $data = null) {
+ if($this->_codePage->getNameSpace() != $node->namespaceURI) {
+ $this->_switchCodePage($node->namespaceURI);
+ }
+ $this->_writeTag($node->localName, $this->getAttributes($node), $withContent, $data);
+ }
+
+ protected function _traverseDom($_dom)
+ {
+ if ($_dom->childNodes->length == 0) {
+ return false;
+ }
+ // print(str_pad("", $this->_level, " ") . "traversing {$_dom->nodeName}" . "\n");
+ $this->_level++;
+ $prevNode = $_dom;
+ $foundElementNode = false;
+ foreach ($_dom->childNodes as $node) {
+ if ($node->nodeType == XML_ELEMENT_NODE) {
+ $foundElementNode = true;
+ if ($prevNode && $this->_level > 1) {
+ // print(str_pad("", $this->_level, " ") . "{$node->nodeName} creating parent {$prevNode->nodeName}" . "\n");
+ $this->writeNode($prevNode, true);
+ $prevNode = null;
+ }
+ if (!$this->_traverseDom($node)) {
+ // print(str_pad("", $this->_level, " ") . "{$node->nodeName} content {$node->nodeValue}" . "\n");
+ $data = $node->nodeValue;
+ if (strlen($data) == 0) {
+ $this->writeNode($node);
+ } else {
+ $this->writeNode($node, true, $data);
+ $this->_writeByte(Syncroton_Wbxml_Abstract::END);
+ // print("Closing tag after writing tag\n");
+ }
+ } else {
+ $this->_writeByte(Syncroton_Wbxml_Abstract::END);
+ // print("Closing tag\n");
+ }
+ }
+ }
+ $this->_level--;
- fclose($tempStream);
- xml_parser_free($parser);
+ return $foundElementNode;
}
/**
* get's called by xml parser when tag starts
*
* @param resource $_parser
* @param string $_tag current tag prefixed with namespace
* @param array $_attributes list of tag attributes
*/
protected function _handleStartTag($_parser, $_tag, $_attributes)
{
$this->_level++;
$this->_currentTagData = null;
// write data for previous tag happens whith <tag1><tag2>
if($this->_currentTag !== NULL) {
$this->_writeTag($this->_currentTag, $this->_attributes, true);
}
list($nameSpace, $this->_currentTag) = explode(';', $_tag);
if($this->_codePage->getNameSpace() != $nameSpace) {
$this->_switchCodePage($nameSpace);
}
$this->_attributes = $_attributes;
}
/**
* strip uri: from nameSpace
*
* @param unknown_type $_nameSpace
* @return unknown
*/
protected function _stripNameSpace($_nameSpace)
{
return substr($_nameSpace, 4);
}
/**
* get's called by xml parser when tag ends
*
* @param resource $_parser
* @param string $_tag current tag prefixed with namespace
*/
protected function _handleEndTag($_parser, $_tag)
{
#echo "$_tag Level: $this->_level == $this->_nextStackPop \n";
if($this->_nextStackPop !== NULL && $this->_nextStackPop == $this->_level) {
#echo "TAG: $_tag\n";
$this->_writeByte(Syncroton_Wbxml_Abstract::END);
$subStream = $this->_stream;
$subStreamLength = ftell($subStream);
$this->_dtd = array_pop($this->_dtdStack);
$this->_stream = array_pop($this->_streamStack);
$this->_nextStackPop = array_pop($this->_popStack);
$this->_codePage = $this->_dtd->getCurrentCodePage();
rewind($subStream);
#while (!feof($subStream)) {$buffer = fgets($subStream, 4096);echo $buffer;}
$this->_writeByte(Syncroton_Wbxml_Abstract::OPAQUE);
$this->_writeMultibyteUInt($subStreamLength);
$writenBytes = stream_copy_to_stream($subStream, $this->_stream);
if($writenBytes !== $subStreamLength) {
//echo "$writenBytes !== $subStreamLength\n";
throw new Syncroton_Wbxml_Exception('blow');
}
fclose($subStream);
#echo "$this->_nextStackPop \n"; exit;
} else {
if ($this->_currentTag !== NULL && $this->_currentTagData !== NULL) {
$this->_writeTag($this->_currentTag, $this->_attributes, true, $this->_currentTagData);
$this->_writeByte(Syncroton_Wbxml_Abstract::END);
} elseif ($this->_currentTag !== NULL && $this->_currentTagData === NULL) {
// for example <UTC/> tag with no data, jumps directly from _handleStartTag to _handleEndTag
$this->_writeTag($this->_currentTag, $this->_attributes);
// no end tag required, tag has no content
} else {
$this->_writeByte(Syncroton_Wbxml_Abstract::END);
}
}
#list($urn, $tag) = explode(';', $_tag); echo "</$tag> ($this->_level)\n";
// reset $this->_currentTag, as tag got writen to stream already
$this->_currentTag = NULL;
$this->_level--;
}
/**
* collects data(value) of tag
* can be called multiple lines if the value contains linebreaks
*
* @param resource $_parser the xml parser
* @param string $_data the data(value) of the tag
*/
protected function _handleCharacters($_parser, $_data)
{
$this->_currentTagData .= $_data;
}
/**
* writes tag with data to stream
*
* @param string $_tag
* @param array $_attributes
* @param bool $_hasContent
* @param string $_data
*/
protected function _writeTag($_tag, $_attributes=NULL, $_hasContent=false, $_data=NULL)
{
if($_hasContent == false && $_data !== NULL) {
throw new Syncroton_Wbxml_Exception('$_hasContent can not be false, when $_data !== NULL');
}
// handle the tag
$identity = $this->_codePage->getIdentity($_tag);
if (is_array($_attributes) && isset($_attributes['uri:Syncroton;encoding'])) {
$encoding = 'opaque';
unset($_attributes['uri:Syncroton;encoding']);
} else {
$encoding = 'termstring';
}
if(!empty($_attributes)) {
$identity |= 0x80;
}
if($_hasContent == true) {
$identity |= 0x40;
}
$this->_writeByte($identity);
// handle the data
if($_data !== NULL) {
if ($encoding == 'opaque') {
$this->_writeOpaqueString(base64_decode($_data));
} else {
$this->_writeTerminatedString($_data);
}
}
$this->_currentTagData = NULL;
}
/**
* switch code page
*
* @param string $_urn
*/
protected function _switchCodePage($_nameSpace)
{
try {
$codePageName = $this->_stripNameSpace($_nameSpace);
if(!defined('Syncroton_Wbxml_Dtd_ActiveSync::CODEPAGE_'. strtoupper($codePageName))) {
throw new Syncroton_Wbxml_Exception('codepage ' . $codePageName . ' not found');
}
// switch to another codepage
// no need to write the wbxml header again
$codePageId = constant('Syncroton_Wbxml_Dtd_ActiveSync::CODEPAGE_'. strtoupper($codePageName));
$this->_codePage = $this->_dtd->switchCodePage($codePageId);
$this->_writeByte(Syncroton_Wbxml_Abstract::SWITCH_PAGE);
$this->_writeByte($codePageId);
} catch (Syncroton_Wbxml_Dtd_Exception_CodePageNotFound $e) {
// switch to another dtd
// need to write the wbxml header again
// put old dtd and stream on stack
$this->_dtdStack[] = $this->_dtd;
$this->_streamStack[] = $this->_stream;
$this->_popStack[] = $this->_nextStackPop;
$this->_nextStackPop = $this->_level;
$this->_stream = fopen("php://temp", 'r+');
$this->_initialize($_urn);
}
}
}
\ No newline at end of file
diff --git a/tests/wbxml.php b/tests/wbxml.php
new file mode 100644
index 0000000..29a5c37
--- /dev/null
+++ b/tests/wbxml.php
@@ -0,0 +1,888 @@
+<?php
+
+class message extends PHPUnit\Framework\TestCase
+{
+ //function testDecode()
+ //{
+ // //TODO input some wbxml document
+ // //
+ // $dom = new DOMDocument();
+ // $dom->loadXML($lastSyncCollection['lastXML']);
+ // //
+ // try {
+ // $decoder = new Syncroton_Wbxml_Decoder($dom);
+ // $requestBody = $decoder->decode();
+ // if ($this->_logger instanceof Zend_Log) {
+ // $requestBody->formatOutput = true;
+ // $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml request:\n" . $requestBody->saveXML());
+ // }
+ // } catch(Syncroton_Wbxml_Exception_UnexpectedEndOfFile $e) {
+ // $requestBody = NULL;
+ // }
+ // //TODO validate output
+ //}
+
+
+
+ public function testEncode()
+ {
+ $outputStream = fopen("php://temp", 'r+');
+
+ $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
+
+ $xml = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Tasks="uri:Tasks">
+ <Collections>
+ <Collection>
+ <SyncKey>2</SyncKey>
+ <CollectionId>tasksId</CollectionId>
+ <Commands>
+ <Add>
+ <ClientId>clientId2</ClientId>
+ <ApplicationData>
+ <Subject xmlns="uri:Tasks">task2</Subject>
+ <Complete xmlns="uri:Tasks">0</Complete>
+ <DueDate xmlns="uri:Tasks">2020-11-04T00:00:00.000Z</DueDate>
+ <UtcDueDate xmlns="uri:Tasks">2020-11-03T23:00:00.000Z</UtcDueDate>
+ </ApplicationData>
+ </Add>
+ <Add>
+ <ClientId>clientId3</ClientId>
+ <ApplicationData>
+ <Subject xmlns="uri:Tasks">task3</Subject>
+ <Complete xmlns="uri:Tasks">0</Complete>
+ <DueDate xmlns="uri:Tasks">2020-11-04T00:00:00.000Z</DueDate>
+ <UtcDueDate xmlns="uri:Tasks">2020-11-03T23:00:00.000Z</UtcDueDate>
+ </ApplicationData>
+ </Add>
+ </Commands>
+ </Collection>
+ </Collections>
+ <WindowSize>16</WindowSize>
+ </Sync>
+ EOF;
+
+
+ $dom = new DOMDocument();
+ $dom->loadXML($xml);
+
+ $encoder->encode($dom);
+
+ rewind($outputStream);
+ $output = stream_get_contents($outputStream);
+ // print("----");
+ // print(var_export($output, true));
+ // print("----");
+ $this->assertEquals(
+ base64_decode('AwFqAEVcT0sDMgABUgN0YXNrc0lkAAFWR0wDY2xpZW50SWQyAAFdAAlgA3Rhc2syAAFKAzAAAUwDMjAyMC0xMS0wNFQwMDowMDowMC4wMDBaAAFNAzIwMjAtMTEtMDNUMjM6MDA6MDAuMDAwWgABAQEAAEdMA2NsaWVudElkMwABXQAJYAN0YXNrMwABSgMwAAFMAzIwMjAtMTEtMDRUMDA6MDA6MDAuMDAwWgABTQMyMDIwLTExLTAzVDIzOjAwOjAwLjAwMFoAAQEBAQEBAABVAzE2AAEB'),
+ $output
+ );
+ }
+
+ public function testEncodeFolderSync()
+ {
+ $outputStream = fopen("php://temp", 'r+');
+
+ $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
+
+ $xml = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <FolderSync xmlns="uri:FolderHierarchy" xmlns:Syncroton="uri:Syncroton" xmlns:Internal="uri:Internal">
+ <Status>1</Status>
+ <SyncKey>1</SyncKey>
+ <Changes>
+ <Count>18</Count>
+ <Add>
+ <ServerId>2685b302b79f58d2753199545e3cb8be</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Test2</DisplayName>
+ <Type>13</Type>
+ </Add>
+ <Add>
+ <ServerId>9770b083c68e8584f396d15a116d6608</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>DavidCalendar</DisplayName>
+ <Type>13</Type>
+ </Add>
+ <Add>
+ <ServerId>0f66388806743c514b8063bf0dc87486</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>SergeyCalendar</DisplayName>
+ <Type>13</Type>
+ </Add>
+ <Add>
+ <ServerId>cca1b81c734abbcd669bea90d23e08ae</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Calendar</DisplayName>
+ <Type>8</Type>
+ </Add>
+ <Add>
+ <ServerId>ab1ddb4ef8e8f8fcc2c9f5a7f9062452</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>PubCal</DisplayName>
+ <Type>13</Type>
+ </Add>
+ <Add>
+ <ServerId>d98bd8721371544ed095841ead941893</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>(david) Test2</DisplayName>
+ <Type>13</Type>
+ </Add>
+ <Add>
+ <ServerId>9e7b9656ef61d4af2fb2fdcabe600079</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>(david) DavidCalendar</DisplayName>
+ <Type>13</Type>
+ </Add>
+ <Add>
+ <ServerId>384cf2d877c39a622fdc2a16898052e2</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>(david) Calendar</DisplayName>
+ <Type>13</Type>
+ </Add>
+ <Add>
+ <ServerId>Contacts::Syncroton</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Contacts</DisplayName>
+ <Type>9</Type>
+ </Add>
+ <Add>
+ <ServerId>1bb8c55fe84d52c6968db2571f7dc124</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Archive</DisplayName>
+ <Type>12</Type>
+ </Add>
+ <Add>
+ <ServerId>b51abe73e9e98fe200a4afe409050502</ServerId>
+ <ParentId>38b950ebd62cd9a66929c89615d0fc04</ParentId>
+ <DisplayName>Spam</DisplayName>
+ <Type>12</Type>
+ </Add>
+ <Add>
+ <ServerId>cf529c792fc87d1f207435b3921bb02e</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Sent</DisplayName>
+ <Type>5</Type>
+ </Add>
+ <Add>
+ <ServerId>715ed9ea29b8a5377a69c1f758037c65</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Spam</DisplayName>
+ <Type>12</Type>
+ </Add>
+ <Add>
+ <ServerId>db0d959a3aeb21757f8849a830947a7a</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Trash</DisplayName>
+ <Type>4</Type>
+ </Add>
+ <Add>
+ <ServerId>5ac9ec2e1a9d99e2e10cabe4abf26729</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Drafts</DisplayName>
+ <Type>3</Type>
+ </Add>
+ <Add>
+ <ServerId>38b950ebd62cd9a66929c89615d0fc04</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>INBOX</DisplayName>
+ <Type>2</Type>
+ </Add>
+ <Add>
+ <ServerId>fc56f4c7ffe0aefa622db9f8d9186c4a</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Notes</DisplayName>
+ <Type>10</Type>
+ </Add>
+ <Add>
+ <ServerId>90335880f65deff6e521acea2b71a773</ServerId>
+ <ParentId>0</ParentId>
+ <DisplayName>Tasks</DisplayName>
+ <Type>7</Type>
+ </Add>
+ </Changes>
+ </FolderSync>
+ EOF;
+
+
+ $dom = new DOMDocument();
+ $dom->loadXML($xml);
+
+ $encoder->encode($dom);
+
+ rewind($outputStream);
+ $output = stream_get_contents($outputStream);
+ // print("----");
+ // print(var_export(base64_encode($output), true));
+ // print("----");
+ $this->assertEquals(
+ base64_decode('AwFqAAAHVkwDMQABUgMxAAFOVwMxOAABT0gDMjY4NWIzMDJiNzlmNThkMjc1MzE5OTU0NWUzY2I4YmUAAUkDMAABRwNUZXN0MgABSgMxMwABAU9IAzk3NzBiMDgzYzY4ZTg1ODRmMzk2ZDE1YTExNmQ2NjA4AAFJAzAAAUcDRGF2aWRDYWxlbmRhcgABSgMxMwABAU9IAzBmNjYzODg4MDY3NDNjNTE0YjgwNjNiZjBkYzg3NDg2AAFJAzAAAUcDU2VyZ2V5Q2FsZW5kYXIAAUoDMTMAAQFPSANjY2ExYjgxYzczNGFiYmNkNjY5YmVhOTBkMjNlMDhhZQABSQMwAAFHA0NhbGVuZGFyAAFKAzgAAQFPSANhYjFkZGI0ZWY4ZThmOGZjYzJjOWY1YTdmOTA2MjQ1MgABSQMwAAFHA1B1YkNhbAABSgMxMwABAU9IA2Q5OGJkODcyMTM3MTU0NGVkMDk1ODQxZWFkOTQxODkzAAFJAzAAAUcDKGRhdmlkKSBUZXN0MgABSgMxMwABAU9IAzllN2I5NjU2ZWY2MWQ0YWYyZmIyZmRjYWJlNjAwMDc5AAFJAzAAAUcDKGRhdmlkKSBEYXZpZENhbGVuZGFyAAFKAzEzAAEBT0gDMzg0Y2YyZDg3N2MzOWE2MjJmZGMyYTE2ODk4MDUyZTIAAUkDMAABRwMoZGF2aWQpIENhbGVuZGFyAAFKAzEzAAEBT0gDQ29udGFjdHM6OlN5bmNyb3RvbgABSQMwAAFHA0NvbnRhY3RzAAFKAzkAAQFPSAMxYmI4YzU1ZmU4NGQ1MmM2OTY4ZGIyNTcxZjdkYzEyNAABSQMwAAFHA0FyY2hpdmUAAUoDMTIAAQFPSANiNTFhYmU3M2U5ZTk4ZmUyMDBhNGFmZTQwOTA1MDUwMgABSQMzOGI5NTBlYmQ2MmNkOWE2NjkyOWM4OTYxNWQwZmMwNAABRwNTcGFtAAFKAzEyAAEBT0gDY2Y1MjljNzkyZmM4N2QxZjIwNzQzNWIzOTIxYmIwMmUAAUkDMAABRwNTZW50AAFKAzUAAQFPSAM3MTVlZDllYTI5YjhhNTM3N2E2OWMxZjc1ODAzN2M2NQABSQMwAAFHA1NwYW0AAUoDMTIAAQFPSANkYjBkOTU5YTNhZWIyMTc1N2Y4ODQ5YTgzMDk0N2E3YQABSQMwAAFHA1RyYXNoAAFKAzQAAQFPSAM1YWM5ZWMyZTFhOWQ5OWUyZTEwY2FiZTRhYmYyNjcyOQABSQMwAAFHA0RyYWZ0cwABSgMzAAEBT0gDMzhiOTUwZWJkNjJjZDlhNjY5MjljODk2MTVkMGZjMDQAAUkDMAABRwNJTkJPWAABSgMyAAEBT0gDZmM1NmY0YzdmZmUwYWVmYTYyMmRiOWY4ZDkxODZjNGEAAUkDMAABRwNOb3RlcwABSgMxMAABAU9IAzkwMzM1ODgwZjY1ZGVmZjZlNTIxYWNlYTJiNzFhNzczAAFJAzAAAUcDVGFza3MAAUoDNwABAQEB'),
+ $output
+ );
+ }
+
+ public function testEncodeCalendar()
+ {
+ $outputStream = fopen("php://temp", 'r+');
+
+ $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
+
+ $xml = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
+ <Collections>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>38b950ebd62cd9a66929c89615d0fc04</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <MIMESupport>2</MIMESupport>
+ <MIMETruncation>8</MIMETruncation>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>4</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>cca1b81c734abbcd669bea90d23e08ae</CollectionId>
+ <Supported>
+ <DtStamp xmlns="uri:Calendar"/>
+ <Categories xmlns="uri:Calendar"/>
+ <Sensitivity xmlns="uri:Calendar"/>
+ <BusyStatus xmlns="uri:Calendar"/>
+ <UID xmlns="uri:Calendar"/>
+ <Timezone xmlns="uri:Calendar"/>
+ <StartTime xmlns="uri:Calendar"/>
+ <Subject xmlns="uri:Calendar"/>
+ <Location xmlns="uri:Calendar"/>
+ <EndTime xmlns="uri:Calendar"/>
+ <Recurrence xmlns="uri:Calendar"/>
+ <AllDayEvent xmlns="uri:Calendar"/>
+ <Reminder xmlns="uri:Calendar"/>
+ <Exceptions xmlns="uri:Calendar"/>
+ <Attendees xmlns="uri:Calendar"/>
+ <MeetingStatus xmlns="uri:Calendar"/>
+ <ResponseRequested xmlns="uri:Calendar"/>
+ <DisallowNewTimeProposal xmlns="uri:Calendar"/>
+ </Supported>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>Contacts::Syncroton</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>db0d959a3aeb21757f8849a830947a7a</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <MIMESupport>2</MIMESupport>
+ <MIMETruncation>8</MIMETruncation>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>4</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>cf529c792fc87d1f207435b3921bb02e</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <MIMESupport>2</MIMESupport>
+ <MIMETruncation>8</MIMETruncation>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>4</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>90335880f65deff6e521acea2b71a773</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>1bb8c55fe84d52c6968db2571f7dc124</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <MIMESupport>2</MIMESupport>
+ <MIMETruncation>8</MIMETruncation>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>4</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>715ed9ea29b8a5377a69c1f758037c65</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <MIMESupport>2</MIMESupport>
+ <MIMETruncation>8</MIMETruncation>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>4</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>b51abe73e9e98fe200a4afe409050502</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <MIMESupport>2</MIMESupport>
+ <MIMETruncation>8</MIMETruncation>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>4</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>0f66388806743c514b8063bf0dc87486</CollectionId>
+ <Supported>
+ <DtStamp xmlns="uri:Calendar"/>
+ <Categories xmlns="uri:Calendar"/>
+ <Sensitivity xmlns="uri:Calendar"/>
+ <BusyStatus xmlns="uri:Calendar"/>
+ <UID xmlns="uri:Calendar"/>
+ <Timezone xmlns="uri:Calendar"/>
+ <StartTime xmlns="uri:Calendar"/>
+ <Subject xmlns="uri:Calendar"/>
+ <Location xmlns="uri:Calendar"/>
+ <EndTime xmlns="uri:Calendar"/>
+ <Recurrence xmlns="uri:Calendar"/>
+ <AllDayEvent xmlns="uri:Calendar"/>
+ <Reminder xmlns="uri:Calendar"/>
+ <Exceptions xmlns="uri:Calendar"/>
+ <Attendees xmlns="uri:Calendar"/>
+ <MeetingStatus xmlns="uri:Calendar"/>
+ <ResponseRequested xmlns="uri:Calendar"/>
+ <DisallowNewTimeProposal xmlns="uri:Calendar"/>
+ </Supported>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>2685b302b79f58d2753199545e3cb8be</CollectionId>
+ <Supported>
+ <DtStamp xmlns="uri:Calendar"/>
+ <Categories xmlns="uri:Calendar"/>
+ <Sensitivity xmlns="uri:Calendar"/>
+ <BusyStatus xmlns="uri:Calendar"/>
+ <UID xmlns="uri:Calendar"/>
+ <Timezone xmlns="uri:Calendar"/>
+ <StartTime xmlns="uri:Calendar"/>
+ <Subject xmlns="uri:Calendar"/>
+ <Location xmlns="uri:Calendar"/>
+ <EndTime xmlns="uri:Calendar"/>
+ <Recurrence xmlns="uri:Calendar"/>
+ <AllDayEvent xmlns="uri:Calendar"/>
+ <Reminder xmlns="uri:Calendar"/>
+ <Exceptions xmlns="uri:Calendar"/>
+ <Attendees xmlns="uri:Calendar"/>
+ <MeetingStatus xmlns="uri:Calendar"/>
+ <ResponseRequested xmlns="uri:Calendar"/>
+ <DisallowNewTimeProposal xmlns="uri:Calendar"/>
+ </Supported>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>384cf2d877c39a622fdc2a16898052e2</CollectionId>
+ <Supported>
+ <DtStamp xmlns="uri:Calendar"/>
+ <Categories xmlns="uri:Calendar"/>
+ <Sensitivity xmlns="uri:Calendar"/>
+ <BusyStatus xmlns="uri:Calendar"/>
+ <UID xmlns="uri:Calendar"/>
+ <Timezone xmlns="uri:Calendar"/>
+ <StartTime xmlns="uri:Calendar"/>
+ <Subject xmlns="uri:Calendar"/>
+ <Location xmlns="uri:Calendar"/>
+ <EndTime xmlns="uri:Calendar"/>
+ <Recurrence xmlns="uri:Calendar"/>
+ <AllDayEvent xmlns="uri:Calendar"/>
+ <Reminder xmlns="uri:Calendar"/>
+ <Exceptions xmlns="uri:Calendar"/>
+ <Attendees xmlns="uri:Calendar"/>
+ <MeetingStatus xmlns="uri:Calendar"/>
+ <ResponseRequested xmlns="uri:Calendar"/>
+ <DisallowNewTimeProposal xmlns="uri:Calendar"/>
+ </Supported>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>9770b083c68e8584f396d15a116d6608</CollectionId>
+ <Supported>
+ <DtStamp xmlns="uri:Calendar"/>
+ <Categories xmlns="uri:Calendar"/>
+ <Sensitivity xmlns="uri:Calendar"/>
+ <BusyStatus xmlns="uri:Calendar"/>
+ <UID xmlns="uri:Calendar"/>
+ <Timezone xmlns="uri:Calendar"/>
+ <StartTime xmlns="uri:Calendar"/>
+ <Subject xmlns="uri:Calendar"/>
+ <Location xmlns="uri:Calendar"/>
+ <EndTime xmlns="uri:Calendar"/>
+ <Recurrence xmlns="uri:Calendar"/>
+ <AllDayEvent xmlns="uri:Calendar"/>
+ <Reminder xmlns="uri:Calendar"/>
+ <Exceptions xmlns="uri:Calendar"/>
+ <Attendees xmlns="uri:Calendar"/>
+ <MeetingStatus xmlns="uri:Calendar"/>
+ <ResponseRequested xmlns="uri:Calendar"/>
+ <DisallowNewTimeProposal xmlns="uri:Calendar"/>
+ </Supported>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>9e7b9656ef61d4af2fb2fdcabe600079</CollectionId>
+ <Supported>
+ <DtStamp xmlns="uri:Calendar"/>
+ <Categories xmlns="uri:Calendar"/>
+ <Sensitivity xmlns="uri:Calendar"/>
+ <BusyStatus xmlns="uri:Calendar"/>
+ <UID xmlns="uri:Calendar"/>
+ <Timezone xmlns="uri:Calendar"/>
+ <StartTime xmlns="uri:Calendar"/>
+ <Subject xmlns="uri:Calendar"/>
+ <Location xmlns="uri:Calendar"/>
+ <EndTime xmlns="uri:Calendar"/>
+ <Recurrence xmlns="uri:Calendar"/>
+ <AllDayEvent xmlns="uri:Calendar"/>
+ <Reminder xmlns="uri:Calendar"/>
+ <Exceptions xmlns="uri:Calendar"/>
+ <Attendees xmlns="uri:Calendar"/>
+ <MeetingStatus xmlns="uri:Calendar"/>
+ <ResponseRequested xmlns="uri:Calendar"/>
+ <DisallowNewTimeProposal xmlns="uri:Calendar"/>
+ </Supported>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>ab1ddb4ef8e8f8fcc2c9f5a7f9062452</CollectionId>
+ <Supported>
+ <DtStamp xmlns="uri:Calendar"/>
+ <Categories xmlns="uri:Calendar"/>
+ <Sensitivity xmlns="uri:Calendar"/>
+ <BusyStatus xmlns="uri:Calendar"/>
+ <UID xmlns="uri:Calendar"/>
+ <Timezone xmlns="uri:Calendar"/>
+ <StartTime xmlns="uri:Calendar"/>
+ <Subject xmlns="uri:Calendar"/>
+ <Location xmlns="uri:Calendar"/>
+ <EndTime xmlns="uri:Calendar"/>
+ <Recurrence xmlns="uri:Calendar"/>
+ <AllDayEvent xmlns="uri:Calendar"/>
+ <Reminder xmlns="uri:Calendar"/>
+ <Exceptions xmlns="uri:Calendar"/>
+ <Attendees xmlns="uri:Calendar"/>
+ <MeetingStatus xmlns="uri:Calendar"/>
+ <ResponseRequested xmlns="uri:Calendar"/>
+ <DisallowNewTimeProposal xmlns="uri:Calendar"/>
+ </Supported>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>d98bd8721371544ed095841ead941893</CollectionId>
+ <Supported>
+ <DtStamp xmlns="uri:Calendar"/>
+ <Categories xmlns="uri:Calendar"/>
+ <Sensitivity xmlns="uri:Calendar"/>
+ <BusyStatus xmlns="uri:Calendar"/>
+ <UID xmlns="uri:Calendar"/>
+ <Timezone xmlns="uri:Calendar"/>
+ <StartTime xmlns="uri:Calendar"/>
+ <Subject xmlns="uri:Calendar"/>
+ <Location xmlns="uri:Calendar"/>
+ <EndTime xmlns="uri:Calendar"/>
+ <Recurrence xmlns="uri:Calendar"/>
+ <AllDayEvent xmlns="uri:Calendar"/>
+ <Reminder xmlns="uri:Calendar"/>
+ <Exceptions xmlns="uri:Calendar"/>
+ <Attendees xmlns="uri:Calendar"/>
+ <MeetingStatus xmlns="uri:Calendar"/>
+ <ResponseRequested xmlns="uri:Calendar"/>
+ <DisallowNewTimeProposal xmlns="uri:Calendar"/>
+ </Supported>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ </Collections>
+ <WindowSize>16</WindowSize>
+ </Sync>
+ EOF;
+
+
+ $dom = new DOMDocument();
+ $dom->loadXML($xml);
+
+ $encoder->encode($dom);
+
+ rewind($outputStream);
+ $output = stream_get_contents($outputStream);
+ // print("----");
+ // print(var_export(base64_encode($output), true));
+ // print("----");
+
+ $this->assertEquals(
+ base64_decode('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'),
+ $output
+ );
+ }
+
+ public function testEncodeEmail()
+ {
+ $outputStream = fopen("php://temp", 'r+');
+
+ $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
+
+ $xml = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <Sync xmlns="uri:AirSync" xmlns:Syncroton="uri:Syncroton" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Email="uri:Email" xmlns:Email2="uri:Email2" xmlns:Tasks="uri:Tasks">
+ <Collections>
+ <Collection xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase">
+ <Class>Email</Class>
+ <SyncKey>2</SyncKey>
+ <CollectionId>38b950ebd62cd9a66929c89615d0fc04</CollectionId>
+ <Status>1</Status>
+ <MoreAvailable/>
+ <Commands xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase">
+ <Add xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase">
+ <ServerId>38b950ebd62cd9a66929c89615d0fc04::1</ServerId>
+ <ApplicationData>
+ <Email:DateReceived xmlns="uri:Email">2023-05-06T14:51:40.000Z</Email:DateReceived>
+ <Email:From xmlns="uri:Email">"Mollekopf, Christian" &lt;christian@example.ch&gt;</Email:From>
+ <Email:InternetCPID xmlns="uri:Email">65001</Email:InternetCPID>
+ <Email:Subject xmlns="uri:Email">Foobar 1</Email:Subject>
+ <Email:To xmlns="uri:Email">christian@example.ch</Email:To>
+ <Email:Read xmlns="uri:Email">0</Email:Read>
+ <Email:Flag xmlns="uri:Email"/>
+ <AirSyncBase:Body xmlns="uri:AirSyncBase">
+ <AirSyncBase:Type>4</AirSyncBase:Type>
+ <AirSyncBase:Data>Return-Path: &lt;christian@example.ch&gt;&#13;
+ Received: from imapb010.mykolab.com ([unix socket])&#13;
+ by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;&#13;
+ Wed, 09 Aug 2017 18:37:01 +0200&#13;
+ X-Sieve: CMU Sieve 2.4&#13;
+ Received: from int-mx002.mykolab.com (unknown [10.9.13.2])&#13;
+ by imapb010.mykolab.com (Postfix) with ESMTPS id 0A93910A25047&#13;
+ for &lt;christian@example.ch&gt;; Wed, 9 Aug 2017 18:37:01 +0200 (CEST)&#13;
+ Received: from int-subm002.mykolab.com (unknown [10.9.37.2])&#13;
+ by int-mx002.mykolab.com (Postfix) with ESMTPS id EC06AF6E&#13;
+ for &lt;christian@example.ch&gt;; Wed, 9 Aug 2017 18:37:00 +0200 (CEST)&#13;
+ MIME-Version: 1.0&#13;
+ Content-Type: multipart/mixed;&#13;
+ boundary="=_291b8e96564265636432c6d494e02322"&#13;
+ Date: Sat, 06 May 2023 14:41:40 &#13;
+ From: "Mollekopf, Christian" &lt;christian@example.ch&gt;&#13;
+ To: christian@example.ch&#13;
+ Subject: Foobar 1&#13;
+ Message-ID: &lt;foobar1@example.org&gt;&#13;
+ &#13;
+ --=_291b8e96564265636432c6d494e02322&#13;
+ Content-Type: multipart/alternative;&#13;
+ boundary="=_ceff0fd19756f45ed1295ee2069ff8e0"&#13;
+ &#13;
+ --=_ceff0fd19756f45ed1295ee2069ff8e0&#13;
+ Content-Transfer-Encoding: 7bit&#13;
+ Content-Type: text/plain; charset=US-ASCII&#13;
+ &#13;
+ sdlkjsdjf&#13;
+ --=_ceff0fd19756f45ed1295ee2069ff8e0&#13;
+ Content-Transfer-Encoding: quoted-printable&#13;
+ Content-Type: text/html; charset=UTF-8&#13;
+ &#13;
+ &lt;html&gt;&lt;head&gt;&lt;meta http-equiv=3D"Content-Type" content=3D"text/html; charset=&#13;
+ =3DUTF-8" /&gt;&lt;/head&gt;&lt;body style=3D'font-size: 10pt; font-family: Verdana,Gen=&#13;
+ eva,sans-serif'&gt;&#13;
+ &lt;p&gt;sdlkjsdjf&lt;/p&gt;&#13;
+ &#13;
+ &lt;/body&gt;&lt;/html&gt;&#13;
+ &#13;
+ --=_ceff0fd19756f45ed1295ee2069ff8e0--&#13;
+ &#13;
+ --=_291b8e96564265636432c6d494e02322&#13;
+ Content-Transfer-Encoding: base64&#13;
+ Content-Type: text/plain;&#13;
+ name=xorg.conf&#13;
+ Content-Disposition: attachment;&#13;
+ filename=xorg.conf;&#13;
+ size=211&#13;
+ &#13;
+ U2VjdGlvbiAiRGV2aWNlIgogICAgSWRlbnRpZmllciAgICAgIkRldmljZTAiCiAgICBEcml2ZXIg&#13;
+ ICAgIEJvYXJkTmFtZSAgICAgICJOVlMgNDIwME0iCiAgICBPcHRpb24gIk5vTG9nbyIgInRydWUi&#13;
+ CiAgICBPcHRpb24gIlVzZUVESUQiICJ0cnVlIgpFbmRTZWN0aW9uCg==&#13;
+ --=_291b8e96564265636432c6d494e02322--</AirSyncBase:Data>
+ </AirSyncBase:Body>
+ <AirSyncBase:NativeBodyType xmlns="uri:AirSyncBase">2</AirSyncBase:NativeBodyType>
+ <Email:MessageClass xmlns="uri:Email">IPM.Note</Email:MessageClass>
+ <Email:ContentClass xmlns="uri:Email">urn:content-classes:message</Email:ContentClass>
+ <AirSyncBase:Attachments xmlns="uri:AirSyncBase">
+ <AirSyncBase:Attachment>
+ <AirSyncBase:DisplayName>xorg.conf</AirSyncBase:DisplayName>
+ <AirSyncBase:FileReference>38b950ebd62cd9a66929c89615d0fc04::5::2</AirSyncBase:FileReference>
+ <AirSyncBase:Method>1</AirSyncBase:Method>
+ <AirSyncBase:EstimatedDataSize>35100212</AirSyncBase:EstimatedDataSize>
+ </AirSyncBase:Attachment>
+ </AirSyncBase:Attachments>
+ </ApplicationData>
+ </Add>
+ </Commands>
+ </Collection>
+ </Collections>
+ </Sync>
+ EOF;
+
+ $dom = new DOMDocument();
+ $dom->loadXML($xml);
+
+ $encoder->encode($dom);
+
+ rewind($outputStream);
+ $output = stream_get_contents($outputStream);
+ // print("----");
+ // print(var_export(base64_encode($output), true));
+ // print("----");
+
+ $this->assertEquals(
+ base64_decode('AwFqAEVcT1ADRW1haWwAAUsDMgABUgMzOGI5NTBlYmQ2MmNkOWE2NjkyOWM4OTYxNWQwZmMwNAABTgMxAAEUVkdNAzM4Yjk1MGViZDYyY2Q5YTY2OTI5Yzg5NjE1ZDBmYzA0OjoxAAFdAAJPAzIwMjMtMDUtMDZUMTQ6NTE6NDAuMDAwWgABWAMiTW9sbGVrb3BmLCBDaHJpc3RpYW4iIDxjaHJpc3RpYW5AZXhhbXBsZS5jaD4AAXkDNjUwMDEAAVQDRm9vYmFyIDEAAVYDY2hyaXN0aWFuQGV4YW1wbGUuY2gAAVUDMAABOgARSkYDNAABSwNSZXR1cm4tUGF0aDogPGNocmlzdGlhbkBleGFtcGxlLmNoPg0KUmVjZWl2ZWQ6IGZyb20gaW1hcGIwMTAubXlrb2xhYi5jb20gKFt1bml4IHNvY2tldF0pDQogICAgICAgIGJ5IGltYXBiMDEwLm15a29sYWIuY29tIChDeXJ1cyAyLjUuMTAtNDktZzJlMjE0YjQtS29sYWItMi41LjEwLTguMS5lbDcua29sYWJfMTQpIHdpdGggTE1UUEE7DQogICAgICAgIFdlZCwgMDkgQXVnIDIwMTcgMTg6Mzc6MDEgKzAyMDANClgtU2lldmU6IENNVSBTaWV2ZSAyLjQNClJlY2VpdmVkOiBmcm9tIGludC1teDAwMi5teWtvbGFiLmNvbSAodW5rbm93biBbMTAuOS4xMy4yXSkNCiAgICAgICAgYnkgaW1hcGIwMTAubXlrb2xhYi5jb20gKFBvc3RmaXgpIHdpdGggRVNNVFBTIGlkIDBBOTM5MTBBMjUwNDcNCiAgICAgICAgZm9yIDxjaHJpc3RpYW5AZXhhbXBsZS5jaD47IFdlZCwgIDkgQXVnIDIwMTcgMTg6Mzc6MDEgKzAyMDAgKENFU1QpDQpSZWNlaXZlZDogZnJvbSBpbnQtc3VibTAwMi5teWtvbGFiLmNvbSAodW5rbm93biBbMTAuOS4zNy4yXSkNCiAgICAgICAgYnkgaW50LW14MDAyLm15a29sYWIuY29tIChQb3N0Zml4KSB3aXRoIEVTTVRQUyBpZCBFQzA2QUY2RQ0KICAgICAgICBmb3IgPGNocmlzdGlhbkBleGFtcGxlLmNoPjsgV2VkLCAgOSBBdWcgMjAxNyAxODozNzowMCArMDIwMCAoQ0VTVCkNCk1JTUUtVmVyc2lvbjogMS4wDQpDb250ZW50LVR5cGU6IG11bHRpcGFydC9taXhlZDsNCmJvdW5kYXJ5PSI9XzI5MWI4ZTk2NTY0MjY1NjM2NDMyYzZkNDk0ZTAyMzIyIg0KRGF0ZTogU2F0LCAwNiBNYXkgMjAyMyAxNDo0MTo0MCANCkZyb206ICJNb2xsZWtvcGYsIENocmlzdGlhbiIgPGNocmlzdGlhbkBleGFtcGxlLmNoPg0KVG86IGNocmlzdGlhbkBleGFtcGxlLmNoDQpTdWJqZWN0OiBGb29iYXIgMQ0KTWVzc2FnZS1JRDogPGZvb2JhcjFAZXhhbXBsZS5vcmc+DQoNCi0tPV8yOTFiOGU5NjU2NDI2NTYzNjQzMmM2ZDQ5NGUwMjMyMg0KQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvYWx0ZXJuYXRpdmU7DQpib3VuZGFyeT0iPV9jZWZmMGZkMTk3NTZmNDVlZDEyOTVlZTIwNjlmZjhlMCINCg0KLS09X2NlZmYwZmQxOTc1NmY0NWVkMTI5NWVlMjA2OWZmOGUwDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA3Yml0DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9VVMtQVNDSUkNCg0Kc2Rsa2pzZGpmDQotLT1fY2VmZjBmZDE5NzU2ZjQ1ZWQxMjk1ZWUyMDY5ZmY4ZTANCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IHF1b3RlZC1wcmludGFibGUNCkNvbnRlbnQtVHlwZTogdGV4dC9odG1sOyBjaGFyc2V0PVVURi04DQoNCjxodG1sPjxoZWFkPjxtZXRhIGh0dHAtZXF1aXY9M0QiQ29udGVudC1UeXBlIiBjb250ZW50PTNEInRleHQvaHRtbDsgY2hhcnNldD0NCj0zRFVURi04IiAvPjwvaGVhZD48Ym9keSBzdHlsZT0zRCdmb250LXNpemU6IDEwcHQ7IGZvbnQtZmFtaWx5OiBWZXJkYW5hLEdlbj0NCmV2YSxzYW5zLXNlcmlmJz4NCjxwPnNkbGtqc2RqZjwvcD4NCg0KPC9ib2R5PjwvaHRtbD4NCg0KLS09X2NlZmYwZmQxOTc1NmY0NWVkMTI5NWVlMjA2OWZmOGUwLS0NCg0KLS09XzI5MWI4ZTk2NTY0MjY1NjM2NDMyYzZkNDk0ZTAyMzIyDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQNCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsNCm5hbWU9eG9yZy5jb25mDQpDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50Ow0KZmlsZW5hbWU9eG9yZy5jb25mOw0Kc2l6ZT0yMTENCg0KVTJWamRHbHZiaUFpUkdWMmFXTmxJZ29nSUNBZ1NXUmxiblJwWm1sbGNpQWdJQ0FnSWtSbGRtbGpaVEFpQ2lBZ0lDQkVjbWwyWlhJZw0KSUNBZ0lFSnZZWEprVG1GdFpTQWdJQ0FnSUNKT1ZsTWdOREl3TUUwaUNpQWdJQ0JQY0hScGIyNGdJazV2VEc5bmJ5SWdJblJ5ZFdVaQ0KQ2lBZ0lDQlBjSFJwYjI0Z0lsVnpaVVZFU1VRaUlDSjBjblZsSWdwRmJtUlRaV04wYVc5dUNnPT0NCi0tPV8yOTFiOGU5NjU2NDI2NTYzNjQzMmM2ZDQ5NGUwMjMyMi0tAAEBVgMyAAEAAlMDSVBNLk5vdGUAAXwDdXJuOmNvbnRlbnQtY2xhc3NlczptZXNzYWdlAAEAEU5PUAN4b3JnLmNvbmYAAVEDMzhiOTUwZWJkNjJjZDlhNjY5MjljODk2MTVkMGZjMDQ6OjU6OjIAAVIDMQABTAMzNTEwMDIxMgABAQEBAQEBAQE='),
+ $output
+ );
+ }
+
+ public function testEncodeEmailPerformanceTest()
+ {
+ $outputStream = fopen("php://temp", 'r+');
+
+ $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
+ $attachment = str_repeat("ICAgIEJvYXJkTmFtZSAgICAgICJOVlMgNDIwME0iCiAgICBPcHRpb24gIk5vTG9nbyIgInRydWUi&#13;\n", 100000);
+ $xml = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <Sync xmlns="uri:AirSync" xmlns:Syncroton="uri:Syncroton" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Email="uri:Email" xmlns:Email2="uri:Email2" xmlns:Tasks="uri:Tasks">
+ <Collections>
+ <Collection xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase">
+ <Class>Email</Class>
+ <SyncKey>2</SyncKey>
+ <CollectionId>38b950ebd62cd9a66929c89615d0fc04</CollectionId>
+ <Status>1</Status>
+ <MoreAvailable/>
+ <Commands xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase">
+ <Add xmlns:default="uri:Email" xmlns:default1="uri:AirSyncBase">
+ <ServerId>38b950ebd62cd9a66929c89615d0fc04::1</ServerId>
+ <ApplicationData>
+ <Email:DateReceived xmlns="uri:Email">2023-05-06T14:51:40.000Z</Email:DateReceived>
+ <Email:From xmlns="uri:Email">"Mollekopf, Christian" &lt;christian@example.ch&gt;</Email:From>
+ <Email:InternetCPID xmlns="uri:Email">65001</Email:InternetCPID>
+ <Email:Subject xmlns="uri:Email">Foobar 1</Email:Subject>
+ <Email:To xmlns="uri:Email">christian@example.ch</Email:To>
+ <Email:Read xmlns="uri:Email">0</Email:Read>
+ <Email:Flag xmlns="uri:Email"/>
+ <AirSyncBase:Body xmlns="uri:AirSyncBase">
+ <AirSyncBase:Type>4</AirSyncBase:Type>
+ <AirSyncBase:Data>Return-Path: &lt;christian@example.ch&gt;&#13;
+ Received: from imapb010.mykolab.com ([unix socket])&#13;
+ by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;&#13;
+ Wed, 09 Aug 2017 18:37:01 +0200&#13;
+ X-Sieve: CMU Sieve 2.4&#13;
+ Received: from int-mx002.mykolab.com (unknown [10.9.13.2])&#13;
+ by imapb010.mykolab.com (Postfix) with ESMTPS id 0A93910A25047&#13;
+ for &lt;christian@example.ch&gt;; Wed, 9 Aug 2017 18:37:01 +0200 (CEST)&#13;
+ Received: from int-subm002.mykolab.com (unknown [10.9.37.2])&#13;
+ by int-mx002.mykolab.com (Postfix) with ESMTPS id EC06AF6E&#13;
+ for &lt;christian@example.ch&gt;; Wed, 9 Aug 2017 18:37:00 +0200 (CEST)&#13;
+ MIME-Version: 1.0&#13;
+ Content-Type: multipart/mixed;&#13;
+ boundary="=_291b8e96564265636432c6d494e02322"&#13;
+ Date: Sat, 06 May 2023 14:41:40 &#13;
+ From: "Mollekopf, Christian" &lt;christian@example.ch&gt;&#13;
+ To: christian@example.ch&#13;
+ Subject: Foobar 1&#13;
+ Message-ID: &lt;foobar1@example.org&gt;&#13;
+ &#13;
+ --=_291b8e96564265636432c6d494e02322&#13;
+ Content-Type: multipart/alternative;&#13;
+ boundary="=_ceff0fd19756f45ed1295ee2069ff8e0"&#13;
+ &#13;
+ --=_ceff0fd19756f45ed1295ee2069ff8e0&#13;
+ Content-Transfer-Encoding: 7bit&#13;
+ Content-Type: text/plain; charset=US-ASCII&#13;
+ &#13;
+ sdlkjsdjf&#13;
+ --=_ceff0fd19756f45ed1295ee2069ff8e0&#13;
+ Content-Transfer-Encoding: quoted-printable&#13;
+ Content-Type: text/html; charset=UTF-8&#13;
+ &#13;
+ &lt;html&gt;&lt;head&gt;&lt;meta http-equiv=3D"Content-Type" content=3D"text/html; charset=&#13;
+ =3DUTF-8" /&gt;&lt;/head&gt;&lt;body style=3D'font-size: 10pt; font-family: Verdana,Gen=&#13;
+ eva,sans-serif'&gt;&#13;
+ &lt;p&gt;sdlkjsdjf&lt;/p&gt;&#13;
+ &#13;
+ &lt;/body&gt;&lt;/html&gt;&#13;
+ &#13;
+ --=_ceff0fd19756f45ed1295ee2069ff8e0--&#13;
+ &#13;
+ --=_291b8e96564265636432c6d494e02322&#13;
+ Content-Transfer-Encoding: base64&#13;
+ Content-Type: text/plain;&#13;
+ name=xorg.conf&#13;
+ Content-Disposition: attachment;&#13;
+ filename=xorg.conf;&#13;
+ size=211&#13;
+ &#13;
+ U2VjdGlvbiAiRGV2aWNlIgogICAgSWRlbnRpZmllciAgICAgIkRldmljZTAiCiAgICBEcml2ZXIg&#13;
+ {$attachment}
+ CiAgICBPcHRpb24gIlVzZUVESUQiICJ0cnVlIgpFbmRTZWN0aW9uCg==&#13;
+ --=_291b8e96564265636432c6d494e02322--</AirSyncBase:Data>
+ </AirSyncBase:Body>
+ <AirSyncBase:NativeBodyType xmlns="uri:AirSyncBase">2</AirSyncBase:NativeBodyType>
+ <Email:MessageClass xmlns="uri:Email">IPM.Note</Email:MessageClass>
+ <Email:ContentClass xmlns="uri:Email">urn:content-classes:message</Email:ContentClass>
+ <AirSyncBase:Attachments xmlns="uri:AirSyncBase">
+ <AirSyncBase:Attachment>
+ <AirSyncBase:DisplayName>xorg.conf</AirSyncBase:DisplayName>
+ <AirSyncBase:FileReference>38b950ebd62cd9a66929c89615d0fc04::5::2</AirSyncBase:FileReference>
+ <AirSyncBase:Method>1</AirSyncBase:Method>
+ <AirSyncBase:EstimatedDataSize>35100212</AirSyncBase:EstimatedDataSize>
+ </AirSyncBase:Attachment>
+ </AirSyncBase:Attachments>
+ </ApplicationData>
+ </Add>
+ </Commands>
+ </Collection>
+ </Collections>
+ </Sync>
+ EOF;
+
+ $dom = new DOMDocument();
+ $dom->loadXML($xml);
+
+ $encoder->encode($dom);
+ }
+}
+

File Metadata

Mime Type
text/x-diff
Expires
Thu, Mar 19, 7:52 AM (19 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
458485
Default Alt Text
(60 KB)

Event Timeline