GrommunioDavBackend   F
last analyzed

Complexity

Total Complexity 115

Size/Duplication

Total Lines 877
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
eloc 361
dl 0
loc 877
rs 2
c 5
b 0
f 1
wmc 115

25 Methods

Rating   Name   Duplication   Size   Complexity  
A GetUser() 0 4 1
A __construct() 0 3 1
A CreateFolder() 0 7 1
A Logon() 0 16 3
A DeleteFolder() 0 11 2
B GetObjects() 0 40 7
A CreateObject() 0 9 1
A GetStore() 0 22 5
A GetAddressBook() 0 3 1
B OpenMapiStore() 0 31 11
A GetStoreById() 0 4 1
C getRestrictionForFilters() 0 63 13
C GetFolders() 0 68 15
A GetMapiFolder() 0 6 1
A GetPropsToSet() 0 13 3
F GetMapiMessageForId() 0 90 15
A isGdavEnabled() 0 10 2
A GetCalendarRestriction() 0 71 1
A GetIdOfMapiMessage() 0 21 3
A GetObjectIdFromObjectUri() 0 10 3
A GetCurrentSyncToken() 0 9 5
A checkMapiExtVersion() 0 15 5
A GetSession() 0 2 1
A GetCustomProperties() 0 12 2
C Sync() 0 75 12

How to fix   Complexity   

Complex Class

Complex classes like GrommunioDavBackend often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GrommunioDavBackend, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * SPDX-License-Identifier: AGPL-3.0-only
5
 * SPDX-FileCopyrightText: Copyright 2016 - 2018 Kopano b.v.
6
 * SPDX-FileCopyrightText: Copyright 2020 - 2025 grommunio GmbH
7
 *
8
 * grommunio DAV backend class which handles grommunio related activities.
9
 */
