Passed
Push — master ( fa02f2...bc9f14 )
by
unknown
02:45
created

GrommunioDavBackend   F

Complexity

Total Complexity 114

Size/Duplication

Total Lines 880
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
eloc 363
c 5
b 0
f 1
dl 0
loc 880
rs 2
wmc 114

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
A GetPropsToSet() 0 13 3
B GetObjects() 0 40 7
F GetMapiMessageForId() 0 90 15
A CreateObject() 0 9 1
A isGdavEnabled() 0 10 2
A GetStore() 0 22 5
A GetCalendarRestriction() 0 71 1
A GetAddressBook() 0 3 1
B OpenMapiStore() 0 31 11
A GetIdOfMapiMessage() 0 21 3
A GetObjectIdFromObjectUri() 0 10 3
A GetCurrentSyncToken() 0 12 5
A GetStoreById() 0 4 1
A checkMapiExtVersion() 0 15 5
C getRestrictionForFilters() 0 63 13
C GetFolders() 0 68 15
A GetSession() 0 2 1
A GetMapiFolder() 0 6 1
A GetCustomProperties() 0 12 2
C Sync() 0 75 11

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
			$folderId = $principalUri . ":" . bin2hex($row[PR_SOURCE_KEY]);
138
			$syncToken = $this->GetCurrentSyncToken($folderId);
139
			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...
140
				continue;
141
			}
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 the current sync-token for the folder if one was issued.
440
	 *
441
	 * @param string $folderId composite id in form principal:sourcekey
442
	 *
443
	 * @return string
444
	 */
445
	public function GetCurrentSyncToken($folderId) {
446
		$arr = explode(':', $folderId, 2);
447
		if (count($arr) < 2 || $arr[1] === '') {
448
			return '0000000000';
449
		}
450
451
		$token = $this->syncstate->getCurrentToken($arr[1]);
452
		if (!is_string($token) || $token === '') {
453
			return '0000000000';
454
		}
455
456
		return $token;
457
	}
458
459
	/**
460
	 * Returns logon session.
461
	 *
462
	 * @return mixed
463
	 */
464
	public function GetSession() {
465
		return $this->session;
466
	}
467
468
	/**
469
	 * Returns an object ID of a mapi object.
470
	 * If set, goid will be preferred. If not the PR_SOURCE_KEY of the message (as hex) will be returned.
471
	 *
472
	 * This order is reflected as well when searching for a message with these ids in GrommunioDavBackend->GetMapiMessageForId().
473
	 *
474
	 * @param string $folderId
475
	 * @param mixed  $mapimessage
476
	 *
477
	 * @return string
478
	 */
479
	public function GetIdOfMapiMessage($folderId, $mapimessage) {
480
		$this->logger->trace("Finding ID of %s", $mapimessage);
481
		$properties = $this->GetCustomProperties($folderId);
482
483
		// It's one of these, order:
484
		// - GOID (if set)
485
		// - PROP_VCARDUID (if set)
486
		// - PR_SOURCE_KEY
487
		$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...
488
		if (isset($props[$properties['goid']])) {
489
			$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

489
			$id = /** @scrutinizer ignore-call */ getUidFromGoid($props[$properties['goid']]);
Loading history...
490
			$this->logger->debug("Found uid %s from goid: %s", $id, bin2hex($props[$properties['goid']]));
491
			if ($id != null) {
492
				return rawurlencode($id);
493
			}
494
		}
495
		// PR_SOURCE_KEY is always available
496
		$id = bin2hex($props[PR_SOURCE_KEY]);
497
		$this->logger->debug("Found PR_SOURCE_KEY: %s", $id);
498
499
		return $id;
500
	}
501
502
	/**
503
	 * Finds and opens a MapiMessage from an objectId.
504
	 * The id can be a PROP_APPTTSREF or a PR_SOURCE_KEY (as hex).
505
	 *
506
	 * @param string $folderId
507
	 * @param string $objectUri
508
	 * @param mixed  $mapifolder optional
509
	 * @param string $extension  optional
510
	 *
511
	 * @return mixed
512
	 */
