We truncated the diff of some files because they were too big.
If you want to see the full diff for every file, click here.
Changes of Revision 50
kolab-syncroton.spec
Changed
x
1
2
%global _ap_sysconfdir %{_sysconfdir}/%{httpd_name}
3
4
Name: kolab-syncroton
5
-Version: 2.3.23
6
+Version: 2.4.0
7
Release: 1%{?dist}
8
Summary: ActiveSync for Kolab Groupware
9
10
11
License: LGPLv2
12
URL: http://www.syncroton.org
13
14
-Source0: https://mirror.kolabenterprise.com/pub/releases/%{name}-%{version}.tar.gz
15
+Source0: %{name}-%{version}.tar.gz
16
Source1: kolab-syncroton.logrotate
17
Source2: plesk.kolab_syncroton.inc.php
18
19
20
%endif
21
%endif
22
23
+Requires: roundcubemail
24
Requires: logrotate
25
%if 0%{?rhel} < 8
26
Requires: php-kolabformat
27
28
fi
29
30
/usr/share/roundcubemail/bin/updatedb.sh \
31
- --dir /usr/share/doc/kolab-syncroton-%{version}/SQL/ \
32
+ --dir /usr/share/doc/kolab-syncroton/SQL/ \
33
--package syncroton \
34
>/dev/null 2>&1 || :
35
36
37
%attr(0770,%{httpd_user},%{httpd_group}) %{_var}/log/%{name}
38
39
%changelog
40
+* Wed May 10 2023 Christian Mollekopf <mollekopf@apheleia-it.ch> - 2.4.0-1
41
+- Release version 2.4.0
42
+
43
* Fri Feb 04 2022 Jeroen van Meeuwen <vanmeeuwen@apheleia-it.ch> - 2.3.22-1
44
- Release version 2.3.22
45
46
debian.changelog
Changed
11
1
2
+kolab-syncroton (2.4.0-0~kolab1) unstable; urgency=low
3
+
4
+ * Release version 2.4.0
5
+
6
+ -- Christian Mollekopf <mollekopf@apheleia-it.ch> Wed, 10 May 2023 15:13:40 +0200
7
+
8
kolab-syncroton (2.3.23-0~kolab1) unstable; urgency=low
9
10
* Release version 2.3.23
11
kolab-syncroton-2.4.0.tar.gz/docs/SQL/mysql/2023100500.sql
Added
4
1
2
+
3
+ALTER TABLE `syncroton_synckey` ADD `client_id_map` longblob DEFAULT NULL;
4
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
9
1
2
3
// Out-of-Office
4
if (!empty($this->_OofGet)) {
5
+ $OofGet = null;
6
try {
7
$OofGet = $this->_deviceBackend->getOOF($this->_OofGet);
8
} catch (Exception $e) {
9
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
201
1
2
continue;
3
}
4
5
- // check for invalid sycnkey
6
+ $syncKeyReused = $this->_syncStateBackend->haveNext($this->_device, $collectionData->folder, $collectionData->syncKey);
7
+ if ($syncKeyReused) {
8
+ if ($this->_logger instanceof Zend_Log)
9
+ $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " already known synckey {$collectionData->syncKey} provided");
10
+ }
11
+ // check for invalid synckey
12
if(($collectionData->syncState = $this->_syncStateBackend->validate($this->_device, $collectionData->folder, $collectionData->syncKey)) === false) {
13
if ($this->_logger instanceof Zend_Log)
14
$this->_logger->warn(__METHOD__ . '::' . __LINE__ . " invalid synckey {$collectionData->syncKey} provided");
15
16
if ($this->_logger instanceof Zend_Log)
17
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($adds) . " entries to be added to server");
18
19
+ $clientIdMap = ;
20
+ if ($syncKeyReused && $collectionData->syncState->clientIdMap) {
21
+ $clientIdMap = Zend_Json::decode($collectionData->syncState->clientIdMap);
22
+ }
23
+
24
foreach ($adds as $add) {
25
if ($this->_logger instanceof Zend_Log)
26
$this->_logger->debug(__METHOD__ . '::' . __LINE__ . " add entry with clientId " . (string) $add->ClientId);
27
28
if ($this->_logger instanceof Zend_Log)
29
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " adding entry as new");
30
31
- $serverId = $dataController->createEntry($collectionData->collectionId, new $dataClass($add->ApplicationData));
32
-
33
- $clientModifications'added'$serverId = array(
34
- 'clientId' => (string)$add->ClientId,
35
- 'serverId' => $serverId,
36
- 'status' => self::STATUS_SUCCESS,
37
- 'contentState' => $this->_contentStateBackend->create(new Syncroton_Model_Content(array(
38
- 'device_id' => $this->_device,
39
- 'folder_id' => $collectionData->folder,
40
- 'contentid' => $serverId,
41
- 'creation_time' => $this->_syncTimeStamp,
42
- 'creation_synckey' => $collectionData->syncKey + 1
43
- )))
44
- );
45
+ $clientId = (string)$add->ClientId;
46
+ // If the sync key was reused, but we don't have a $clientId mapping,
47
+ // this means the client sent a new item with the same sync_key.
48
+ if ($syncKeyReused && array_key_exists($clientId, $clientIdMap)) {
49
+ // We don't normally store the clientId, so if a command with Add's is resent,
50
+ // we have to look-up the corresponding serverId using a cached clientId => serverId mapping,
51
+ // otherwise we would duplicate all added items on resend.
52
+ $serverId = $clientIdMap$clientId;
53
+ $clientModifications'added'$serverId = array(
54
+ 'clientId' => (string)$add->ClientId,
55
+ 'serverId' => $serverId,
56
+ 'status' => self::STATUS_SUCCESS,
57
+ 'contentState' => null
58
+ );
59
+ } else {
60
+ $serverId = $dataController->createEntry($collectionData->collectionId, new $dataClass($add->ApplicationData));
61
+ $clientModifications'added'$serverId = array(
62
+ 'clientId' => (string)$add->ClientId,
63
+ 'serverId' => $serverId,
64
+ 'status' => self::STATUS_SUCCESS,
65
+ 'contentState' => $this->_contentStateBackend->create(new Syncroton_Model_Content(array(
66
+ 'device_id' => $this->_device,
67
+ 'folder_id' => $collectionData->folder,
68
+ 'contentid' => $serverId,
69
+ 'creation_time' => $this->_syncTimeStamp,
70
+ 'creation_synckey' => $collectionData->syncKey + 1
71
+ )))
72
+ );
73
+ }
74
75
} catch (Exception $e) {
76
if ($this->_logger instanceof Zend_Log)
77
78
}
79
80
// handle changes, but only if not first sync
81
- if($collectionData->syncKey > 1 && $collectionData->hasClientChanges()) {
82
+ if(!$syncKeyReused && $collectionData->syncKey > 1 && $collectionData->hasClientChanges()) {
83
$changes = $collectionData->getClientChanges();
84
85
if ($this->_logger instanceof Zend_Log)
86
87
}
88
89
// handle deletes, but only if not first sync
90
- if($collectionData->hasClientDeletes()) {
91
+ if(!$syncKeyReused && $collectionData->hasClientDeletes()) {
92
$deletes = $collectionData->getClientDeletes();
93
if ($this->_logger instanceof Zend_Log)
94
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " found " . count($deletes) . " entries to be deleted on server");
95
96
if ($this->_logger instanceof Zend_Log)
97
$this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped added entry: " . $serverId);
98
unset($serverModifications'added'$id);
99
- }
100
}
101
+ }
102
103
// entries to be deleted
104
$serverModifications'deleted' = array_diff($allClientEntries, $allServerEntries);
105
106
foreach($clientModifications'added' as $entryData) {
107
$add = $responses->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Add'));
108
$add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ClientId', $entryData'clientId'));
109
- // we have no serverId is the add failed
110
+ // we have no serverId if the add failed
111
if(isset($entryData'serverId')) {
112
$add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $entryData'serverId'));
113
}
114
115
$collectionData->syncState->pendingdata = null;
116
}
117
118
-
119
- if (!empty($clientModifications'added')) {
120
- if ($this->_logger instanceof Zend_Log)
121
- $this->_logger->info(__METHOD__ . '::' . __LINE__ . " remove previous synckey as client added new entries");
122
- $keepPreviousSyncKey = false;
123
- } else {
124
- $keepPreviousSyncKey = true;
125
- }
126
-
127
$collectionData->syncState->lastsync = clone $this->_syncTimeStamp;
128
// increment sync timestamp by 1 second
129
$collectionData->syncState->lastsync->modify('+1 sec');
130
-
131
- try {
132
- $transactionId = Syncroton_Registry::getTransactionManager()->startTransaction(Syncroton_Registry::getDatabase());
133
-
134
- // store new synckey
135
- $this->_syncStateBackend->create($collectionData->syncState, $keepPreviousSyncKey);
136
-
137
- // store contentstates for new entries added to client
138
- foreach($newContentStates as $state) {
139
- $this->_contentStateBackend->create($state);
140
- }
141
-
142
- // remove contentstates for entries to be deleted on client
143
- foreach($deletedContentStates as $state) {
144
- $this->_contentStateBackend->delete($state);
145
+ if (!empty($clientModifications'added')) {
146
+ // Store a client id mapping in case we encounter a reused sync_key in a future request.
147
+ $newClientIdMap = ;
148
+ foreach($clientModifications'added' as $entryData) {
149
+ $newClientIdMap$entryData'clientId' = $entryData'serverId';
150
}
151
-
152
- Syncroton_Registry::getTransactionManager()->commitTransaction($transactionId);
153
- } catch (Zend_Db_Statement_Exception $zdse) {
154
- // something went wrong
155
- // maybe another parallel request added a new synckey
156
- // we must remove data added from client
157
- if (!empty($clientModifications'added')) {
158
- foreach ($clientModifications'added' as $added) {
159
- $this->_contentStateBackend->delete($added'contentState');
160
- $dataController->deleteEntry($collectionData->collectionId, $added'serverId', array());
161
+ $collectionData->syncState->clientIdMap = Zend_Json::encode($newClientIdMap);
162
+ }
163
+
164
+ //Retry in case of deadlock
165
+ $retries = 0;
166
+ while (True) {
167
+ try {
168
+ $transactionId = Syncroton_Registry::getTransactionManager()->startTransaction(Syncroton_Registry::getDatabase());
169
+ // store new synckey
170
+ $this->_syncStateBackend->create($collectionData->syncState, true);
171
+
172
+ // store contentstates for new entries added to client
173
+ foreach($newContentStates as $state) {
174
+ $this->_contentStateBackend->create($state);
175
+ }
176
+
177
+ // remove contentstates for entries to be deleted on client
178
+ foreach($deletedContentStates as $state) {
179
+ $this->_contentStateBackend->delete($state);
180
+ }
181
+
182
+ Syncroton_Registry::getTransactionManager()->commitTransaction($transactionId);
183
+ break;
184
+ } catch (Exception $zdse) {
185
+ $retries++;
186
+ if ($retries > 5) {
187
+ if ($this->_logger instanceof Zend_Log)
188
+ $this->_logger->warn(__METHOD__ . '::' . __LINE__ . ' exception while storing new synckey. Aborting after 5 retries.');
189
+
190
+ // something went wrong
191
+ // maybe another parallel request added a new synckey
192
+ // we must remove data added from client
193
+ if (!empty($clientModifications'added')) {
194
+ foreach ($clientModifications'added' as $added) {
195
+ $this->_contentStateBackend->delete($added'contentState');
196
+ $dataController->deleteEntry($collectionData->collectionId, $added'serverId', array());
197
+ }
198
+ }
199
+
200
+ Syncroton_Registry::getTransactionManager()->rollBack();
201
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
19
1
2
* @var array
3
*/
4
protected $_errorMessages = array(
5
- self::PROTOCOL_ERROR => "Protocol error";
6
- self::ACCESS_DENIED => "Access denied";
7
- self::SERVICE_UNAVAILABLE => "Server unavailable";
8
- self::INVALID_ARGUMENTS => "Invalid arguments";
9
- self::CONFLICTING_ARGUMENTS => "Conflicting arguments";
10
- self::DENIED_BY_POLICY => "Denied by policy. Disabled by administrator";
11
+ self::PROTOCOL_ERROR => "Protocol error",
12
+ self::ACCESS_DENIED => "Access denied",
13
+ self::SERVICE_UNAVAILABLE => "Server unavailable",
14
+ self::INVALID_ARGUMENTS => "Invalid arguments",
15
+ self::CONFLICTING_ARGUMENTS => "Conflicting arguments",
16
+ self::DENIED_BY_POLICY => "Denied by policy. Disabled by administrator",
17
);
18
}
19
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
9
1
2
* @property string counter
3
* @property DateTime lastsync
4
* @property string pendingdata
5
+ * @property string client_id_map
6
*/
7
interface Syncroton_Model_ISyncState
8
{
9
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
201
1
2
class Syncroton_Wbxml_Encoder extends Syncroton_Wbxml_Abstract
3
{
4
/**
5
- * stack of dtd objects
6
- *
7
- * @var array
8
- */
9
- protected $_dtdStack = array();
10
-
11
- /**
12
- * stack of stream resources
13
- *
14
- * @var array
15
- */
16
- protected $_streamStack = array();
17
-
18
- /**
19
- * stack of levels when to pop data from the other stacks
20
- *
21
- * @var array
22
- */
23
- protected $_popStack = array();
24
-
25
- /**
26
* count level of tags
27
*
28
* @var string
29
30
protected $_level = 0;
31
32
/**
33
- * when to take data next time from the different stacks
34
- *
35
- * @var unknown_type
36
- */
37
- protected $_nextStackPop = NULL;
38
-
39
- /**
40
- * collect data trough different calls to _handleCharacters
41
- *
42
- * @var string
43
- */
44
- protected $_currentTagData = NULL;
45
-
46
- /**
47
- * the current tag as read by the parser
48
- *
49
- * @var string
50
- */
51
- protected $_currentTag = NULL;
52
-
53
- /**
54
* the constructor
55
*
56
* @param resource $_stream
57
58
{
59
$_dom->formatOutput = false;
60
61
- $tempStream = tmpfile();
62
-
63
- $meta_data = stream_get_meta_data($tempStream);
64
- $filename = $meta_data"uri";
65
- $_dom->save($filename);
66
- rewind($tempStream);
67
-
68
$this->_initialize($_dom);
69
-
70
- $parser = xml_parser_create_ns($this->_charSet, ';');
71
- xml_set_object($parser, $this);
72
- xml_set_element_handler($parser, '_handleStartTag', '_handleEndTag');
73
- xml_set_character_data_handler($parser, '_handleCharacters');
74
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
75
-
76
- while (!feof($tempStream)) {
77
- if (!xml_parse($parser, fread($tempStream, 1048576), feof($tempStream))) {
78
- // uncomment to write xml document to file
79
- #rewind($tempStream);
80
- #$xmlStream = fopen(tempnam(sys_get_temp_dir(), "xmlerrors"), 'r+');
81
- #stream_copy_to_stream($tempStream, $xmlStream);
82
- #fclose($xmlStream);
83
-
84
- throw new Syncroton_Wbxml_Exception(sprintf('XML error: %s at line %d',
85
- xml_error_string(xml_get_error_code($parser)),
86
- xml_get_current_line_number($parser)
87
- ));
88
+ $this->_traverseDom($_dom);
89
+ }
90
+
91
+ private function getAttributes($node)
92
+ {
93
+ $attributes = array();
94
+ if ($node->attributes) {
95
+ for ($i = 0; $i < $node->attributes->length; ++$i) {
96
+ $attributes$node->attributes->item($i)->name = $node->attributes->item($i)->value;
97
}
98
}
99
+ return $attributes;
100
+ }
101
102
- fclose($tempStream);
103
- xml_parser_free($parser);
104
+ private function writeNode($node, $withContent = false, $data = null) {
105
+ if($this->_codePage->getNameSpace() != $node->namespaceURI) {
106
+ $this->_switchCodePage($node->namespaceURI);
107
+ }
108
+ $this->_writeTag($node->localName, $this->getAttributes($node), $withContent, $data);
109
}
110
111
- /**
112
- * get's called by xml parser when tag starts
113
- *
114
- * @param resource $_parser
115
- * @param string $_tag current tag prefixed with namespace
116
- * @param array $_attributes list of tag attributes
117
- */
118
- protected function _handleStartTag($_parser, $_tag, $_attributes)
119
+ protected function _traverseDom($_dom)
120
{
121
- $this->_level++;
122
- $this->_currentTagData = null;
123
-
124
- // write data for previous tag happens whith <tag1><tag2>
125
- if($this->_currentTag !== NULL) {
126
- $this->_writeTag($this->_currentTag, $this->_attributes, true);
127
+ if ($_dom->childNodes->length == 0) {
128
+ return false;
129
}
130
-
131
- list($nameSpace, $this->_currentTag) = explode(';', $_tag);
132
-
133
- if($this->_codePage->getNameSpace() != $nameSpace) {
134
- $this->_switchCodePage($nameSpace);
135
+ // print(str_pad("", $this->_level, " ") . "traversing {$_dom->nodeName}" . "\n");
136
+ $this->_level++;
137
+ $prevNode = $_dom;
138
+ $foundElementNode = false;
139
+ foreach ($_dom->childNodes as $node) {
140
+ if ($node->nodeType == XML_ELEMENT_NODE) {
141
+ $foundElementNode = true;
142
+ if ($prevNode && $this->_level > 1) {
143
+ // print(str_pad("", $this->_level, " ") . "{$node->nodeName} creating parent {$prevNode->nodeName}" . "\n");
144
+ $this->writeNode($prevNode, true);
145
+ $prevNode = null;
146
+ }
147
+ if (!$this->_traverseDom($node)) {
148
+ // print(str_pad("", $this->_level, " ") . "{$node->nodeName} content {$node->nodeValue}" . "\n");
149
+ $data = $node->nodeValue;
150
+ if (strlen($data) == 0) {
151
+ $this->writeNode($node);
152
+ } else {
153
+ $this->writeNode($node, true, $data);
154
+ $this->_writeByte(Syncroton_Wbxml_Abstract::END);
155
+ // print("Closing tag after writing tag\n");
156
+ }
157
+ } else {
158
+ $this->_writeByte(Syncroton_Wbxml_Abstract::END);
159
+ // print("Closing tag\n");
160
+ }
161
+ }
162
}
163
+ $this->_level--;
164
165
- $this->_attributes = $_attributes;
166
-
167
+ return $foundElementNode;
168
}
169
-
170
+
171
/**
172
* strip uri: from nameSpace
173
*
174
175
}
176
177
/**
178
- * get's called by xml parser when tag ends
179
- *
180
- * @param resource $_parser
181
- * @param string $_tag current tag prefixed with namespace
182
- */
183
- protected function _handleEndTag($_parser, $_tag)
184
- {
185
- #echo "$_tag Level: $this->_level == $this->_nextStackPop \n";
186
-
187
- if($this->_nextStackPop !== NULL && $this->_nextStackPop == $this->_level) {
188
- #echo "TAG: $_tag\n";
189
- $this->_writeByte(Syncroton_Wbxml_Abstract::END);
190
-
191
- $subStream = $this->_stream;
192
- $subStreamLength = ftell($subStream);
193
-
194
- $this->_dtd = array_pop($this->_dtdStack);
195
- $this->_stream = array_pop($this->_streamStack);
196
- $this->_nextStackPop = array_pop($this->_popStack);
197
- $this->_codePage = $this->_dtd->getCurrentCodePage();
198
-
199
- rewind($subStream);
200
- #while (!feof($subStream)) {$buffer = fgets($subStream, 4096);echo $buffer;}
201
kolab-syncroton-2.3.23.tar.gz/lib/kolab_sync.php -> kolab-syncroton-2.4.0.tar.gz/lib/kolab_sync.php
Changed
20
1
2
public $task = null;
3
4
const CHARSET = 'UTF-8';
5
- const VERSION = "2.3.22";
6
+ const VERSION = "2.4.0";
7
8
9
/**
10
11
// parse $host
12
$a_host = parse_url($host);
13
$port = null;
14
- if ($a_host'host') {
15
+ $ssl = null;
16
+ if (!empty($a_host'host')) {
17
$host = $a_host'host';
18
$ssl = (isset($a_host'scheme') && in_array($a_host'scheme', array('ssl','imaps','tls'))) ? $a_host'scheme' : null;
19
if (!empty($a_host'port')) {
20
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
31
1
2
}
3
4
// Activesync folder identifier (serverId)
5
- $folder_type = $typedata$folder ?: 'mail';
6
+ $folder_type = ($typedata$folder ?? null) ?: 'mail';
7
$folder_id = self::folder_id($folder, $folder_type);
8
9
$folders_list$folder_id = $this->folder_data($folder, $folder_type);
10
11
public function device_get($id)
12
{
13
$devices_list = $this->devices_list();
14
-
15
- $result = $devices_list$id;
16
-
17
- return $result;
18
+ return $devices_list$id ?? null;
19
}
20
21
/**
22
23
continue;
24
}
25
26
- $type = $foldertypes$folder ?: 'mail';
27
+ $type = ($foldertypes$folder ?? null) ?: 'mail';
28
if ($type == 'mail' && isset($special_folders$folder)) {
29
$type = $special_folders$folder;
30
}
31
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
25
1
2
3
$result = $this->db->query('DELETE FROM `' . $this->table_name .'` WHERE `id` = ?', array($id));
4
5
+ if ($this->db->is_error($result)) {
6
+ throw new Exception('Failed to delete instance of ' . $this->interface_name);
7
+ }
8
+
9
return (bool) $this->db->affected_rows($result);
10
}
11
12
13
$set = $this->db->quote_identifier($key) . ' = ?';
14
}
15
16
- $this->db->query('UPDATE `' . $this->table_name . '` SET ' . implode(', ', $set)
17
+ $result = $this->db->query('UPDATE `' . $this->table_name . '` SET ' . implode(', ', $set)
18
. ' WHERE `id` = ' . $this->db->quote($object->id), array_values($data));
19
+ if ($this->db->is_error($result)) {
20
+ throw new Exception('Failed to update instance of ' . $this->interface_name);
21
+ }
22
23
return $object;
24
}
25
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
59
1
2
$where'folder_id' = $this->db->quote_identifier('folder_id') . ' = ' . $this->db->quote($folder_id);
3
$where'is_deleted' = $this->db->quote_identifier('is_deleted') . ' = 1';
4
5
- // found more recent synckey => the last sync response got not received by the client
6
+ // found more recent synckey => the last sync response was not received by the client
7
if ($next > $sync_key) {
8
- $where'synckey' = $this->db->quote_identifier('creation_synckey') . ' = ' . $this->db->quote($state->counter);
9
- // undelete entries marked as deleted in syncroton_content table
10
- $this->db->query("UPDATE `syncroton_content` SET `is_deleted` = 0 WHERE " . implode(' AND ', $where));
11
-
12
- // remove entries added during latest sync in syncroton_content table
13
- unset($where'is_deleted');
14
- $where'synckey' = $this->db->quote_identifier('creation_synckey') . ' > ' . $this->db->quote($state->counter);
15
-
16
- $this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where));
17
+ // We store the clientIdMap with the "next" sync state, so we need to copy it back.
18
+ $state->clientIdMap = $states$next->clientIdMap;
19
}
20
else {
21
// finaly delete all entries marked for removal in syncroton_content table
22
- $this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where));
23
+ $retryCounter = 0;
24
+ while(True) {
25
+ $result = $this->db->query("DELETE FROM `syncroton_content` WHERE " . implode(' AND ', $where));
26
+ if ($this->db->is_error($result)) {
27
+ $retryCounter++;
28
+ if ($retryCounter > 5) {
29
+ throw new Exception('Failed to delete entries in sync_key check');
30
+ }
31
+ } else {
32
+ break;
33
+ }
34
+ //Give the other transactions some time before we try again
35
+ sleep(1);
36
+ }
37
}
38
39
// remove all other synckeys
40
41
42
return $state;
43
}
44
+
45
+ public function haveNext($deviceid, $folderid, $sync_key)
46
+ {
47
+ $device_id = $deviceid instanceof Syncroton_Model_IDevice ? $deviceid->id : $deviceid;
48
+ $folder_id = $folderid instanceof Syncroton_Model_IFolder ? $folderid->id : $folderid;
49
+
50
+ $where'device_id' = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id);
51
+ $where'type' = $this->db->quote_identifier('type') . ' = ' . $this->db->quote($folder_id);
52
+ $where'counter' = $this->db->quote_identifier('counter') . ' > ' . $this->db->quote($sync_key);
53
+
54
+ $select = $this->db->query("SELECT id FROM `{$this->table_name}` WHERE " . implode(' AND ', $where));
55
+ return $this->db->num_rows($select) > 0;
56
+ }
57
+
58
}
59
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
55
1
2
}
3
4
// convert categories into tags, save them after creating an object
5
- if (isset($this->tag_categories) && $this->tag_categories) {
6
+ if (!empty($data'categories') && isset($this->tag_categories) && $this->tag_categories) {
7
$tags = $data'categories';
8
unset($data'categories');
9
}
10
11
* @param int $type Result data type (to which the body will be converted, if specified).
12
* One or array of Syncroton_Model_EmailBody constants.
13
*
14
- * @return string Body value
15
+ * @return string|null Body value
16
*/
17
protected function getBody($body, $type = null)
18
{
19
+ $data = null;
20
if ($body && $body->data) {
21
$data = $body->data;
22
}
23
24
if (!$data || empty($type)) {
25
- return;
26
+ return null;
27
}
28
29
$type = (array) $type;
30
31
// handle exceptions from recurrence
32
if (!empty($data->exceptions)) {
33
foreach ($data->exceptions as $exception) {
34
+ $date = clone $exception->exceptionStartTime;
35
+ if ($timezone) {
36
+ $date->setTimezone($timezone);
37
+ }
38
+
39
if ($exception->deleted) {
40
- $date = clone $exception->exceptionStartTime;
41
- if ($timezone) {
42
- $date->setTimezone($timezone);
43
- }
44
$date->setTime(0, 0, 0);
45
$rrule'EXDATE' = $date;
46
}
47
else {
48
$ex = $this->toKolab($exception, $folderid, null, $timezone);
49
50
+ $ex'recurrence_date' = $date;
51
+
52
if ($data->allDayEvent) {
53
$ex'allday' = 1;
54
}
55
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
106
1
2
break;
3
4
case 'sensitivity':
5
- if (empty($value)) {
6
- continue;
7
+ if (!empty($value)) {
8
+ $value = intval($this->sensitivityMap$value);
9
}
10
- $value = intval($this->sensitivityMap$value);
11
break;
12
13
case 'free_busy':
14
- if (empty($value)) {
15
- continue;
16
+ if (!empty($value)) {
17
+ $value = $this->busyStatusMap$value;
18
}
19
- $value = $this->busyStatusMap$value;
20
break;
21
22
case 'description':
23
24
else if (isset($data->attendees)) {
25
$statusMap = array_flip($this->attendeeStatusMap);
26
foreach ($data->attendees as $attendee) {
27
- if ($attendee->email && $attendee->email == $organizer_email) {
28
+ if (!empty($organizer_email) && $attendee->email && !strcasecmp($attendee->email, $organizer_email)) {
29
+ // skip the organizer
30
continue;
31
}
32
33
34
}
35
}
36
37
- // Make sure the event has the organizer set
38
- if (!$organizer_email && ($identity = kolab_sync::get_instance()->user->get_identity())) {
39
- $attendees = array(
40
- 'role' => 'ORGANIZER',
41
- 'name' => $identity'name',
42
- 'email' => $identity'email',
43
- );
44
+ if (!$is_exception) {
45
+ // Make sure the event has the organizer set
46
+ if (!$organizer_email && ($identity = kolab_sync::get_instance()->user->get_identity())) {
47
+ $attendees = array(
48
+ 'role' => 'ORGANIZER',
49
+ 'name' => $identity'name',
50
+ 'email' => $identity'email',
51
+ );
52
+ }
53
+
54
+ // recurrence (and exceptions)
55
+ $event'recurrence' = $this->recurrence_to_kolab($data, $folderid, $timezone);
56
}
57
58
$event'attendees' = $attendees;
59
$event'categories' = $categories;
60
-
61
- // recurrence (and exceptions)
62
- if (!$is_exception) {
63
- $event'recurrence' = $this->recurrence_to_kolab($data, $folderid, $timezone);
64
- }
65
+ $event'exceptions' = isset($event'recurrence''EXCEPTIONS') ? $event'recurrence''EXCEPTIONS' : array();
66
67
// Bump SEQUENCE number on update (Outlook only).
68
// It's been confirmed that any change of the event that has attendees specified
69
70
*/
71
protected function update_attendee_status(&$event, $status)
72
{
73
- $organizer = null;
74
- $emails = $this->user_emails();
75
+ $emails = $this->user_emails();
76
77
foreach ((array) $event'attendees' as $i => $attendee) {
78
- if ($attendee'role' == 'ORGANIZER') {
79
- $organizer = $attendee;
80
- }
81
- else if ($attendee'email' && in_array_nocase($attendee'email', $emails)) {
82
+ if (!empty($attendee'email')
83
+ && (empty($attendee'role') || $attendee'role' != 'ORGANIZER')
84
+ && in_array_nocase($attendee'email', $emails)
85
+ ) {
86
$event'attendees'$i'status' = $status;
87
$event'attendees'$i'rsvp' = false;
88
$event_attendee = $attendee;
89
90
}
91
92
if (!$event_attendee) {
93
- $this->logger->warn('MeetingResponse on an event where the user is not an attendee. UID: ' . $event'uid');
94
- throw new Syncroton_Exception_Status_MeetingResponse(Syncroton_Exception_Status_MeetingResponse::MEETING_ERROR);
95
+ // Add the user to the attendees list
96
+ $event'attendees' = array(
97
+ 'role' => 'OPT-PARTICIPANT',
98
+ 'name' => '',
99
+ 'email' => $emails0,
100
+ 'status' => $status,
101
+ 'rsvp' => false,
102
+ );
103
}
104
}
105
106
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
9
1
2
// In Sync examples there's one in which bodyPreferences is not defined
3
// in such case Truncated=1 and there's no body sent to the client
4
// only it's estimated size
5
+ $isTruncated = 0;
6
if (empty($prefs)) {
7
$messageBody = '';
8
$real_length = $headers->size;
9
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
49
1
2
break;
3
4
case 'sensitivity':
5
- $value = intval($this->sensitivityMap$value);
6
+ if (!empty($value)) {
7
+ $value = intval($this->sensitivityMap$value);
8
+ }
9
break;
10
11
case 'priority':
12
13
}
14
15
// convert kolab tags into categories
16
- $result'categories' = $this->getKolabTags($task'uid', $result'categories');
17
+ if (!empty($result'categories')) {
18
+ $result'categories' = $this->getKolabTags($task'uid', $result'categories');
19
+ }
20
21
// Recurrence
22
$this->recurrence_from_kolab($collection, $task, $result, 'Task');
23
24
25
case 'sensitivity':
26
$map = array_flip($this->sensitivityMap);
27
- $value = $map$value;
28
+ $value = $map$value ?? 'none' ?? self::SENSITIVITY_NORMAL;
29
break;
30
31
case 'description':
32
33
$task'status' = 'COMPLETED';
34
$task'complete' = 100;
35
}
36
- else if (isset($data->complete) && ($task'status' == 'COMPLETED' || $task'complete' == 100)) {
37
- $task'status' = '';
38
- $task'complete' = 0;
39
+ else if (isset($data->complete)) {
40
+ if ((!empty($task'status') && $task'status' == 'COMPLETED')
41
+ || (!empty($task'complete') && $task'complete' == 100)
42
+ ) {
43
+ $task'status' = '';
44
+ $task'complete' = 0;
45
+ }
46
}
47
48
// recurrence
49
kolab-syncroton-2.4.0.tar.gz/tests/wbxml.php
Added
201
1
2
+<?php
3
+
4
+class message extends PHPUnit\Framework\TestCase
5
+{
6
+ //function testDecode()
7
+ //{
8
+ // //TODO input some wbxml document
9
+ // //
10
+ // $dom = new DOMDocument();
11
+ // $dom->loadXML($lastSyncCollection'lastXML');
12
+ // //
13
+ // try {
14
+ // $decoder = new Syncroton_Wbxml_Decoder($dom);
15
+ // $requestBody = $decoder->decode();
16
+ // if ($this->_logger instanceof Zend_Log) {
17
+ // $requestBody->formatOutput = true;
18
+ // $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml request:\n" . $requestBody->saveXML());
19
+ // }
20
+ // } catch(Syncroton_Wbxml_Exception_UnexpectedEndOfFile $e) {
21
+ // $requestBody = NULL;
22
+ // }
23
+ // //TODO validate output
24
+ //}
25
+
26
+
27
+
28
+ public function testEncode()
29
+ {
30
+ $outputStream = fopen("php://temp", 'r+');
31
+
32
+ $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
33
+
34
+ $xml = <<<EOF
35
+ <?xml version="1.0" encoding="utf-8"?>
36
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
37
+ <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Tasks="uri:Tasks">
38
+ <Collections>
39
+ <Collection>
40
+ <SyncKey>2</SyncKey>
41
+ <CollectionId>tasksId</CollectionId>
42
+ <Commands>
43
+ <Add>
44
+ <ClientId>clientId2</ClientId>
45
+ <ApplicationData>
46
+ <Subject xmlns="uri:Tasks">task2</Subject>
47
+ <Complete xmlns="uri:Tasks">0</Complete>
48
+ <DueDate xmlns="uri:Tasks">2020-11-04T00:00:00.000Z</DueDate>
49
+ <UtcDueDate xmlns="uri:Tasks">2020-11-03T23:00:00.000Z</UtcDueDate>
50
+ </ApplicationData>
51
+ </Add>
52
+ <Add>
53
+ <ClientId>clientId3</ClientId>
54
+ <ApplicationData>
55
+ <Subject xmlns="uri:Tasks">task3</Subject>
56
+ <Complete xmlns="uri:Tasks">0</Complete>
57
+ <DueDate xmlns="uri:Tasks">2020-11-04T00:00:00.000Z</DueDate>
58
+ <UtcDueDate xmlns="uri:Tasks">2020-11-03T23:00:00.000Z</UtcDueDate>
59
+ </ApplicationData>
60
+ </Add>
61
+ </Commands>
62
+ </Collection>
63
+ </Collections>
64
+ <WindowSize>16</WindowSize>
65
+ </Sync>
66
+ EOF;
67
+
68
+
69
+ $dom = new DOMDocument();
70
+ $dom->loadXML($xml);
71
+
72
+ $encoder->encode($dom);
73
+
74
+ rewind($outputStream);
75
+ $output = stream_get_contents($outputStream);
76
+ // print("----");
77
+ // print(var_export($output, true));
78
+ // print("----");
79
+ $this->assertEquals(
80
+ base64_decode('AwFqAEVcT0sDMgABUgN0YXNrc0lkAAFWR0wDY2xpZW50SWQyAAFdAAlgA3Rhc2syAAFKAzAAAUwDMjAyMC0xMS0wNFQwMDowMDowMC4wMDBaAAFNAzIwMjAtMTEtMDNUMjM6MDA6MDAuMDAwWgABAQEAAEdMA2NsaWVudElkMwABXQAJYAN0YXNrMwABSgMwAAFMAzIwMjAtMTEtMDRUMDA6MDA6MDAuMDAwWgABTQMyMDIwLTExLTAzVDIzOjAwOjAwLjAwMFoAAQEBAQEBAABVAzE2AAEB'),
81
+ $output
82
+ );
83
+ }
84
+
85
+ public function testEncodeFolderSync()
86
+ {
87
+ $outputStream = fopen("php://temp", 'r+');
88
+
89
+ $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
90
+
91
+ $xml = <<<EOF
92
+ <?xml version="1.0" encoding="utf-8"?>
93
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
94
+ <FolderSync xmlns="uri:FolderHierarchy" xmlns:Syncroton="uri:Syncroton" xmlns:Internal="uri:Internal">
95
+ <Status>1</Status>
96
+ <SyncKey>1</SyncKey>
97
+ <Changes>
98
+ <Count>18</Count>
99
+ <Add>
100
+ <ServerId>2685b302b79f58d2753199545e3cb8be</ServerId>
101
+ <ParentId>0</ParentId>
102
+ <DisplayName>Test2</DisplayName>
103
+ <Type>13</Type>
104
+ </Add>
105
+ <Add>
106
+ <ServerId>9770b083c68e8584f396d15a116d6608</ServerId>
107
+ <ParentId>0</ParentId>
108
+ <DisplayName>DavidCalendar</DisplayName>
109
+ <Type>13</Type>
110
+ </Add>
111
+ <Add>
112
+ <ServerId>0f66388806743c514b8063bf0dc87486</ServerId>
113
+ <ParentId>0</ParentId>
114
+ <DisplayName>SergeyCalendar</DisplayName>
115
+ <Type>13</Type>
116
+ </Add>
117
+ <Add>
118
+ <ServerId>cca1b81c734abbcd669bea90d23e08ae</ServerId>
119
+ <ParentId>0</ParentId>
120
+ <DisplayName>Calendar</DisplayName>
121
+ <Type>8</Type>
122
+ </Add>
123
+ <Add>
124
+ <ServerId>ab1ddb4ef8e8f8fcc2c9f5a7f9062452</ServerId>
125
+ <ParentId>0</ParentId>
126
+ <DisplayName>PubCal</DisplayName>
127
+ <Type>13</Type>
128
+ </Add>
129
+ <Add>
130
+ <ServerId>d98bd8721371544ed095841ead941893</ServerId>
131
+ <ParentId>0</ParentId>
132
+ <DisplayName>(david) Test2</DisplayName>
133
+ <Type>13</Type>
134
+ </Add>
135
+ <Add>
136
+ <ServerId>9e7b9656ef61d4af2fb2fdcabe600079</ServerId>
137
+ <ParentId>0</ParentId>
138
+ <DisplayName>(david) DavidCalendar</DisplayName>
139
+ <Type>13</Type>
140
+ </Add>
141
+ <Add>
142
+ <ServerId>384cf2d877c39a622fdc2a16898052e2</ServerId>
143
+ <ParentId>0</ParentId>
144
+ <DisplayName>(david) Calendar</DisplayName>
145
+ <Type>13</Type>
146
+ </Add>
147
+ <Add>
148
+ <ServerId>Contacts::Syncroton</ServerId>
149
+ <ParentId>0</ParentId>
150
+ <DisplayName>Contacts</DisplayName>
151
+ <Type>9</Type>
152
+ </Add>
153
+ <Add>
154
+ <ServerId>1bb8c55fe84d52c6968db2571f7dc124</ServerId>
155
+ <ParentId>0</ParentId>
156
+ <DisplayName>Archive</DisplayName>
157
+ <Type>12</Type>
158
+ </Add>
159
+ <Add>
160
+ <ServerId>b51abe73e9e98fe200a4afe409050502</ServerId>
161
+ <ParentId>38b950ebd62cd9a66929c89615d0fc04</ParentId>
162
+ <DisplayName>Spam</DisplayName>
163
+ <Type>12</Type>
164
+ </Add>
165
+ <Add>
166
+ <ServerId>cf529c792fc87d1f207435b3921bb02e</ServerId>
167
+ <ParentId>0</ParentId>
168
+ <DisplayName>Sent</DisplayName>
169
+ <Type>5</Type>
170
+ </Add>
171
+ <Add>
172
+ <ServerId>715ed9ea29b8a5377a69c1f758037c65</ServerId>
173
+ <ParentId>0</ParentId>
174
+ <DisplayName>Spam</DisplayName>
175
+ <Type>12</Type>
176
+ </Add>
177
+ <Add>
178
+ <ServerId>db0d959a3aeb21757f8849a830947a7a</ServerId>
179
+ <ParentId>0</ParentId>
180
+ <DisplayName>Trash</DisplayName>
181
+ <Type>4</Type>
182
+ </Add>
183
+ <Add>
184
+ <ServerId>5ac9ec2e1a9d99e2e10cabe4abf26729</ServerId>
185
+ <ParentId>0</ParentId>
186
+ <DisplayName>Drafts</DisplayName>
187
+ <Type>3</Type>
188
+ </Add>
189
+ <Add>
190
+ <ServerId>38b950ebd62cd9a66929c89615d0fc04</ServerId>
191
+ <ParentId>0</ParentId>
192
+ <DisplayName>INBOX</DisplayName>
193
+ <Type>2</Type>
194
+ </Add>
195
+ <Add>
196
+ <ServerId>fc56f4c7ffe0aefa622db9f8d9186c4a</ServerId>
197
+ <ParentId>0</ParentId>
198
+ <DisplayName>Notes</DisplayName>
199
+ <Type>10</Type>
200
+ </Add>
201
kolab-syncroton.dsc
Changed
17
1
2
Source: kolab-syncroton
3
Binary: kolab-syncroton
4
Architecture: all
5
-Version: 2.3.23-1~kolab1
6
+Version: 2.4.0-1~kolab1
7
Maintainer: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com>
8
Uploaders: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com>
9
Homepage: http://www.kolab.org/
10
11
Package-List:
12
kolab-syncroton deb utils extra
13
Files:
14
- 00000000000000000000000000000000 0 kolab-syncroton-2.3.23.tar.gz
15
+ 00000000000000000000000000000000 0 kolab-syncroton-2.4.0.tar.gz
16
00000000000000000000000000000000 0 debian.tar.gz
17
plesk.kolab_syncroton.inc.php
Changed
10
1
2
$config'activesync_multifolder_blacklist_note' = null;
3
$config'activesync_multifolder_blacklist_task' = null;
4
5
-$config'activesync_protected_folders' = array('windowsoutlook' => array('INBOX', 'Sent', 'Trash'));
6
-
7
// Enables adding sender name in the From: header of send email
8
// when a device uses email address only (e.g. iOS devices)
9
$config'activesync_fix_from' = true;
10