Projects
Kolab:16
kolab-syncroton
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 50
View file
kolab-syncroton.spec
Changed
@@ -36,7 +36,7 @@ %global _ap_sysconfdir %{_sysconfdir}/%{httpd_name} Name: kolab-syncroton -Version: 2.3.23 +Version: 2.4.0 Release: 1%{?dist} Summary: ActiveSync for Kolab Groupware @@ -44,7 +44,7 @@ License: LGPLv2 URL: http://www.syncroton.org -Source0: https://mirror.kolabenterprise.com/pub/releases/%{name}-%{version}.tar.gz +Source0: %{name}-%{version}.tar.gz Source1: kolab-syncroton.logrotate Source2: plesk.kolab_syncroton.inc.php @@ -68,6 +68,7 @@ %endif %endif +Requires: roundcubemail Requires: logrotate %if 0%{?rhel} < 8 Requires: php-kolabformat @@ -171,7 +172,7 @@ fi /usr/share/roundcubemail/bin/updatedb.sh \ - --dir /usr/share/doc/kolab-syncroton-%{version}/SQL/ \ + --dir /usr/share/doc/kolab-syncroton/SQL/ \ --package syncroton \ >/dev/null 2>&1 || : @@ -192,6 +193,9 @@ %attr(0770,%{httpd_user},%{httpd_group}) %{_var}/log/%{name} %changelog +* Wed May 10 2023 Christian Mollekopf <mollekopf@apheleia-it.ch> - 2.4.0-1 +- Release version 2.4.0 + * Fri Feb 04 2022 Jeroen van Meeuwen <vanmeeuwen@apheleia-it.ch> - 2.3.22-1 - Release version 2.3.22
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +kolab-syncroton (2.4.0-0~kolab1) unstable; urgency=low + + * Release version 2.4.0 + + -- Christian Mollekopf <mollekopf@apheleia-it.ch> Wed, 10 May 2023 15:13:40 +0200 + kolab-syncroton (2.3.23-0~kolab1) unstable; urgency=low * Release version 2.3.23
View file
kolab-syncroton-2.4.0.tar.gz/docs/SQL/mysql/2023100500.sql
Added
@@ -0,0 +1,2 @@ + +ALTER TABLE `syncroton_synckey` ADD `client_id_map` longblob DEFAULT NULL;
View file
kolab-syncroton-2.3.23.tar.gz/lib/ext/Syncroton/Command/Settings.php -> kolab-syncroton-2.4.0.tar.gz/lib/ext/Syncroton/Command/Settings.php
Changed
@@ -110,6 +110,7 @@ // Out-of-Office if (!empty($this->_OofGet)) { + $OofGet = null; try { $OofGet = $this->_deviceBackend->getOOF($this->_OofGet); } catch (Exception $e) {
View file
kolab-syncroton-2.3.23.tar.gz/lib/ext/Syncroton/Command/Sync.php -> kolab-syncroton-2.4.0.tar.gz/lib/ext/Syncroton/Command/Sync.php
Changed
@@ -243,7 +243,12 @@ continue; } - // check for invalid sycnkey + $syncKeyReused = $this->_syncStateBackend->haveNext($this->_device, $collectionData->folder, $collectionData->syncKey); + if ($syncKeyReused) { + if ($this->_logger instanceof Zend_Log) + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " already known synckey {$collectionData->syncKey} provided"); + } + // check for invalid synckey if(($collectionData->syncState = $this->_syncStateBackend->validate($this->_device, $collectionData->folder, $collectionData->syncKey)) === false) { if ($this->_logger instanceof Zend_Log) $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " invalid synckey {$collectionData->syncKey} provided"); @@ -301,6 +306,11 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($adds) . " entries to be added to server"); + $clientIdMap = ; + if ($syncKeyReused && $collectionData->syncState->clientIdMap) { + $clientIdMap = Zend_Json::decode($collectionData->syncState->clientIdMap); + } + foreach ($adds as $add) { if ($this->_logger instanceof Zend_Log) $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " add entry with clientId " . (string) $add->ClientId); @@ -309,20 +319,35 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " adding entry as new"); - $serverId = $dataController->createEntry($collectionData->collectionId, new $dataClass($add->ApplicationData)); - - $clientModifications'added'$serverId = array( - 'clientId' => (string)$add->ClientId, - 'serverId' => $serverId, - 'status' => self::STATUS_SUCCESS, - 'contentState' => $this->_contentStateBackend->create(new Syncroton_Model_Content(array( - 'device_id' => $this->_device, - 'folder_id' => $collectionData->folder, - 'contentid' => $serverId, - 'creation_time' => $this->_syncTimeStamp, - 'creation_synckey' => $collectionData->syncKey + 1 - ))) - ); + $clientId = (string)$add->ClientId; + // If the sync key was reused, but we don't have a $clientId mapping, + // this means the client sent a new item with the same sync_key. + if ($syncKeyReused && array_key_exists($clientId, $clientIdMap)) { + // We don't normally store the clientId, so if a command with Add's is resent, + // we have to look-up the corresponding serverId using a cached clientId => serverId mapping, + // otherwise we would duplicate all added items on resend. + $serverId = $clientIdMap$clientId; + $clientModifications'added'$serverId = array( + 'clientId' => (string)$add->ClientId, + 'serverId' => $serverId, + 'status' => self::STATUS_SUCCESS, + 'contentState' => null + ); + } else { + $serverId = $dataController->createEntry($collectionData->collectionId, new $dataClass($add->ApplicationData)); + $clientModifications'added'$serverId = array( + 'clientId' => (string)$add->ClientId, + 'serverId' => $serverId, + 'status' => self::STATUS_SUCCESS, + 'contentState' => $this->_contentStateBackend->create(new Syncroton_Model_Content(array( + 'device_id' => $this->_device, + 'folder_id' => $collectionData->folder, + 'contentid' => $serverId, + 'creation_time' => $this->_syncTimeStamp, + 'creation_synckey' => $collectionData->syncKey + 1 + ))) + ); + } } catch (Exception $e) { if ($this->_logger instanceof Zend_Log) @@ -336,7 +361,7 @@ } // handle changes, but only if not first sync - if($collectionData->syncKey > 1 && $collectionData->hasClientChanges()) { + if(!$syncKeyReused && $collectionData->syncKey > 1 && $collectionData->hasClientChanges()) { $changes = $collectionData->getClientChanges(); if ($this->_logger instanceof Zend_Log) @@ -367,7 +392,7 @@ } // handle deletes, but only if not first sync - if($collectionData->hasClientDeletes()) { + if(!$syncKeyReused && $collectionData->hasClientDeletes()) { $deletes = $collectionData->getClientDeletes(); if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($deletes) . " entries to be deleted on server"); @@ -670,8 +695,8 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped added entry: " . $serverId); unset($serverModifications'added'$id); - } } + } // entries to be deleted $serverModifications'deleted' = array_diff($allClientEntries, $allServerEntries); @@ -731,7 +756,7 @@ foreach($clientModifications'added' as $entryData) { $add = $responses->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Add')); $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ClientId', $entryData'clientId')); - // we have no serverId is the add failed + // we have no serverId if the add failed if(isset($entryData'serverId')) { $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $entryData'serverId')); } @@ -990,50 +1015,65 @@ $collectionData->syncState->pendingdata = null; } - - if (!empty($clientModifications'added')) { - if ($this->_logger instanceof Zend_Log) - $this->_logger->info(__METHOD__ . '::' . __LINE__ . " remove previous synckey as client added new entries"); - $keepPreviousSyncKey = false; - } else { - $keepPreviousSyncKey = true; - } - $collectionData->syncState->lastsync = clone $this->_syncTimeStamp; // increment sync timestamp by 1 second $collectionData->syncState->lastsync->modify('+1 sec'); - - try { - $transactionId = Syncroton_Registry::getTransactionManager()->startTransaction(Syncroton_Registry::getDatabase()); - - // store new synckey - $this->_syncStateBackend->create($collectionData->syncState, $keepPreviousSyncKey); - - // store contentstates for new entries added to client - foreach($newContentStates as $state) { - $this->_contentStateBackend->create($state); - } - - // remove contentstates for entries to be deleted on client - foreach($deletedContentStates as $state) { - $this->_contentStateBackend->delete($state); + if (!empty($clientModifications'added')) { + // Store a client id mapping in case we encounter a reused sync_key in a future request. + $newClientIdMap = ; + foreach($clientModifications'added' as $entryData) { + $newClientIdMap$entryData'clientId' = $entryData'serverId'; } - - Syncroton_Registry::getTransactionManager()->commitTransaction($transactionId); - } catch (Zend_Db_Statement_Exception $zdse) { - // something went wrong - // maybe another parallel request added a new synckey - // we must remove data added from client - if (!empty($clientModifications'added')) { - foreach ($clientModifications'added' as $added) { - $this->_contentStateBackend->delete($added'contentState'); - $dataController->deleteEntry($collectionData->collectionId, $added'serverId', array()); + $collectionData->syncState->clientIdMap = Zend_Json::encode($newClientIdMap); + } + + //Retry in case of deadlock + $retries = 0; + while (True) { + try { + $transactionId = Syncroton_Registry::getTransactionManager()->startTransaction(Syncroton_Registry::getDatabase()); + // store new synckey + $this->_syncStateBackend->create($collectionData->syncState, true); + + // store contentstates for new entries added to client + foreach($newContentStates as $state) { + $this->_contentStateBackend->create($state); + } + + // remove contentstates for entries to be deleted on client + foreach($deletedContentStates as $state) { + $this->_contentStateBackend->delete($state); + } + + Syncroton_Registry::getTransactionManager()->commitTransaction($transactionId); + break; + } catch (Exception $zdse) { + $retries++; + if ($retries > 5) { + if ($this->_logger instanceof Zend_Log) + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . ' exception while storing new synckey. Aborting after 5 retries.'); + + // something went wrong + // maybe another parallel request added a new synckey + // we must remove data added from client + if (!empty($clientModifications'added')) { + foreach ($clientModifications'added' as $added) { + $this->_contentStateBackend->delete($added'contentState'); + $dataController->deleteEntry($collectionData->collectionId, $added'serverId', array()); + } + } + + Syncroton_Registry::getTransactionManager()->rollBack(); + + throw $zdse; } + + Syncroton_Registry::getTransactionManager()->rollBack(); + // Give the other transactions some time before we try again + sleep(1); + if ($this->_logger instanceof Zend_Log) + $this->_logger->warn(__METHOD__ . '::' . __LINE__ . ' error during transaction, trying again.'); } - - Syncroton_Registry::getTransactionManager()->rollBack(); - - throw $zdse; } }
View file
kolab-syncroton-2.3.23.tar.gz/lib/ext/Syncroton/Exception/Status/Settings.php -> kolab-syncroton-2.4.0.tar.gz/lib/ext/Syncroton/Exception/Status/Settings.php
Changed
@@ -32,11 +32,11 @@ * @var array */ protected $_errorMessages = array( - self::PROTOCOL_ERROR => "Protocol error"; - self::ACCESS_DENIED => "Access denied"; - self::SERVICE_UNAVAILABLE => "Server unavailable"; - self::INVALID_ARGUMENTS => "Invalid arguments"; - self::CONFLICTING_ARGUMENTS => "Conflicting arguments"; - self::DENIED_BY_POLICY => "Denied by policy. Disabled by administrator"; + self::PROTOCOL_ERROR => "Protocol error", + self::ACCESS_DENIED => "Access denied", + self::SERVICE_UNAVAILABLE => "Server unavailable", + self::INVALID_ARGUMENTS => "Invalid arguments", + self::CONFLICTING_ARGUMENTS => "Conflicting arguments", + self::DENIED_BY_POLICY => "Denied by policy. Disabled by administrator", ); }
View file
kolab-syncroton-2.3.23.tar.gz/lib/ext/Syncroton/Model/ISyncState.php -> kolab-syncroton-2.4.0.tar.gz/lib/ext/Syncroton/Model/ISyncState.php
Changed
@@ -19,6 +19,7 @@ * @property string counter * @property DateTime lastsync * @property string pendingdata + * @property string client_id_map */ interface Syncroton_Model_ISyncState {
View file
kolab-syncroton-2.3.23.tar.gz/lib/ext/Syncroton/Wbxml/Encoder.php -> kolab-syncroton-2.4.0.tar.gz/lib/ext/Syncroton/Wbxml/Encoder.php
Changed
@@ -20,27 +20,6 @@ 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 @@ -48,27 +27,6 @@ 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 @@ -151,67 +109,66 @@ { $_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); - - 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) - )); + $this->_traverseDom($_dom); + } + + 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; + } - fclose($tempStream); - xml_parser_free($parser); + 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); } - /** - * 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) + protected function _traverseDom($_dom) { - $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); + if ($_dom->childNodes->length == 0) { + return false; } - - list($nameSpace, $this->_currentTag) = explode(';', $_tag); - - if($this->_codePage->getNameSpace() != $nameSpace) { - $this->_switchCodePage($nameSpace); + // 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--; - $this->_attributes = $_attributes; - + return $foundElementNode; } - + /** * strip uri: from nameSpace * @@ -224,73 +181,6 @@ } /** - * 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 @@ -332,8 +222,6 @@ $this->_writeTerminatedString($_data); } } - - $this->_currentTagData = NULL; } /** @@ -343,30 +231,16 @@ */ 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); + $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); } -} \ No newline at end of file +}
View file
kolab-syncroton-2.3.23.tar.gz/lib/kolab_sync.php -> kolab-syncroton-2.4.0.tar.gz/lib/kolab_sync.php
Changed
@@ -48,7 +48,7 @@ public $task = null; const CHARSET = 'UTF-8'; - const VERSION = "2.3.22"; + const VERSION = "2.4.0"; /** @@ -301,7 +301,8 @@ // parse $host $a_host = parse_url($host); $port = null; - if ($a_host'host') { + $ssl = null; + if (!empty($a_host'host')) { $host = $a_host'host'; $ssl = (isset($a_host'scheme') && in_array($a_host'scheme', array('ssl','imaps','tls'))) ? $a_host'scheme' : null; if (!empty($a_host'port')) {
View file
kolab-syncroton-2.3.23.tar.gz/lib/kolab_sync_backend.php -> kolab-syncroton-2.4.0.tar.gz/lib/kolab_sync_backend.php
Changed
@@ -176,7 +176,7 @@ } // Activesync folder identifier (serverId) - $folder_type = $typedata$folder ?: 'mail'; + $folder_type = ($typedata$folder ?? null) ?: 'mail'; $folder_id = self::folder_id($folder, $folder_type); $folders_list$folder_id = $this->folder_data($folder, $folder_type); @@ -417,10 +417,7 @@ public function device_get($id) { $devices_list = $this->devices_list(); - - $result = $devices_list$id; - - return $result; + return $devices_list$id ?? null; } /** @@ -670,7 +667,7 @@ continue; } - $type = $foldertypes$folder ?: 'mail'; + $type = ($foldertypes$folder ?? null) ?: 'mail'; if ($type == 'mail' && isset($special_folders$folder)) { $type = $special_folders$folder; }
View file
kolab-syncroton-2.3.23.tar.gz/lib/kolab_sync_backend_common.php -> kolab-syncroton-2.4.0.tar.gz/lib/kolab_sync_backend_common.php
Changed
@@ -151,6 +151,10 @@ $result = $this->db->query('DELETE FROM `' . $this->table_name .'` WHERE `id` = ?', array($id)); + if ($this->db->is_error($result)) { + throw new Exception('Failed to delete instance of ' . $this->interface_name); + } + return (bool) $this->db->affected_rows($result); } @@ -175,8 +179,11 @@ $set = $this->db->quote_identifier($key) . ' = ?'; } - $this->db->query('UPDATE `' . $this->table_name . '` SET ' . implode(', ', $set) + $result = $this->db->query('UPDATE `' . $this->table_name . '` SET ' . implode(', ', $set) . ' WHERE `id` = ' . $this->db->quote($object->id), array_values($data)); + if ($this->db->is_error($result)) { + throw new Exception('Failed to update instance of ' . $this->interface_name); + } return $object; }
View file
kolab-syncroton-2.3.23.tar.gz/lib/kolab_sync_backend_state.php -> kolab-syncroton-2.4.0.tar.gz/lib/kolab_sync_backend_state.php
Changed
@@ -180,21 +180,27 @@ $where'folder_id' = $this->db->quote_identifier('folder_id') . ' = ' . $this->db->quote($folder_id); $where'is_deleted' = $this->db->quote_identifier('is_deleted') . ' = 1'; - // found more recent synckey => the last sync response got not received by the client + // found more recent synckey => the last sync response was not received by the client if ($next > $sync_key) { - $where'synckey' = $this->db->quote_identifier('creation_synckey') . ' = ' . $this->db->quote($state->counter); - // undelete entries marked as deleted in syncroton_content table - $this->db->query("UPDATE `syncroton_content` SET `is_deleted` = 0 WHERE " . implode(' AND ', $where)); - - // remove entries added during latest sync in syncroton_content table - unset($where'is_deleted'); - $where'synckey' = $this->db->quote_identifier('creation_synckey') . ' > ' . $this->db->quote($state->counter); - - $this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where)); + // We store the clientIdMap with the "next" sync state, so we need to copy it back. + $state->clientIdMap = $states$next->clientIdMap; } else { // finaly delete all entries marked for removal in syncroton_content table - $this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where)); + $retryCounter = 0; + while(True) { + $result = $this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where)); + if ($this->db->is_error($result)) { + $retryCounter++; + if ($retryCounter > 5) { + throw new Exception('Failed to delete entries in sync_key check'); + } + } else { + break; + } + //Give the other transactions some time before we try again + sleep(1); + } } // remove all other synckeys @@ -204,4 +210,18 @@ return $state; } + + public function haveNext($deviceid, $folderid, $sync_key) + { + $device_id = $deviceid instanceof Syncroton_Model_IDevice ? $deviceid->id : $deviceid; + $folder_id = $folderid instanceof Syncroton_Model_IFolder ? $folderid->id : $folderid; + + $where'device_id' = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id); + $where'type' = $this->db->quote_identifier('type') . ' = ' . $this->db->quote($folder_id); + $where'counter' = $this->db->quote_identifier('counter') . ' > ' . $this->db->quote($sync_key); + + $select = $this->db->query("SELECT id FROM `{$this->table_name}` WHERE " . implode(' AND ', $where)); + return $this->db->num_rows($select) > 0; + } + }
View file
kolab-syncroton-2.3.23.tar.gz/lib/kolab_sync_data.php -> kolab-syncroton-2.4.0.tar.gz/lib/kolab_sync_data.php
Changed
@@ -1011,7 +1011,7 @@ } // convert categories into tags, save them after creating an object - if (isset($this->tag_categories) && $this->tag_categories) { + if (!empty($data'categories') && isset($this->tag_categories) && $this->tag_categories) { $tags = $data'categories'; unset($data'categories'); } @@ -1462,16 +1462,17 @@ * @param int $type Result data type (to which the body will be converted, if specified). * One or array of Syncroton_Model_EmailBody constants. * - * @return string Body value + * @return string|null Body value */ protected function getBody($body, $type = null) { + $data = null; if ($body && $body->data) { $data = $body->data; } if (!$data || empty($type)) { - return; + return null; } $type = (array) $type; @@ -1813,17 +1814,20 @@ // handle exceptions from recurrence if (!empty($data->exceptions)) { foreach ($data->exceptions as $exception) { + $date = clone $exception->exceptionStartTime; + if ($timezone) { + $date->setTimezone($timezone); + } + if ($exception->deleted) { - $date = clone $exception->exceptionStartTime; - if ($timezone) { - $date->setTimezone($timezone); - } $date->setTime(0, 0, 0); $rrule'EXDATE' = $date; } else { $ex = $this->toKolab($exception, $folderid, null, $timezone); + $ex'recurrence_date' = $date; + if ($data->allDayEvent) { $ex'allday' = 1; }
View file
kolab-syncroton-2.3.23.tar.gz/lib/kolab_sync_data_calendar.php -> kolab-syncroton-2.4.0.tar.gz/lib/kolab_sync_data_calendar.php
Changed
@@ -236,17 +236,15 @@ break; case 'sensitivity': - if (empty($value)) { - continue; + if (!empty($value)) { + $value = intval($this->sensitivityMap$value); } - $value = intval($this->sensitivityMap$value); break; case 'free_busy': - if (empty($value)) { - continue; + if (!empty($value)) { + $value = $this->busyStatusMap$value; } - $value = $this->busyStatusMap$value; break; case 'description': @@ -495,7 +493,8 @@ else if (isset($data->attendees)) { $statusMap = array_flip($this->attendeeStatusMap); foreach ($data->attendees as $attendee) { - if ($attendee->email && $attendee->email == $organizer_email) { + if (!empty($organizer_email) && $attendee->email && !strcasecmp($attendee->email, $organizer_email)) { + // skip the organizer continue; } @@ -540,22 +539,23 @@ } } - // Make sure the event has the organizer set - if (!$organizer_email && ($identity = kolab_sync::get_instance()->user->get_identity())) { - $attendees = array( - 'role' => 'ORGANIZER', - 'name' => $identity'name', - 'email' => $identity'email', - ); + if (!$is_exception) { + // Make sure the event has the organizer set + if (!$organizer_email && ($identity = kolab_sync::get_instance()->user->get_identity())) { + $attendees = array( + 'role' => 'ORGANIZER', + 'name' => $identity'name', + 'email' => $identity'email', + ); + } + + // recurrence (and exceptions) + $event'recurrence' = $this->recurrence_to_kolab($data, $folderid, $timezone); } $event'attendees' = $attendees; $event'categories' = $categories; - - // recurrence (and exceptions) - if (!$is_exception) { - $event'recurrence' = $this->recurrence_to_kolab($data, $folderid, $timezone); - } + $event'exceptions' = isset($event'recurrence''EXCEPTIONS') ? $event'recurrence''EXCEPTIONS' : array(); // Bump SEQUENCE number on update (Outlook only). // It's been confirmed that any change of the event that has attendees specified @@ -807,14 +807,13 @@ */ protected function update_attendee_status(&$event, $status) { - $organizer = null; - $emails = $this->user_emails(); + $emails = $this->user_emails(); foreach ((array) $event'attendees' as $i => $attendee) { - if ($attendee'role' == 'ORGANIZER') { - $organizer = $attendee; - } - else if ($attendee'email' && in_array_nocase($attendee'email', $emails)) { + if (!empty($attendee'email') + && (empty($attendee'role') || $attendee'role' != 'ORGANIZER') + && in_array_nocase($attendee'email', $emails) + ) { $event'attendees'$i'status' = $status; $event'attendees'$i'rsvp' = false; $event_attendee = $attendee; @@ -822,8 +821,14 @@ } if (!$event_attendee) { - $this->logger->warn('MeetingResponse on an event where the user is not an attendee. UID: ' . $event'uid'); - throw new Syncroton_Exception_Status_MeetingResponse(Syncroton_Exception_Status_MeetingResponse::MEETING_ERROR); + // Add the user to the attendees list + $event'attendees' = array( + 'role' => 'OPT-PARTICIPANT', + 'name' => '', + 'email' => $emails0, + 'status' => $status, + 'rsvp' => false, + ); } }
View file
kolab-syncroton-2.3.23.tar.gz/lib/kolab_sync_data_email.php -> kolab-syncroton-2.4.0.tar.gz/lib/kolab_sync_data_email.php
Changed
@@ -283,6 +283,7 @@ // In Sync examples there's one in which bodyPreferences is not defined // in such case Truncated=1 and there's no body sent to the client // only it's estimated size + $isTruncated = 0; if (empty($prefs)) { $messageBody = ''; $real_length = $headers->size;
View file
kolab-syncroton-2.3.23.tar.gz/lib/kolab_sync_data_tasks.php -> kolab-syncroton-2.4.0.tar.gz/lib/kolab_sync_data_tasks.php
Changed
@@ -140,7 +140,9 @@ break; case 'sensitivity': - $value = intval($this->sensitivityMap$value); + if (!empty($value)) { + $value = intval($this->sensitivityMap$value); + } break; case 'priority': @@ -156,7 +158,9 @@ } // convert kolab tags into categories - $result'categories' = $this->getKolabTags($task'uid', $result'categories'); + if (!empty($result'categories')) { + $result'categories' = $this->getKolabTags($task'uid', $result'categories'); + } // Recurrence $this->recurrence_from_kolab($collection, $task, $result, 'Task'); @@ -222,7 +226,7 @@ case 'sensitivity': $map = array_flip($this->sensitivityMap); - $value = $map$value; + $value = $map$value ?? 'none' ?? self::SENSITIVITY_NORMAL; break; case 'description': @@ -245,9 +249,13 @@ $task'status' = 'COMPLETED'; $task'complete' = 100; } - else if (isset($data->complete) && ($task'status' == 'COMPLETED' || $task'complete' == 100)) { - $task'status' = ''; - $task'complete' = 0; + else if (isset($data->complete)) { + if ((!empty($task'status') && $task'status' == 'COMPLETED') + || (!empty($task'complete') && $task'complete' == 100) + ) { + $task'status' = ''; + $task'complete' = 0; + } } // recurrence
View file
kolab-syncroton-2.4.0.tar.gz/tests/wbxml.php
Added
@@ -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('AwFqAEVcT0sDMAABUgMzOGI5NTBlYmQ2MmNkOWE2NjkyOWM4OTYxNWQwZmMwNAABXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAFiAzIAAWMDOAABABFFRgM0AAFIAzEAAQEBAQAAT0sDMAABUgNjY2ExYjgxYzczNGFiYmNkNjY5YmVhOTBkMjNlMDhhZQABYAAEEQ4lDSgFJyYXEhsGJBQHGDQzAQAAXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAEAEUVGAzEAAUgDMQABAQEBAABPSwMwAAFSA0NvbnRhY3RzOjpTeW5jcm90b24AAV4DMAABUwMwAAFVAzUxMgABV1gDMAABABFFRgMxAAFIAzEAAQEBAQAAT0sDMAABUgNkYjBkOTU5YTNhZWIyMTc1N2Y4ODQ5YTgzMDk0N2E3YQABXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAFiAzIAAWMDOAABABFFRgM0AAFIAzEAAQEBAQAAT0sDMAABUgNjZjUyOWM3OTJmYzg3ZDFmMjA3NDM1YjM5MjFiYjAyZQABXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAFiAzIAAWMDOAABABFFRgM0AAFIAzEAAQEBAQAAT0sDMAABUgM5MDMzNTg4MGY2NWRlZmY2ZTUyMWFjZWEyYjcxYTc3MwABXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAEAEUVGAzEAAUgDMQABAQEBAABPSwMwAAFSAzFiYjhjNTVmZTg0ZDUyYzY5NjhkYjI1NzFmN2RjMTI0AAFeAzAAAVMDMAABVQM1MTIAAVdYAzAAAWIDMgABYwM4AAEAEUVGAzQAAUgDMQABAQEBAABPSwMwAAFSAzcxNWVkOWVhMjliOGE1Mzc3YTY5YzFmNzU4MDM3YzY1AAFeAzAAAVMDMAABVQM1MTIAAVdYAzAAAWIDMgABYwM4AAEAEUVGAzQAAUgDMQABAQEBAABPSwMwAAFSA2I1MWFiZTczZTllOThmZTIwMGE0YWZlNDA5MDUwNTAyAAFeAzAAAVMDMAABVQM1MTIAAVdYAzAAAWIDMgABYwM4AAEAEUVGAzQAAUgDMQABAQEBAABPSwMwAAFSAzBmNjYzODg4MDY3NDNjNTE0YjgwNjNiZjBkYzg3NDg2AAFgAAQRDiUNKAUnJhcSGwYkFAcYNDMBAABeAzAAAVMDMAABVQM1MTIAAVdYAzAAAQARRUYDMQABSAMxAAEBAQEAAE9LAzAAAVIDMjY4NWIzMDJiNzlmNThkMjc1MzE5OTU0NWUzY2I4YmUAAWAABBEOJQ0oBScmFxIbBiQUBxg0MwEAAF4DMAABUwMwAAFVAzUxMgABV1gDMAABABFFRgMxAAFIAzEAAQEBAQAAT0sDMAABUgMzODRjZjJkODc3YzM5YTYyMmZkYzJhMTY4OTgwNTJlMgABYAAEEQ4lDSgFJyYXEhsGJBQHGDQzAQAAXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAEAEUVGAzEAAUgDMQABAQEBAABPSwMwAAFSAzk3NzBiMDgzYzY4ZTg1ODRmMzk2ZDE1YTExNmQ2NjA4AAFgAAQRDiUNKAUnJhcSGwYkFAcYNDMBAABeAzAAAVMDMAABVQM1MTIAAVdYAzAAAQARRUYDMQABSAMxAAEBAQEAAE9LAzAAAVIDOWU3Yjk2NTZlZjYxZDRhZjJmYjJmZGNhYmU2MDAwNzkAAWAABBEOJQ0oBScmFxIbBiQUBxg0MwEAAF4DMAABUwMwAAFVAzUxMgABV1gDMAABABFFRgMxAAFIAzEAAQEBAQAAT0sDMAABUgNhYjFkZGI0ZWY4ZThmOGZjYzJjOWY1YTdmOTA2MjQ1MgABYAAEEQ4lDSgFJyYXEhsGJBQHGDQzAQAAXgMwAAFTAzAAAVUDNTEyAAFXWAMwAAEAEUVGAzEAAUgDMQABAQEBAABPSwMwAAFSA2Q5OGJkODcyMTM3MTU0NGVkMDk1ODQxZWFkOTQxODkzAAFgAAQRDiUNKAUnJhcSGwYkFAcYNDMBAABeAzAAAVMDMAABVQM1MTIAAVdYAzAAAQARRUYDMQABSAMxAAEBAQEBAABVAzE2AAEB'), + $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); + } +} +
View file
kolab-syncroton.dsc
Changed
@@ -2,7 +2,7 @@ Source: kolab-syncroton Binary: kolab-syncroton Architecture: all -Version: 2.3.23-1~kolab1 +Version: 2.4.0-1~kolab1 Maintainer: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> Uploaders: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> Homepage: http://www.kolab.org/ @@ -12,5 +12,5 @@ Package-List: kolab-syncroton deb utils extra Files: - 00000000000000000000000000000000 0 kolab-syncroton-2.3.23.tar.gz + 00000000000000000000000000000000 0 kolab-syncroton-2.4.0.tar.gz 00000000000000000000000000000000 0 debian.tar.gz
View file
plesk.kolab_syncroton.inc.php
Changed
@@ -118,8 +118,6 @@ $config'activesync_multifolder_blacklist_note' = null; $config'activesync_multifolder_blacklist_task' = null; -$config'activesync_protected_folders' = array('windowsoutlook' => array('INBOX', 'Sent', 'Trash')); - // Enables adding sender name in the From: header of send email // when a device uses email address only (e.g. iOS devices) $config'activesync_fix_from' = true;
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.