513
	public function GetMapiMessageForId($folderId, $objectUri, $mapifolder = null, $extension = null) {
514
		$this->logger->trace("Searching for '%s' in '%s' (%s) (%s)", $objectUri, $folderId, $mapifolder, $extension);
515
516
		if (!$mapifolder) {
517
			$mapifolder = $this->GetMapiFolder($folderId);
518
		}
519
520
		$id = rawurldecode($this->GetObjectIdFromObjectUri($objectUri, $extension));
521
522
		/* The ID can be several different things:
523
		 * - a UID that is saved in goid
524
		 * - a PROP_VCARDUID
525
		 * - a PR_SOURCE_KEY
526
		 *
527
		 * If it's a sourcekey, we can open the message directly.
528
		 * If the $extension is set:
529
		 *      if it's ics:
530
		 *          - search GOID with this value
531
		 *      if it's vcf:
532
		 *          - search PROP_VCARDUID value
533
		 */
534
		$entryid = false;
535
		$restriction = false;
536
537
		if (ctype_xdigit($id) && strlen($id) % 2 == 0) {
538
			$this->logger->trace("Try PR_SOURCE_KEY %s", $id);
539
			$arr = explode(':', $folderId);
540
			$entryid = mapi_msgstore_entryidfromsourcekey($this->GetStoreById($arr[0]), hex2bin($arr[1]), hex2bin($id));
541
		}
542
543
		if (!$entryid) {
544
			$this->logger->trace("Entryid not found. Try goid/vcarduid %s", $id);
545
546
			$properties = $this->GetCustomProperties($folderId);
547
			$restriction = [];
548
549
			if ($extension) {
550
				if ($extension == GrommunioCalDavBackend::FILE_EXTENSION) {
551
					$this->logger->trace("Try goid %s", $id);
552
					$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

552
					$goid = /** @scrutinizer ignore-call */ getGoidFromUid($id);
Loading history...
553
					$this->logger->trace("Try goid 0x%08X => %s", $properties["goid"], bin2hex($goid));
554
					$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

554
					$goid0 = /** @scrutinizer ignore-call */ getGoidFromUidZero($id);
Loading history...
555
					$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...
556
						[RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => $properties["goid"], VALUE => $goid]],
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\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\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_EQ 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...
557
						[RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => $properties["goid"], VALUE => $goid0]],
558
					]];
559
				}
560
				elseif ($extension == GrommunioCardDavBackend::FILE_EXTENSION) {
561
					$this->logger->trace("Try vcarduid %s", $id);
562
					$restriction[] = [RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => $properties["vcarduid"], VALUE => $id]];
563
				}
564
			}
565
		}
566
567
		// find the message if we have a restriction
568
		if ($restriction) {
569
			$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...
570
			mapi_table_restrict($table, [RES_OR, $restriction]);
571
			// Get requested properties, plus whatever we need
572
			$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...
573
			$rows = mapi_table_queryallrows($table, $proplist);
574
			if (count($rows) > 1) {
575
				$this->logger->warn("Found %d entries for id '%s' searching for message, returnin first in the list", count($rows), $id);
576
			}
577
			if (isset($rows[0], $rows[0][PR_ENTRYID])) {
578
				$entryid = $rows[0][PR_ENTRYID];
579
			}
580
		}
581
		if (!$entryid) {
582
			$this->logger->debug("Try to get entryid from appttsref");
583
			$arr = explode(':', $folderId);
584
			$sk = $this->syncstate->getSourcekey($arr[1], $id);
585
			if ($sk !== null) {
586
				$this->logger->debug("Found sourcekey from appttsref %s", $sk);
587
				$entryid = mapi_msgstore_entryidfromsourcekey($this->GetStoreById($arr[0]), hex2bin($arr[1]), hex2bin($sk));
588
			}
589
		}
590
		if ($entryid) {
591
			$mapimessage = mapi_msgstore_openentry($this->GetStoreById($folderId), $entryid);
592
			if (!$mapimessage) {
593
				$this->logger->warn("Error, unable to open entry id: %s 0x%X", bin2hex($entryid), mapi_last_hresult());
594
595
				return null;
596
			}
597
598
			return $mapimessage;
599
		}
600
		$this->logger->debug("Nothing found for %s", $id);
601
602
		return null;
603
	}
604
605
	/**
606
	 * Returns the objectId from an objectUri. It strips the file extension
607
	 * if it matches the passed one.
608
	 *
609
	 * @param string $objectUri
610
	 * @param string $extension
611
	 *
612
	 * @return string
613
	 */
614
	public function GetObjectIdFromObjectUri($objectUri, $extension) {
615
		if (!$extension) {
616
			return $objectUri;
617
		}
618
		$extLength = strlen($extension);
619
		if (substr($objectUri, -$extLength) === $extension) {
620
			return substr($objectUri, 0, -$extLength);
621
		}
622
623
		return $objectUri;
624
	}
625
626
	/**
627
	 * Checks if the PHP-MAPI extension is available and in a requested version.
628
	 *
629
	 * @param string $version the version to be checked ("6.30.10-18495", parts or build number)
630
	 *
631
	 * @return bool installed version is superior to the checked string
632
	 */
633
	protected function checkMapiExtVersion($version = "") {
634
		if (!extension_loaded("mapi")) {
635
			return false;
636
		}
637
		// compare build number if requested
638
		if (preg_match('/^\d+$/', $version) && strlen($version) > 3) {
639
			$vs = preg_split('/-/', phpversion("mapi"));
640
641
			return $version <= $vs[1];
642
		}
643
		if (version_compare(phpversion("mapi"), $version) == -1) {
644
			return false;
645
		}
646
647
		return true;
648
	}