10
11
namespace grommunio\DAV;
12
13
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
14
15
class GrommunioDavBackend {
16
	private $logger;
17
	protected $session;
18
	protected $stores;
19
	protected $user;
20
	protected $customprops;
21
	protected $syncstate;
22
23
	/**
24
	 * Constructor.
25
	 */
26
	public function __construct(GLogger $glogger) {
27
		$this->logger = $glogger;
28
		$this->syncstate = new GrommunioSyncState($glogger, SYNC_DB);
29
	}
30
31
	/**
32
	 * Connect to grommunio and create session.
33
	 *
34
	 * @param string $user
35
	 * @param string $pass
36
	 *
37
	 * @return bool
38
	 */
39
	public function Logon($user, $pass) {
40
		$this->logger->trace('%s / password', $user);
41
42
		$gDavVersion = 'grommunio-dav' . @constant('GDAV_VERSION');
43
		$userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'unknown';
44
		$this->session = mapi_logon_zarafa($user, $pass, MAPI_SERVER, null, null, 1, $gDavVersion, $userAgent);
45
		if (!$this->session) {
46
			$this->logger->info("Auth: ERROR - logon failed for user %s from IP %s", $user, $_SERVER['REMOTE_ADDR']);
47
48
			return false;
49
		}
50
51
		$this->user = $user;
52
		$this->logger->debug("Auth: OK - user %s - session %s", $this->user, $this->session);
53
54
		return $this->isGdavEnabled();
55
	}
56
57
	/**
58
	 * Returns the authenticated user.
59
	 *
60
	 * @return string
61
	 */
62
	public function GetUser() {
63
		$this->logger->trace($this->user);
64
65
		return $this->user;
66
	}
67
68
	/**
69
	 * Create a folder with MAPI class.
70
	 *
71
	 * @param mixed  $principalUri
72
	 * @param string $url
73
	 * @param string $class
74
	 * @param string $displayname
75
	 *
76
	 * @return string
77
	 */
78
	public function CreateFolder($principalUri, $url, $class, $displayname) {
79
		$props = mapi_getprops($this->GetStore($principalUri), [PR_IPM_SUBTREE_ENTRYID]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_IPM_SUBTREE_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
80
		$folder = mapi_msgstore_openentry($this->GetStore($principalUri), $props[PR_IPM_SUBTREE_ENTRYID]);
81
		$newfolder = mapi_folder_createfolder($folder, $url, $displayname);
0 ignored issues
show
Bug introduced by
The function mapi_folder_createfolder was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

81
		$newfolder = /** @scrutinizer ignore-call */ mapi_folder_createfolder($folder, $url, $displayname);
Loading history...
82
		mapi_setprops($newfolder, [PR_CONTAINER_CLASS => $class]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_CONTAINER_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
83
84
		return $url;
85
	}
86
87
	/**
88
	 * Delete a folder with MAPI class.
89
	 *
90
	 * @param mixed $id
91
	 *
92
	 * @return bool
93
	 */
94
	public function DeleteFolder($id) {
95
		$folder = $this->GetMapiFolder($id);
96
		if (!$folder) {
97
			return false;
98
		}
99
100
		$props = mapi_getprops($folder, [PR_ENTRYID, PR_PARENT_ENTRYID]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
101
		$parentfolder = mapi_msgstore_openentry($this->GetStoreById($id), $props[PR_PARENT_ENTRYID]);
102
		mapi_folder_deletefolder($parentfolder, $props[PR_ENTRYID]);
0 ignored issues
show
Bug introduced by
The function mapi_folder_deletefolder was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

102
		/** @scrutinizer ignore-call */ 
103
  mapi_folder_deletefolder($parentfolder, $props[PR_ENTRYID]);
Loading history...
103
104
		return true;
105
	}
106
107
	/**
108
	 * Returns a list of folders for a MAPI class.
109
	 *
110
	 * @param string $principalUri
111
	 * @param mixed  $classes
112
	 *
113
	 * @return array
114
	 */
115
	public function GetFolders($principalUri, $classes) {
116
		$this->logger->trace("principal '%s', classes '%s'", $principalUri, $classes);
117
		$folders = [];
118
119
		// TODO limit the output to subfolders of the principalUri?
120
121
		$store = $this->GetStore($principalUri);
122
		$storeprops = mapi_getprops($store, [PR_IPM_WASTEBASKET_ENTRYID]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_IPM_WASTEBASKET_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
123
		$rootfolder = mapi_msgstore_openentry($store);
124
		$hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH | MAPI_DEFERRED_ERRORS);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\MAPI_DEFERRED_ERRORS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\CONVENIENT_DEPTH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
125
		// TODO also filter hidden folders
126
		$restrictions = [];
127
		foreach ($classes as $class) {
128
			$restrictions[] = [RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => PR_CONTAINER_CLASS, VALUE => $class]];
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RELOP_EQ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_CONTAINER_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\VALUE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\ULPROPTAG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\RELOP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\RES_PROPERTY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
129
		}
130
		mapi_table_restrict($hierarchy, [RES_OR, $restrictions]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_OR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
131
132
		// TODO how to handle hierarchies?
133
		$rows = mapi_table_queryallrows($hierarchy, [PR_DISPLAY_NAME, PR_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_FOLDER_TYPE, PR_LOCAL_COMMIT_TIME_MAX, PR_CONTAINER_CLASS, PR_COMMENT, PR_PARENT_ENTRYID]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_PARENT_SOURCE_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_COMMENT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_FOLDER_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_SOURCE_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_LOCAL_COMMIT_TIME_MAX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
134
135
		$rootprops = mapi_getprops($rootfolder, [PR_IPM_CONTACT_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_IPM_CONTACT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_IPM_APPOINTMENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
136
		foreach ($rows as $row) {
137
			if ($row[PR_FOLDER_TYPE] == FOLDER_SEARCH) {
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\FOLDER_SEARCH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
138
				continue;
139
			}
140
			$folderId = $principalUri . ":" . bin2hex($row[PR_SOURCE_KEY]);
141
			$syncToken = $this->GetCurrentSyncToken($folderId);
142
143
			if (isset($row[PR_PARENT_ENTRYID], $storeprops[PR_IPM_WASTEBASKET_ENTRYID]) && $row[PR_PARENT_ENTRYID] == $storeprops[PR_IPM_WASTEBASKET_ENTRYID]) {
144
				continue;
145
			}
146
147
			$folder = [
148
				'id' => $folderId,
149
				'uri' => $row[PR_DISPLAY_NAME],
150
				'principaluri' => $principalUri,
151
				'{http://sabredav.org/ns}sync-token' => $syncToken,
152
				'{DAV:}displayname' => $row[PR_DISPLAY_NAME],
153
				'{urn:ietf:params:xml:ns:caldav}calendar-description' => $row[PR_COMMENT],
154
				'{http://calendarserver.org/ns/}getctag' => isset($row[PR_LOCAL_COMMIT_TIME_MAX]) ? strval($row[PR_LOCAL_COMMIT_TIME_MAX]) : '0000000000',
155
			];
156
157
			// set the supported component (task or calendar)
158
			if ($row[PR_CONTAINER_CLASS] == "IPF.Task") {
159
				$folder['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'] = new SupportedCalendarComponentSet(['VTODO']);
160
			}
161
			if ($row[PR_CONTAINER_CLASS] == "IPF.Appointment") {
162
				$folder['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'] = new SupportedCalendarComponentSet(['VEVENT']);
163
			}
164
165
			// ensure default contacts folder is put first, some clients
166
			// i.e. Apple Addressbook only supports one contact folder,
167
			// therefore it is desired that folder is the default one.
168
			if (in_array("IPF.Contact", $classes) && isset($rootprops[PR_IPM_CONTACT_ENTRYID]) && $row[PR_ENTRYID] == $rootprops[PR_IPM_CONTACT_ENTRYID]) {
169
				array_unshift($folders, $folder);
170
			}
171
			// ensure default calendar folder is put first,
172
			// before the tasks folder.
173
			elseif (in_array('IPF.Appointment', $classes) && isset($rootprops[PR_IPM_APPOINTMENT_ENTRYID]) && $row[PR_ENTRYID] == $rootprops[PR_IPM_APPOINTMENT_ENTRYID]) {
174
				array_unshift($folders, $folder);
175
			}
176
			else {
177
				array_push($folders, $folder);
178
			}
179
		}
180
		$this->logger->trace('found %d folders: %s', count($folders), $folders);
181
182
		return $folders;
183
	}
184
185
	/**
186
	 * Returns a MAPI restriction for a defined set of filters.
187
	 *
188
	 * @param array  $filters
189
	 * @param string $storeId (optional) mapi compatible storeid - required when using start+end filter
190
	 *
191
	 * @return null|array
192
	 */
193
	private function getRestrictionForFilters($filters, $storeId = null) {
194
		$hasTimerange = isset($filters['start'], $filters['end'], $storeId);
195
		$hasTypes = isset($filters['types']) && is_array($filters['types']) && !empty($filters['types']);
196
197
		// Fast path: only message-class filtering, no time-range.
198
		if (!$hasTimerange && $hasTypes) {
199
			$this->logger->trace("getRestrictionForFilters - types only: %s", $filters['types']);
200
			$arr = [];
201
			foreach ($filters['types'] as $filter) {
202
				$arr[] = [RES_PROPERTY,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_PROPERTY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
203
					[RELOP => RELOP_EQ,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RELOP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\RELOP_EQ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
204
						ULPROPTAG => PR_MESSAGE_CLASS,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\ULPROPTAG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
205
						VALUE => $filter,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\VALUE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
206
					],
207
				];
208
			}
209
			$restriction = [RES_OR, $arr];
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_OR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
210
			$this->logger->trace("getRestrictionForFilters - built: %s", simplifyRestriction($restriction));
0 ignored issues
show
Bug introduced by
The function simplifyRestriction was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
			$this->logger->trace("getRestrictionForFilters - built: %s", /** @scrutinizer ignore-call */ simplifyRestriction($restriction));
Loading history...
211
212
			return $restriction;
213
		}
214
215
		// Time-range only (no explicit types) – interpret as events time-range.
216
		if ($hasTimerange && !$hasTypes) {
217
			$this->logger->trace("getRestrictionForFilters - timerange only start:%d end:%d", $filters['start'], $filters['end']);
218
			$restriction = $this->GetCalendarRestriction($storeId, $filters['start'], $filters['end']);
219
			$this->logger->trace("getRestrictionForFilters - built: %s", simplifyRestriction($restriction));
220
221
			return $restriction;
222
		}
223
224
		// Both types and time-range. Apply date restriction to appointments only,
225
		// and include other types (e.g., tasks) without date constraints.
226
		if ($hasTimerange && $hasTypes) {
227
			$this->logger->trace("getRestrictionForFilters - timerange + types start:%d end:%d types:%s", $filters['start'], $filters['end'], $filters['types']);
228
			$orParts = [];
229
230
			$dateRestriction = $this->GetCalendarRestriction($storeId, $filters['start'], $filters['end']);
231
			foreach ($filters['types'] as $type) {
232
				if ($type === 'IPM.Appointment') {
233
					$orParts[] = [RES_AND, [
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_AND was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
234
						$dateRestriction,
235
						[RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => PR_MESSAGE_CLASS, VALUE => 'IPM.Appointment']],
236
					]];
237
				}
238
				else {
239
					// Other types (e.g., tasks) – no date constraint.
240
					$orParts[] = [RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => PR_MESSAGE_CLASS, VALUE => $type]];
241
				}
242
			}
243
244
			// If no parts were added, return null.
245
			if (empty($orParts)) {
246
				return null;
247
			}
248
249
			$restriction = [RES_OR, $orParts];
250
			$this->logger->trace("getRestrictionForFilters - built: %s", simplifyRestriction($restriction));
251
252
			return $restriction;
253
		}
254
255
		return null;
256
	}
257
258
	/**
259
	 * Returns a list of objects for a folder given by the id.
260
	 *
261
	 * @param string $id
262
	 * @param string $fileExtension
263
	 * @param array  $filters
264
	 *
265
	 * @return array
266
	 */
267
	public function GetObjects($id, $fileExtension, $filters = []) {
268
		$folder = $this->GetMapiFolder($id);
269
		$properties = $this->GetCustomProperties($id);
270
		$table = mapi_folder_getcontentstable($folder, MAPI_DEFERRED_ERRORS);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\MAPI_DEFERRED_ERRORS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
271
		$restriction = $this->getRestrictionForFilters($filters, $this->GetStoreById($id));
0 ignored issues
show
Bug introduced by
It seems like $this->GetStoreById($id) can also be of type false; however, parameter $storeId of grommunio\DAV\GrommunioD...RestrictionForFilters() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

271
		$restriction = $this->getRestrictionForFilters($filters, /** @scrutinizer ignore-type */ $this->GetStoreById($id));
Loading history...
272
		if ($restriction) {
273
			mapi_table_restrict($table, $restriction);
274
		}
275
276
		$rows = mapi_table_queryallrows($table, [PR_SOURCE_KEY, PR_LAST_MODIFICATION_TIME, PR_MESSAGE_SIZE, $properties['goid']]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_LAST_MODIFICATION_TIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_SOURCE_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_MESSAGE_SIZE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
277
278
		$results = [];
279
		foreach ($rows as $row) {
280
			$realId = "";
281
			if (isset($row[$properties['goid']])) {
282
				$realId = getUidFromGoid($row[$properties['goid']]);
0 ignored issues
show
Bug introduced by
The function getUidFromGoid was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

282
				$realId = /** @scrutinizer ignore-call */ getUidFromGoid($row[$properties['goid']]);
Loading history...
283
			}
284
			if (!$realId) {
285
				$realId = bin2hex($row[PR_SOURCE_KEY]);
286
			}
287
			$realId = rawurlencode($realId);
288
289
			$result = [
290
				'id' => $realId,
291
				'uri' => $realId . $fileExtension,
292
				'etag' => '"' . $row[PR_LAST_MODIFICATION_TIME] . '"',
293
				'lastmodified' => $row[PR_LAST_MODIFICATION_TIME],
294
				'size' => $row[PR_MESSAGE_SIZE], // only approximation
295
			];
296
297
			if ($fileExtension == GrommunioCalDavBackend::FILE_EXTENSION) {
298
				$result['calendarid'] = $id;
299
			}
300
			elseif ($fileExtension == GrommunioCardDavBackend::FILE_EXTENSION) {
301
				$result['addressbookid'] = $id;
302
			}
303
			$results[] = $result;
304
		}
305
306
		return $results;
307
	}
308
309
	/**
310
	 * Create the object and set appttsref.
311
	 *
312
	 * @param mixed  $folderId
313
	 * @param mixed  $folder
314
	 * @param string $objectId
315
	 *
316
	 * @return mixed
317
	 */
318
	public function CreateObject($folderId, $folder, $objectId) {
319
		$mapimessage = mapi_folder_createmessage($folder);
320
		// we save the objectId in PROP_APPTTSREF so we find it by this id
321
		$properties = $this->GetCustomProperties($folderId);
322
		// FIXME: uid for contacts
323
		$goid = getGoidFromUid($objectId);
0 ignored issues
show
Bug introduced by
The function getGoidFromUid was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

323
		$goid = /** @scrutinizer ignore-call */ getGoidFromUid($objectId);
Loading history...
324
		mapi_setprops($mapimessage, [$properties['goid'] => $goid]);
325
326
		return $mapimessage;
327
	}
328
329
	/**
330
	 * Returns a mapi folder resource for a folderid (PR_SOURCE_KEY).
331
	 *
332
	 * @param string $folderid
333
	 *
334
	 * @return mixed
335
	 */
336
	public function GetMapiFolder($folderid) {
337
		$this->logger->trace('Id: %s', $folderid);
338
		$arr = explode(':', $folderid);
339
		$entryid = mapi_msgstore_entryidfromsourcekey($this->GetStore($arr[0]), hex2bin($arr[1]));
340
341
		return mapi_msgstore_openentry($this->GetStore($arr[0]), $entryid);
342
	}
343
344
	/**
345
	 * Returns MAPI addressbook.
346
	 *
347
	 * @return mixed
348
	 */
349
	public function GetAddressBook() {
350
		// TODO should be a singleton
351
		return mapi_openaddressbook($this->session);
352
	}
353
354
	/**
355
	 * Opens MAPI store for the user.
356
	 *
357
	 * @param string $username
358
	 *
359
	 * @return mixed
360
	 */
361
	public function OpenMapiStore($username = null) {
362
		$msgstorestable = mapi_getmsgstorestable($this->session);
363
		$msgstores = mapi_table_queryallrows($msgstorestable, [PR_DEFAULT_STORE, PR_ENTRYID, PR_MDB_PROVIDER]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_DEFAULT_STORE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\PR_MDB_PROVIDER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
364
365
		$defaultstore = null;
366
		$publicstore = null;
367
		foreach ($msgstores as $row) {
368
			if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE]) {
369
				$defaultstore = $row[PR_ENTRYID];
370
			}
371
			if (isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\ZARAFA_STORE_PUBLIC_GUID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
372
				$publicstore = $row[PR_ENTRYID];
373
			}
374
		}
375
376
		/* user's own store or public store */
377
		if ($username == $this->GetUser() && $defaultstore != null) {
378
			return mapi_openmsgstore($this->session, $defaultstore);
379
		}
380
		if ($username == 'public' && $publicstore != null) {
381
			return mapi_openmsgstore($this->session, $publicstore);
382
		}
383
384
		/* otherwise other user's store */
385
		$store = mapi_openmsgstore($this->session, $defaultstore);
386
		if (!$store) {
387
			return false;
388
		}
389
		$otherstore = mapi_msgstore_createentryid($store, $username);
390
391
		return mapi_openmsgstore($this->session, $otherstore);
392
	}
393
394
	/**
395
	 * Returns store for the user.
396
	 *
397
	 * @param string $storename
398
	 *
399
	 * @return mixed
400
	 */
401
	public function GetStore($storename) {
402
		if ($storename == null) {
403
			$storename = $this->GetUser();
404
		}
405
		else {
406
			$storename = str_replace('principals/', '', $storename);
407
		}
408
		$this->logger->trace("storename %s", $storename);
409
410
		/* We already got the store */
411
		if (isset($this->stores[$storename]) && $this->stores[$storename] != null) {
412
			return $this->stores[$storename];
413
		}
414
415
		$this->stores[$storename] = $this->OpenMapiStore($storename);
416
		if (!$this->stores[$storename]) {
417
			$this->logger->info("Auth: ERROR - unable to open store for %s (0x%08X)", $storename, mapi_last_hresult());
418
419
			return false;
420
		}
421
422
		return $this->stores[$storename];
423
	}
424
425
	/**
426
	 * Returns store from the id.
427
	 *
428
	 * @param mixed $id
429
	 *
430
	 * @return mixed
431
	 */
432
	public function GetStoreById($id) {
433
		$arr = explode(':', $id);
434
435
		return $this->GetStore($arr[0]);
436
	}
437
438
	/**
439
	 * Returns logon session.
440
	 *
441
	 * @return mixed
442
	 */
443
	public function GetSession() {
444
		return $this->session;
445
	}
446
447
	/**
448
	 * Returns an object ID of a mapi object.
449
	 * If set, goid will be preferred. If not the PR_SOURCE_KEY of the message (as hex) will be returned.
450
	 *
451
	 * This order is reflected as well when searching for a message with these ids in GrommunioDavBackend->GetMapiMessageForId().
452
	 *
453
	 * @param string $folderId
454
	 * @param mixed  $mapimessage
455
	 *
456
	 * @return string
457
	 */
458
	public function GetIdOfMapiMessage($folderId, $mapimessage) {
459
		$this->logger->trace("Finding ID of %s", $mapimessage);
460
		$properties = $this->GetCustomProperties($folderId);
461
462
		// It's one of these, order:
463
		// - GOID (if set)
464
		// - PROP_VCARDUID (if set)
465
		// - PR_SOURCE_KEY
466
		$props = mapi_getprops($mapimessage, [$properties['goid'], PR_SOURCE_KEY]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_SOURCE_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
467
		if (isset($props[$properties['goid']])) {
468
			$id = getUidFromGoid($props[$properties['goid']]);
0 ignored issues
show
Bug introduced by
The function getUidFromGoid was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

468
			$id = /** @scrutinizer ignore-call */ getUidFromGoid($props[$properties['goid']]);
Loading history...
469
			$this->logger->debug("Found uid %s from goid: %s", $id, bin2hex($props[$properties['goid']]));
470
			if ($id != null) {
471
				return rawurlencode($id);
472
			}
473
		}
474
		// PR_SOURCE_KEY is always available
475
		$id = bin2hex($props[PR_SOURCE_KEY]);
476
		$this->logger->debug("Found PR_SOURCE_KEY: %s", $id);
477
478
		return $id;
479
	}
480
481
	/**
482
	 * Finds and opens a MapiMessage from an objectId.
483
	 * The id can be a PROP_APPTTSREF or a PR_SOURCE_KEY (as hex).
484
	 *
485
	 * @param string $folderId
486
	 * @param string $objectUri
487
	 * @param mixed  $mapifolder optional
488
	 * @param string $extension  optional
489
	 *
490
	 * @return mixed
491
	 */
492
	public function GetMapiMessageForId($folderId, $objectUri, $mapifolder = null, $extension = null) {
493
		$this->logger->trace("Searching for '%s' in '%s' (%s) (%s)", $objectUri, $folderId, $mapifolder, $extension);
494
495
		if (!$mapifolder) {
496
			$mapifolder = $this->GetMapiFolder($folderId);
497
		}
498
499
		$id = rawurldecode($this->GetObjectIdFromObjectUri($objectUri, $extension));
500
501
		/* The ID can be several different things:
502
		 * - a UID that is saved in goid
503
		 * - a PROP_VCARDUID
504
		 * - a PR_SOURCE_KEY
505
		 *
506
		 * If it's a sourcekey, we can open the message directly.
507
		 * If the $extension is set:
508
		 *      if it's ics:
509
		 *          - search GOID with this value
510
		 *      if it's vcf:
511
		 *          - search PROP_VCARDUID value
512
		 */
513
		$entryid = false;
514
		$restriction = false;
515
516
		if (ctype_xdigit($id) && strlen($id) % 2 == 0) {
517
			$this->logger->trace("Try PR_SOURCE_KEY %s", $id);
518
			$arr = explode(':', $folderId);
519
			$entryid = mapi_msgstore_entryidfromsourcekey($this->GetStoreById($arr[0]), hex2bin($arr[1]), hex2bin($id));
520
		}
521
522
		if (!$entryid) {
523
			$this->logger->trace("Entryid not found. Try goid/vcarduid %s", $id);
524
525
			$properties = $this->GetCustomProperties($folderId);
526
			$restriction = [];
527
528
			if ($extension) {
529
				if ($extension == GrommunioCalDavBackend::FILE_EXTENSION) {
530
					$this->logger->trace("Try goid %s", $id);
531
					$goid = getGoidFromUid($id);
0 ignored issues
show
Bug introduced by
The function getGoidFromUid was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

531
					$goid = /** @scrutinizer ignore-call */ getGoidFromUid($id);
Loading history...
532
					$this->logger->trace("Try goid 0x%08X => %s", $properties["goid"], bin2hex($goid));
533
					$goid0 = getGoidFromUidZero($id);
0 ignored issues
show
Bug introduced by
The function getGoidFromUidZero was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

533
					$goid0 = /** @scrutinizer ignore-call */ getGoidFromUidZero($id);
Loading history...
534
					$restriction[] = [RES_OR, [
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_OR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
535
						[RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => $properties["goid"], VALUE => $goid]],
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\VALUE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\RELOP_EQ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\ULPROPTAG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\RES_PROPERTY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\RELOP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
536
						[RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => $properties["goid"], VALUE => $goid0]],
537
					]];
538
				}
539
				elseif ($extension == GrommunioCardDavBackend::FILE_EXTENSION) {
540
					$this->logger->trace("Try vcarduid %s", $id);
541
					$restriction[] = [RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => $properties["vcarduid"], VALUE => $id]];
542
				}
543
			}
544
		}
545
546
		// find the message if we have a restriction
547
		if ($restriction) {
548
			$table = mapi_folder_getcontentstable($mapifolder, MAPI_DEFERRED_ERRORS);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\MAPI_DEFERRED_ERRORS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
549
			mapi_table_restrict($table, [RES_OR, $restriction]);
550
			// Get requested properties, plus whatever we need
551
			$proplist = [PR_ENTRYID];
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
552
			$rows = mapi_table_queryallrows($table, $proplist);
553
			if (count($rows) > 1) {
554
				$this->logger->warn("Found %d entries for id '%s' searching for message, returnin first in the list", count($rows), $id);
555
			}
556
			if (isset($rows[0], $rows[0][PR_ENTRYID])) {
557
				$entryid = $rows[0][PR_ENTRYID];
558
			}
559
		}
560
		if (!$entryid) {
561
			$this->logger->debug("Try to get entryid from appttsref");
562
			$arr = explode(':', $folderId);
563
			$sk = $this->syncstate->getSourcekey($arr[1], $id);
564
			if ($sk !== null) {
565
				$this->logger->debug("Found sourcekey from appttsref %s", $sk);
566
				$entryid = mapi_msgstore_entryidfromsourcekey($this->GetStoreById($arr[0]), hex2bin($arr[1]), hex2bin($sk));
567
			}
568
		}
569
		if ($entryid) {
570
			$mapimessage = mapi_msgstore_openentry($this->GetStoreById($folderId), $entryid);
571
			if (!$mapimessage) {
572
				$this->logger->warn("Error, unable to open entry id: %s 0x%X", bin2hex($entryid), mapi_last_hresult());
573
574
				return null;
575
			}
576
577
			return $mapimessage;
578
		}
579
		$this->logger->debug("Nothing found for %s", $id);
580
581
		return null;
582
	}
583
584
	/**
585
	 * Returns the objectId from an objectUri. It strips the file extension
586
	 * if it matches the passed one.
587
	 *
588
	 * @param string $objectUri
589
	 * @param string $extension
590
	 *
591
	 * @return string
592
	 */
593
	public function GetObjectIdFromObjectUri($objectUri, $extension) {
594
		if (!$extension) {
595
			return $objectUri;
596
		}
597
		$extLength = strlen($extension);
598
		if (substr($objectUri, -$extLength) === $extension) {
599
			return substr($objectUri, 0, -$extLength);
600
		}
601
602
		return $objectUri;
603
	}
604
605
	/**
606
	 * Checks if the PHP-MAPI extension is available and in a requested version.
607
	 *
608
	 * @param string $version the version to be checked ("6.30.10-18495", parts or build number)
609
	 *
610
	 * @return bool installed version is superior to the checked string
611
	 */
612
	protected function checkMapiExtVersion($version = "") {
613
		if (!extension_loaded("mapi")) {
614
			return false;
615
		}
616
		// compare build number if requested
617
		if (preg_match('/^\d+$/', $version) && strlen($version) > 3) {
618
			$vs = preg_split('/-/', phpversion("mapi"));
619
620
			return $version <= $vs[1];
621
		}
622
		if (version_compare(phpversion("mapi"), $version) == -1) {
623
			return false;
624
		}
625
626
		return true;
627
	}
628
629
	/**
630
	 * Get named (custom) properties. Currently only PROP_APPTTSREF.
631
	 *
632
	 * @param string $id the folder id
633
	 *
634
	 * @return mixed
635
	 */
636
	protected function GetCustomProperties($id) {
637
		if (!isset($this->customprops[$id])) {
638
			$this->logger->trace("Fetching properties id:%s", $id);
639
			$store = $this->GetStoreById($id);
640
			$properties = getPropIdsFromStrings($store, [
0 ignored issues
show
Bug introduced by
The function getPropIdsFromStrings was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

640
			$properties = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, [
Loading history...
641
				"goid" => "PT_BINARY:PSETID_Meeting:" . PidLidGlobalObjectId,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PidLidGlobalObjectId was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
642
				"vcarduid" => MapiProps::PROP_VCARDUID,
643
			]);
644
			$this->customprops[$id] = $properties;
645
		}
646
647
		return $this->customprops[$id];
648
	}
649
650
	/**
651
	 * Create a MAPI restriction to use in the calendar which will
652
	 * return future calendar items (until $end), plus those since $start.
653
	 * Origins: Z-Push.
654
	 *
655
	 * @param mixed $store the MAPI store
656
	 * @param int   $start Timestamp since when to include messages
657
	 * @param int   $end   Ending timestamp
658
	 *
659
	 * @return array
660
	 */
661
	// TODO getting named properties
662
	public function GetCalendarRestriction($store, $start, $end) {
663
		$props = MapiProps::GetAppointmentProperties();
664
		$props = getPropIdsFromStrings($store, $props);
0 ignored issues
show
Bug introduced by
The function getPropIdsFromStrings was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

664
		$props = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, $props);
Loading history...
665
666
		return [RES_OR,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_OR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
667
			[
668
				// OR
669
				// item.end > window.start && item.start < window.end
670
				[RES_AND,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_AND was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
671
					[
672
						[RES_PROPERTY,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_PROPERTY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
673
							[RELOP => RELOP_LE,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RELOP_LE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\RELOP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
674
								ULPROPTAG => $props["starttime"],
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\ULPROPTAG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
675
								VALUE => $end,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\VALUE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
676
							],
677
						],
678
						[RES_PROPERTY,
679
							[RELOP => RELOP_GE,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RELOP_GE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
680
								ULPROPTAG => $props["endtime"],
681
								VALUE => $start,
682
							],
683
						],
684
					],
685
				],
686
				// OR
687
				[RES_OR,
688
					[
689
						// OR
690
						// (EXIST(recurrence_enddate_property) && item[isRecurring] == true && recurrence_enddate_property >= start)
691
						[RES_AND,
692
							[
693
								[RES_EXIST,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_EXIST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
694
									[ULPROPTAG => $props["recurrenceend"],
695
									],
696
								],
697
								[RES_PROPERTY,
698
									[RELOP => RELOP_EQ,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RELOP_EQ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
699
										ULPROPTAG => $props["isrecurring"],
700
										VALUE => true,
701
									],
702
								],
703
								[RES_PROPERTY,
704
									[RELOP => RELOP_GE,
705
										ULPROPTAG => $props["recurrenceend"],
706
										VALUE => $start,
707
									],
708
								],
709
							],
710
						],
711
						// OR
712
						// (!EXIST(recurrence_enddate_property) && item[isRecurring] == true && item[start] <= end)
713
						[RES_AND,
714
							[
715
								[RES_NOT,
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\RES_NOT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
716
									[
717
										[RES_EXIST,
718
											[ULPROPTAG => $props["recurrenceend"],
719
											],
720
										],
721
									],
722
								],
723
								[RES_PROPERTY,
724
									[RELOP => RELOP_LE,
725
										ULPROPTAG => $props["starttime"],
726
										VALUE => $end,
727
									],
728
								],
729
								[RES_PROPERTY,
730
									[RELOP => RELOP_EQ,
731
										ULPROPTAG => $props["isrecurring"],
732
										VALUE => true,
733
									],
734
								],
735
							],
736
						],
737
					],
738
				], // EXISTS OR
739
			],
740
		];        // global OR
741
	}
742
743
	/**
744
	 * Performs ICS based sync used from getChangesForAddressBook
745
	 * / getChangesForCalendar.
746
	 *
747
	 * @param string $folderId
748
	 * @param string $syncToken
749
	 * @param string $fileExtension
750
	 * @param int    $limit
751
	 * @param array  $filters
752
	 *
753
	 * @return null|array
754
	 */
755
	public function Sync($folderId, $syncToken, $fileExtension, $limit = null, $filters = []) {
756
		$arr = explode(':', $folderId);
757
		$phpwrapper = new PHPWrapper($this->GetStoreById($folderId), $this->logger, $this->GetCustomProperties($folderId), $fileExtension, $this->syncstate, $arr[1]);
758
		$mapiimporter = mapi_wrap_importcontentschanges($phpwrapper);
0 ignored issues
show
Bug introduced by
The function mapi_wrap_importcontentschanges was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

758
		$mapiimporter = /** @scrutinizer ignore-call */ mapi_wrap_importcontentschanges($phpwrapper);
Loading history...
759
760
		$mapifolder = $this->GetMapiFolder($folderId);
761
		$exporter = mapi_openproperty($mapifolder, PR_CONTENTS_SYNCHRONIZER, IID_IExchangeExportChanges, 0, 0);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_CONTENTS_SYNCHRONIZER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\IID_IExchangeExportChanges was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
762
		if (!$exporter) {
763
			$this->logger->error("Unable to get exporter");
764
765
			return null;
766
		}
767
768
		$stream = mapi_stream_create();
0 ignored issues
show
Bug introduced by
The function mapi_stream_create was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

768
		$stream = /** @scrutinizer ignore-call */ mapi_stream_create();
Loading history...
769
		if ($syncToken == null || $syncToken == '0000000000') {
770
			mapi_stream_write($stream, hex2bin("0000000000000000"));
771
		}
772
		else {
773
			$value = $this->syncstate->getState($arr[1], $syncToken);
774
			if ($value === null) {
775
				$this->logger->error("Unable to get value from token: %s - folderId: %s", $syncToken, $folderId);
776
777
				return null;
778
			}
779
			mapi_stream_write($stream, hex2bin($value));
780
		}
781
782
		// force restriction of "types" to export only appointments or contacts
783
		$restriction = $this->getRestrictionForFilters($filters);
784
785
		// The last parameter in mapi_exportchanges_config is buffer size for mapi_exportchanges_synchronize - how many
786
		// changes will be processed in its call. Setting it to MAX_SYNC_ITEMS won't export more items than is set in
787
		// the config. If there are more changes than MAX_SYNC_ITEMS the client will eventually catch up and sync
788
		// the rest on the subsequent sync request(s).
789
		$bufferSize = ($limit !== null && $limit > 0) ? $limit : MAX_SYNC_ITEMS;
790
		mapi_exportchanges_config($exporter, $stream, SYNC_NORMAL | SYNC_UNICODE, $mapiimporter, $restriction, false, false, $bufferSize);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\SYNC_UNICODE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The constant grommunio\DAV\SYNC_NORMAL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug introduced by
The function mapi_exportchanges_config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

790
		/** @scrutinizer ignore-call */ 
791
  mapi_exportchanges_config($exporter, $stream, SYNC_NORMAL | SYNC_UNICODE, $mapiimporter, $restriction, false, false, $bufferSize);
Loading history...
791
		$changesCount = mapi_exportchanges_getchangecount($exporter);
0 ignored issues
show
Bug introduced by
The function mapi_exportchanges_getchangecount was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

791
		$changesCount = /** @scrutinizer ignore-call */ mapi_exportchanges_getchangecount($exporter);
Loading history...
792
		$this->logger->debug("Exporter found %d changes, buffer size for mapi_exportchanges_synchronize %d", $changesCount, $bufferSize);
793
		while (is_array(mapi_exportchanges_synchronize($exporter))) {
0 ignored issues
show
Bug introduced by
The function mapi_exportchanges_synchronize was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

793
		while (is_array(/** @scrutinizer ignore-call */ mapi_exportchanges_synchronize($exporter))) {
Loading history...
794
			if ($changesCount > $bufferSize) {
795
				$this->logger->info("There were too many changes to be exported in this request. Total changes %d, exported %d.", $changesCount, $phpwrapper->Total());
796
797
				break;
798
			}
799
		}
800
		$exportedChanges = $phpwrapper->Total();
801
		$this->logger->debug("Exported %d changes, pending %d", $exportedChanges, $changesCount - $exportedChanges);
802
803
		mapi_exportchanges_updatestate($exporter, $stream);
0 ignored issues
show
Bug introduced by
The function mapi_exportchanges_updatestate was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

803
		/** @scrutinizer ignore-call */ 
804
  mapi_exportchanges_updatestate($exporter, $stream);
Loading history...
804
		mapi_stream_seek($stream, 0, STREAM_SEEK_SET);
0 ignored issues
show
Bug introduced by
The function mapi_stream_seek was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

804
		/** @scrutinizer ignore-call */ 
805
  mapi_stream_seek($stream, 0, STREAM_SEEK_SET);
Loading history...
Bug introduced by
The constant grommunio\DAV\STREAM_SEEK_SET was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
805
		$state = "";
806
		while (true) {
807
			$data = mapi_stream_read($stream, 4096);
0 ignored issues
show
Bug introduced by
The function mapi_stream_read was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

807
			$data = /** @scrutinizer ignore-call */ mapi_stream_read($stream, 4096);
Loading history...
808
			if (strlen($data) > 0) {
809
				$state .= $data;
810
			}
811
			else {
812
				break;
813
			}
814
		}
815
816
		$newtoken = ($phpwrapper->Total() > 0) ? uniqid() : $syncToken;
817
818
		$this->syncstate->setState($arr[1], $newtoken, bin2hex($state));
819
820
		$result = [
821
			"syncToken" => $newtoken,
822
			"added" => $phpwrapper->GetAdded(),
823
			"modified" => $phpwrapper->GetModified(),
824
			"deleted" => $phpwrapper->GetDeleted(),
825
		];
826
827
		$this->logger->trace("Returning %s", $result);
828
829
		return $result;
830
	}
831
832
	/**
833
	 * Returns an array of necessary properties to set with default values.
834
	 *
835
	 * @see MapiProps::GetDefault...Properties()
836
	 *
837
	 * @param mixed $id           storeid
838
	 * @param mixed $mapimessage  mapi message to check
839
	 * @param array $propList     array of mapped properties
840
	 * @param array $defaultProps array of necessary properties with default values
841
	 *
842
	 * @return array
843
	 */
844
	public function GetPropsToSet($id, $mapimessage, $propList, $defaultProps) {
845
		$propsToSet = [];
846
		$store = $this->GetStoreById($id);
847
		$propList = getPropIdsFromStrings($store, $propList);
0 ignored issues
show
Bug introduced by
The function getPropIdsFromStrings was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

847
		$propList = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, $propList);
Loading history...
848
		$props = mapi_getprops($mapimessage);
849
850
		foreach ($defaultProps as $prop => $value) {
851
			if (!isset($props[$propList[$prop]])) {
852
				$propsToSet[$propList[$prop]] = $value;
853
			}
854
		}
855
856
		return $propsToSet;
857
	}
858
859
	/**
860
	 * Returns the current sync-token for the folder if one was issued.
861
	 *
862
	 * @param string $folderId composite id in form principal:sourcekey
863
	 *
864
	 * @return string
865
	*/
866
	public function GetCurrentSyncToken($folderId) {
867
		$arr = explode(':', $folderId, 2);
868
		if (count($arr) < 2 || $arr[1] === '') {
869
			return '0000000000';
870
		}
871
872
		$token = $this->syncstate->getCurrentToken($arr[1]);
873
874
		return (!is_string($token) || $token === '') ? '0000000000' : $token;
875
	}
876
877
	/**
878
	 * Checks whether the user is enabled for grommunio-dav.
879
	 *
880
	 * @return bool
881
	 */
882
	private function isGdavEnabled() {
883
		$storeProps = mapi_getprops($this->GetStore($this->GetUser()), [PR_EC_ENABLED_FEATURES_L]);
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\PR_EC_ENABLED_FEATURES_L was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
884
		if ($storeProps[PR_EC_ENABLED_FEATURES_L] & UP_DAV) {
0 ignored issues
show
Bug introduced by
The constant grommunio\DAV\UP_DAV was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
885
			$this->logger->debug("user %s is enabled for grommunio-dav", $this->user);
886
887
			return true;
888
		}
889
		$this->logger->debug("user %s is disabled for grommunio-dav", $this->user);
890
891
		return false;
892
	}
893
}
894