Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2571468
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
60 KB
Referenced Files
None
Subscribers
None
View Options
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" <christian@example.ch></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: <christian@example.ch>
+ Received: from imapb010.mykolab.com ([unix socket])
+ by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
+ Wed, 09 Aug 2017 18:37:01 +0200
+ X-Sieve: CMU Sieve 2.4
+ Received: from int-mx002.mykolab.com (unknown [10.9.13.2])
+ by imapb010.mykolab.com (Postfix) with ESMTPS id 0A93910A25047
+ for <christian@example.ch>; Wed, 9 Aug 2017 18:37:01 +0200 (CEST)
+ Received: from int-subm002.mykolab.com (unknown [10.9.37.2])
+ by int-mx002.mykolab.com (Postfix) with ESMTPS id EC06AF6E
+ for <christian@example.ch>; Wed, 9 Aug 2017 18:37:00 +0200 (CEST)
+ MIME-Version: 1.0
+ Content-Type: multipart/mixed;
+ boundary="=_291b8e96564265636432c6d494e02322"
+ Date: Sat, 06 May 2023 14:41:40
+ From: "Mollekopf, Christian" <christian@example.ch>
+ To: christian@example.ch
+ Subject: Foobar 1
+ Message-ID: <foobar1@example.org>
+
+ --=_291b8e96564265636432c6d494e02322
+ Content-Type: multipart/alternative;
+ boundary="=_ceff0fd19756f45ed1295ee2069ff8e0"
+
+ --=_ceff0fd19756f45ed1295ee2069ff8e0
+ Content-Transfer-Encoding: 7bit
+ Content-Type: text/plain; charset=US-ASCII
+
+ sdlkjsdjf
+ --=_ceff0fd19756f45ed1295ee2069ff8e0
+ Content-Transfer-Encoding: quoted-printable
+ Content-Type: text/html; charset=UTF-8
+
+ <html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; charset=
+ =3DUTF-8" /></head><body style=3D'font-size: 10pt; font-family: Verdana,Gen=
+ eva,sans-serif'>
+ <p>sdlkjsdjf</p>
+
+ </body></html>
+
+ --=_ceff0fd19756f45ed1295ee2069ff8e0--
+
+ --=_291b8e96564265636432c6d494e02322
+ Content-Transfer-Encoding: base64
+ Content-Type: text/plain;
+ name=xorg.conf
+ Content-Disposition: attachment;
+ filename=xorg.conf;
+ size=211
+
+ U2VjdGlvbiAiRGV2aWNlIgogICAgSWRlbnRpZmllciAgICAgIkRldmljZTAiCiAgICBEcml2ZXIg
+ ICAgIEJvYXJkTmFtZSAgICAgICJOVlMgNDIwME0iCiAgICBPcHRpb24gIk5vTG9nbyIgInRydWUi
+ CiAgICBPcHRpb24gIlVzZUVESUQiICJ0cnVlIgpFbmRTZWN0aW9uCg==
+ --=_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 \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" <christian@example.ch></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: <christian@example.ch>
+ Received: from imapb010.mykolab.com ([unix socket])
+ by imapb010.mykolab.com (Cyrus 2.5.10-49-g2e214b4-Kolab-2.5.10-8.1.el7.kolab_14) with LMTPA;
+ Wed, 09 Aug 2017 18:37:01 +0200
+ X-Sieve: CMU Sieve 2.4
+ Received: from int-mx002.mykolab.com (unknown [10.9.13.2])
+ by imapb010.mykolab.com (Postfix) with ESMTPS id 0A93910A25047
+ for <christian@example.ch>; Wed, 9 Aug 2017 18:37:01 +0200 (CEST)
+ Received: from int-subm002.mykolab.com (unknown [10.9.37.2])
+ by int-mx002.mykolab.com (Postfix) with ESMTPS id EC06AF6E
+ for <christian@example.ch>; Wed, 9 Aug 2017 18:37:00 +0200 (CEST)
+ MIME-Version: 1.0
+ Content-Type: multipart/mixed;
+ boundary="=_291b8e96564265636432c6d494e02322"
+ Date: Sat, 06 May 2023 14:41:40
+ From: "Mollekopf, Christian" <christian@example.ch>
+ To: christian@example.ch
+ Subject: Foobar 1
+ Message-ID: <foobar1@example.org>
+
+ --=_291b8e96564265636432c6d494e02322
+ Content-Type: multipart/alternative;
+ boundary="=_ceff0fd19756f45ed1295ee2069ff8e0"
+
+ --=_ceff0fd19756f45ed1295ee2069ff8e0
+ Content-Transfer-Encoding: 7bit
+ Content-Type: text/plain; charset=US-ASCII
+
+ sdlkjsdjf
+ --=_ceff0fd19756f45ed1295ee2069ff8e0
+ Content-Transfer-Encoding: quoted-printable
+ Content-Type: text/html; charset=UTF-8
+
+ <html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; charset=
+ =3DUTF-8" /></head><body style=3D'font-size: 10pt; font-family: Verdana,Gen=
+ eva,sans-serif'>
+ <p>sdlkjsdjf</p>
+
+ </body></html>
+
+ --=_ceff0fd19756f45ed1295ee2069ff8e0--
+
+ --=_291b8e96564265636432c6d494e02322
+ Content-Transfer-Encoding: base64
+ Content-Type: text/plain;
+ name=xorg.conf
+ Content-Disposition: attachment;
+ filename=xorg.conf;
+ size=211
+
+ U2VjdGlvbiAiRGV2aWNlIgogICAgSWRlbnRpZmllciAgICAgIkRldmljZTAiCiAgICBEcml2ZXIg
+ {$attachment}
+ CiAgICBPcHRpb24gIlVzZUVESUQiICJ0cnVlIgpFbmRTZWN0aW9uCg==
+ --=_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
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Mar 19, 7:52 AM (17 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
458485
Default Alt Text
(60 KB)
Attached To
Mode
R4 syncroton
Attached
Detach File
Event Timeline
Log In to Comment