649
650
	/**
651
	 * Get named (custom) properties. Currently only PROP_APPTTSREF.
652
	 *
653
	 * @param string $id the folder id
654
	 *
655
	 * @return mixed
656
	 */
657
	protected function GetCustomProperties($id) {
658
		if (!isset($this->customprops[$id])) {
659
			$this->logger->trace("Fetching properties id:%s", $id);
660
			$store = $this->GetStoreById($id);
661
			$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

661
			$properties = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, [
Loading history...
662
				"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...
663
				"vcarduid" => MapiProps::PROP_VCARDUID,
664
			]);
665
			$this->customprops[$id] = $properties;
666
		}
667
668
		return $this->customprops[$id];
669
	}
670
671
	/**
672
	 * Create a MAPI restriction to use in the calendar which will
673
	 * return future calendar items (until $end), plus those since $start.
674
	 * Origins: Z-Push.
675
	 *
676
	 * @param mixed $store the MAPI store
677
	 * @param int   $start Timestamp since when to include messages
678
	 * @param int   $end   Ending timestamp
679
	 *
680
	 * @return array
681
	 */
682
	// TODO getting named properties
683
	public function GetCalendarRestriction($store, $start, $end) {
684
		$props = MapiProps::GetAppointmentProperties();
685
		$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

685
		$props = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, $props);
Loading history...
686
687
		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...
688
			[
689
				// OR
690
				// item.end > window.start && item.start < window.end
691
				[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...
692
					[
693
						[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...
694
							[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...
695
								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...
696
								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...
697
							],
698
						],
699
						[RES_PROPERTY,
700
							[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...
701
								ULPROPTAG => $props["endtime"],
702
								VALUE => $start,
703
							],
704
						],
705
					],
706
				],
707
				// OR
708
				[RES_OR,
709
					[
710
						// OR
711
						// (EXIST(recurrence_enddate_property) && item[isRecurring] == true && recurrence_enddate_property >= start)
712
						[RES_AND,
713
							[
714
								[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...
715
									[ULPROPTAG => $props["recurrenceend"],
716
									],
717
								],
718
								[RES_PROPERTY,
719
									[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...
720
										ULPROPTAG => $props["isrecurring"],
721
										VALUE => true,
722
									],
723
								],
724
								[RES_PROPERTY,
725
									[RELOP => RELOP_GE,
726
										ULPROPTAG => $props["recurrenceend"],
727
										VALUE => $start,
728
									],
729
								],
730
							],
731
						],
732
						// OR
733
						// (!EXIST(recurrence_enddate_property) && item[isRecurring] == true && item[start] <= end)
734
						[RES_AND,
735
							[
736
								[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...
737
									[
738
										[RES_EXIST,
739
											[ULPROPTAG => $props["recurrenceend"],
740
											],
741
										],
742
									],
743
								],
744
								[RES_PROPERTY,
745
									[RELOP => RELOP_LE,
746
										ULPROPTAG => $props["starttime"],
747
										VALUE => $end,
748
									],
749
								],
750
								[RES_PROPERTY,
751
									[RELOP => RELOP_EQ,
752
										ULPROPTAG => $props["isrecurring"],
753
										VALUE => true,
754
									],
755
								],
756
							],
757
						],
758
					],
759
				], // EXISTS OR
760
			],
761
		];        // global OR
762
	}
763
764
	/**
765
	 * Performs ICS based sync used from getChangesForAddressBook
766
	 * / getChangesForCalendar.
767
	 *
768
	 * @param string $folderId
769
	 * @param string $syncToken
770
	 * @param string $fileExtension
771
	 * @param int    $limit
772
	 * @param array  $filters
773
	 *
774
	 * @return null|array
775
	 */
776
	public function Sync($folderId, $syncToken, $fileExtension, $limit = null, $filters = []) {
777
		$arr = explode(':', $folderId);
778
		$phpwrapper = new PHPWrapper($this->GetStoreById($folderId), $this->logger, $this->GetCustomProperties($folderId), $fileExtension, $this->syncstate, $arr[1]);
779
		$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

779
		$mapiimporter = /** @scrutinizer ignore-call */ mapi_wrap_importcontentschanges($phpwrapper);
Loading history...
780
781
		$mapifolder = $this->GetMapiFolder($folderId);
782
		$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...
783
		if (!$exporter) {
784
			$this->logger->error("Unable to get exporter");
785
786
			return null;
787
		}
788
789
		$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

789
		$stream = /** @scrutinizer ignore-call */ mapi_stream_create();
Loading history...
790
		if ($syncToken == null) {
791
			mapi_stream_write($stream, hex2bin("0000000000000000"));
792
		}
793
		else {
794
			$value = $this->syncstate->getState($arr[1], $syncToken);
795
			if ($value === null) {
796
				$this->logger->error("Unable to get value from token: %s - folderId: %s", $syncToken, $folderId);
797
798
				return null;
799
			}
800
			mapi_stream_write($stream, hex2bin($value));
801
		}
802
803
		// force restriction of "types" to export only appointments or contacts
804
		$restriction = $this->getRestrictionForFilters($filters);
805
806
		// The last parameter in mapi_exportchanges_config is buffer size for mapi_exportchanges_synchronize - how many
807
		// changes will be processed in its call. Setting it to MAX_SYNC_ITEMS won't export more items than is set in
808
		// the config. If there are more changes than MAX_SYNC_ITEMS the client will eventually catch up and sync
809
		// the rest on the subsequent sync request(s).
810
		$bufferSize = ($limit !== null && $limit > 0) ? $limit : MAX_SYNC_ITEMS;
811
		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_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

811
		/** @scrutinizer ignore-call */ 
812
  mapi_exportchanges_config($exporter, $stream, SYNC_NORMAL | SYNC_UNICODE, $mapiimporter, $restriction, false, false, $bufferSize);
Loading history...
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...
812
		$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

812
		$changesCount = /** @scrutinizer ignore-call */ mapi_exportchanges_getchangecount($exporter);
Loading history...
813
		$this->logger->debug("Exporter found %d changes, buffer size for mapi_exportchanges_synchronize %d", $changesCount, $bufferSize);
814
		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

814
		while (is_array(/** @scrutinizer ignore-call */ mapi_exportchanges_synchronize($exporter))) {
Loading history...
815
			if ($changesCount > $bufferSize) {
816
				$this->logger->info("There were too many changes to be exported in this request. Total changes %d, exported %d.", $changesCount, $phpwrapper->Total());
817
818
				break;
819
			}
820
		}
821
		$exportedChanges = $phpwrapper->Total();
822
		$this->logger->debug("Exported %d changes, pending %d", $exportedChanges, $changesCount - $exportedChanges);
823
824
		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

824
		/** @scrutinizer ignore-call */ 
825
  mapi_exportchanges_updatestate($exporter, $stream);
Loading history...
825
		mapi_stream_seek($stream, 0, STREAM_SEEK_SET);
0 ignored issues
show
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...
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

825
		/** @scrutinizer ignore-call */ 
826
  mapi_stream_seek($stream, 0, STREAM_SEEK_SET);
Loading history...
826
		$state = "";
827
		while (true) {
828
			$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

828
			$data = /** @scrutinizer ignore-call */ mapi_stream_read($stream, 4096);
Loading history...
829
			if (strlen($data) > 0) {
830
				$state .= $data;
831
			}
832
			else {
833
				break;
834
			}
835
		}
836
837
		$newtoken = ($phpwrapper->Total() > 0) ? uniqid() : $syncToken;
838
839
		$this->syncstate->setState($arr[1], $newtoken, bin2hex($state));
840
841
		$result = [
842
			"syncToken" => $newtoken,
843
			"added" => $phpwrapper->GetAdded(),
844
			"modified" => $phpwrapper->GetModified(),
845
			"deleted" => $phpwrapper->GetDeleted(),
846
		];
847
848
		$this->logger->trace("Returning %s", $result);
849
850
		return $result;
851
	}
852
853
	/**
854
	 * Returns an array of necessary properties to set with default values.
855
	 *
856
	 * @see MapiProps::GetDefault...Properties()
857
	 *
858
	 * @param mixed $id           storeid
859
	 * @param mixed $mapimessage  mapi message to check
860
	 * @param array $propList     array of mapped properties
861
	 * @param array $defaultProps array of necessary properties with default values
862
	 *
863
	 * @return array
864
	 */
865
	public function GetPropsToSet($id, $mapimessage, $propList, $defaultProps) {
866
		$propsToSet = [];
867
		$store = $this->GetStoreById($id);
868
		$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

868
		$propList = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, $propList);
Loading history...
869
		$props = mapi_getprops($mapimessage);
870
871
		foreach ($defaultProps as $prop => $value) {
872
			if (!isset($props[$propList[$prop]])) {
873
				$propsToSet[$propList[$prop]] = $value;
874
			}
875
		}
876
877
		return $propsToSet;
878
	}
879
880
	/**
881
	 * Checks whether the user is enabled for grommunio-dav.
882
	 *
883
	 * @return bool
884
	 */
885
	private function isGdavEnabled() {
886
		$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...
887
		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...
888
			$this->logger->debug("user %s is enabled for grommunio-dav", $this->user);
889
890
			return true;
891
		}
892
		$this->logger->debug("user %s is disabled for grommunio-dav", $this->user);
893
894
		return false;
895
	}
896
}
897