Issues (171)

lib/GrommunioDavBackend.php (89 issues)

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
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
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
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
The constant grommunio\DAV\PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
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
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
The constant grommunio\DAV\MAPI_DEFERRED_ERRORS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
The constant grommunio\DAV\RELOP_EQ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_CONTAINER_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\VALUE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\ULPROPTAG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\RELOP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
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
The constant grommunio\DAV\PR_PARENT_SOURCE_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_COMMENT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_PARENT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_FOLDER_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_DISPLAY_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_SOURCE_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
The constant grommunio\DAV\PR_IPM_CONTACT_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
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
141
			if (isset($row[PR_PARENT_ENTRYID], $storeprops[PR_IPM_WASTEBASKET_ENTRYID]) && $row[PR_PARENT_ENTRYID] == $storeprops[PR_IPM_WASTEBASKET_ENTRYID]) {
142
				continue;
143
			}
144
145
			$folder = [
146
				'id' => $principalUri . ":" . bin2hex($row[PR_SOURCE_KEY]),
147
				'uri' => $row[PR_DISPLAY_NAME],
148
				'principaluri' => $principalUri,
149
				'{http://sabredav.org/ns}sync-token' => '0000000000',
150
				'{DAV:}displayname' => $row[PR_DISPLAY_NAME],
151
				'{urn:ietf:params:xml:ns:caldav}calendar-description' => $row[PR_COMMENT],
152
				'{http://calendarserver.org/ns/}getctag' => isset($row[PR_LOCAL_COMMIT_TIME_MAX]) ? strval($row[PR_LOCAL_COMMIT_TIME_MAX]) : '0000000000',
153
			];
154
155
			// set the supported component (task or calendar)
156
			if ($row[PR_CONTAINER_CLASS] == "IPF.Task") {
157
				$folder['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'] = new SupportedCalendarComponentSet(['VTODO']);
158
			}
159
			if ($row[PR_CONTAINER_CLASS] == "IPF.Appointment") {
160
				$folder['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'] = new SupportedCalendarComponentSet(['VEVENT']);
161
			}
162
163
			// ensure default contacts folder is put first, some clients
164
			// i.e. Apple Addressbook only supports one contact folder,
165
			// therefore it is desired that folder is the default one.
166
			if (in_array("IPF.Contact", $classes) && isset($rootprops[PR_IPM_CONTACT_ENTRYID]) && $row[PR_ENTRYID] == $rootprops[PR_IPM_CONTACT_ENTRYID]) {
167
				array_unshift($folders, $folder);
168
			}
169
			// ensure default calendar folder is put first,
170
			// before the tasks folder.
171
			elseif (in_array('IPF.Appointment', $classes) && isset($rootprops[PR_IPM_APPOINTMENT_ENTRYID]) && $row[PR_ENTRYID] == $rootprops[PR_IPM_APPOINTMENT_ENTRYID]) {
172
				array_unshift($folders, $folder);
173
			}
174
			else {
175
				array_push($folders, $folder);
176
			}
177
		}
178
		$this->logger->trace('found %d folders: %s', count($folders), $folders);
179
180
		return $folders;
181
	}
182
183
	/**
184
	 * Returns a MAPI restriction for a defined set of filters.
185
	 *
186
	 * @param array  $filters
187
	 * @param string $storeId (optional) mapi compatible storeid - required when using start+end filter
188
	 *
189
	 * @return null|array
190
	 */
191
	private function getRestrictionForFilters($filters, $storeId = null) {
192
		$restrictions = [];
193
		if (isset($filters['start'], $filters['end'], $storeId)) {
194
			$this->logger->trace("getRestrictionForFilters - got start: %d and end: %d", $filters['start'], $filters['end']);
195
			$subrestriction = $this->GetCalendarRestriction($storeId, $filters['start'], $filters['end']);
196
			$restrictions[] = $subrestriction;
197
		}
198
		if (isset($filters['types'])) {
199
			$this->logger->trace("getRestrictionForFilters - got types: %s", $filters['types']);
200
			$arr = [];
201
			foreach ($filters['types'] as $filter) {
202
				$arr[] = [RES_PROPERTY,
0 ignored issues
show
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
The constant grommunio\DAV\RELOP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
The constant grommunio\DAV\PR_MESSAGE_CLASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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
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
			$restrictions[] = [RES_OR, $arr];
0 ignored issues
show
The constant grommunio\DAV\RES_OR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
210
		}
211
		if (!empty($restrictions)) {
212
			$restriction = [RES_AND, $restrictions];
0 ignored issues
show
The constant grommunio\DAV\RES_AND was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
213
			$this->logger->trace("getRestrictionForFilters - got restriction: %s", simplifyRestriction($restriction));
0 ignored issues
show
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

213
			$this->logger->trace("getRestrictionForFilters - got restriction: %s", /** @scrutinizer ignore-call */ simplifyRestriction($restriction));
Loading history...
214
215
			return $restriction;
216
		}
217
218
		return null;
219
	}
220
221
	/**
222
	 * Returns a list of objects for a folder given by the id.
223
	 *
224
	 * @param string $id
225
	 * @param string $fileExtension
226
	 * @param array  $filters
227
	 *
228
	 * @return array
229
	 */
230
	public function GetObjects($id, $fileExtension, $filters = []) {
231
		$folder = $this->GetMapiFolder($id);
232
		$properties = $this->GetCustomProperties($id);
233
		$table = mapi_folder_getcontentstable($folder, MAPI_DEFERRED_ERRORS);
0 ignored issues
show
The constant grommunio\DAV\MAPI_DEFERRED_ERRORS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
234
		$restriction = $this->getRestrictionForFilters($filters, $this->GetStoreById($id));
0 ignored issues
show
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

234
		$restriction = $this->getRestrictionForFilters($filters, /** @scrutinizer ignore-type */ $this->GetStoreById($id));
Loading history...
235
		if ($restriction) {
236
			mapi_table_restrict($table, $restriction);
237
		}
238
239
		$rows = mapi_table_queryallrows($table, [PR_SOURCE_KEY, PR_LAST_MODIFICATION_TIME, PR_MESSAGE_SIZE, $properties['goid']]);
0 ignored issues
show
The constant grommunio\DAV\PR_LAST_MODIFICATION_TIME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_MESSAGE_SIZE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_SOURCE_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
240
241
		$results = [];
242
		foreach ($rows as $row) {
243
			$realId = "";
244
			if (isset($row[$properties['goid']])) {
245
				$realId = getUidFromGoid($row[$properties['goid']]);
0 ignored issues
show
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

245
				$realId = /** @scrutinizer ignore-call */ getUidFromGoid($row[$properties['goid']]);
Loading history...
246
			}
247
			if (!$realId) {
248
				$realId = bin2hex($row[PR_SOURCE_KEY]);
249
			}
250
			$realId = rawurlencode($realId);
251
252
			$result = [
253
				'id' => $realId,
254
				'uri' => $realId . $fileExtension,
255
				'etag' => '"' . $row[PR_LAST_MODIFICATION_TIME] . '"',
256
				'lastmodified' => $row[PR_LAST_MODIFICATION_TIME],
257
				'size' => $row[PR_MESSAGE_SIZE], // only approximation
258
			];
259
260
			if ($fileExtension == GrommunioCalDavBackend::FILE_EXTENSION) {
261
				$result['calendarid'] = $id;
262
			}
263
			elseif ($fileExtension == GrommunioCardDavBackend::FILE_EXTENSION) {
264
				$result['addressbookid'] = $id;
265
			}
266
			$results[] = $result;
267
		}
268
269
		return $results;
270
	}
271
272
	/**
273
	 * Create the object and set appttsref.
274
	 *
275
	 * @param mixed  $folderId
276
	 * @param mixed  $folder
277
	 * @param string $objectId
278
	 *
279
	 * @return mixed
280
	 */
281
	public function CreateObject($folderId, $folder, $objectId) {
282
		$mapimessage = mapi_folder_createmessage($folder);
283
		// we save the objectId in PROP_APPTTSREF so we find it by this id
284
		$properties = $this->GetCustomProperties($folderId);
285
		// FIXME: uid for contacts
286
		$goid = getGoidFromUid($objectId);
0 ignored issues
show
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

286
		$goid = /** @scrutinizer ignore-call */ getGoidFromUid($objectId);
Loading history...
287
		mapi_setprops($mapimessage, [$properties['goid'] => $goid]);
288
289
		return $mapimessage;
290
	}
291
292
	/**
293
	 * Returns a mapi folder resource for a folderid (PR_SOURCE_KEY).
294
	 *
295
	 * @param string $folderid
296
	 *
297
	 * @return mixed
298
	 */
299
	public function GetMapiFolder($folderid) {
300
		$this->logger->trace('Id: %s', $folderid);
301
		$arr = explode(':', $folderid);
302
		$entryid = mapi_msgstore_entryidfromsourcekey($this->GetStore($arr[0]), hex2bin($arr[1]));
303
304
		return mapi_msgstore_openentry($this->GetStore($arr[0]), $entryid);
305
	}
306
307
	/**
308
	 * Returns MAPI addressbook.
309
	 *
310
	 * @return mixed
311
	 */
312
	public function GetAddressBook() {
313
		// TODO should be a singleton
314
		return mapi_openaddressbook($this->session);
315
	}
316
317
	/**
318
	 * Opens MAPI store for the user.
319
	 *
320
	 * @param string $username
321
	 *
322
	 * @return mixed
323
	 */
324
	public function OpenMapiStore($username = null) {
325
		$msgstorestable = mapi_getmsgstorestable($this->session);
326
		$msgstores = mapi_table_queryallrows($msgstorestable, [PR_DEFAULT_STORE, PR_ENTRYID, PR_MDB_PROVIDER]);
0 ignored issues
show
The constant grommunio\DAV\PR_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_DEFAULT_STORE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_MDB_PROVIDER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
327
328
		$defaultstore = null;
329
		$publicstore = null;
330
		foreach ($msgstores as $row) {
331
			if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE]) {
332
				$defaultstore = $row[PR_ENTRYID];
333
			}
334
			if (isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
0 ignored issues
show
The constant grommunio\DAV\ZARAFA_STORE_PUBLIC_GUID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
335
				$publicstore = $row[PR_ENTRYID];
336
			}
337
		}
338
339
		/* user's own store or public store */
340
		if ($username == $this->GetUser() && $defaultstore != null) {
341
			return mapi_openmsgstore($this->session, $defaultstore);
342
		}
343
		if ($username == 'public' && $publicstore != null) {
344
			return mapi_openmsgstore($this->session, $publicstore);
345
		}
346
347
		/* otherwise other user's store */
348
		$store = mapi_openmsgstore($this->session, $defaultstore);
349
		if (!$store) {
350
			return false;
351
		}
352
		$otherstore = mapi_msgstore_createentryid($store, $username);
353
354
		return mapi_openmsgstore($this->session, $otherstore);
355
	}
356
357
	/**
358
	 * Returns store for the user.
359
	 *
360
	 * @param string $storename
361
	 *
362
	 * @return mixed
363
	 */
364
	public function GetStore($storename) {
365
		if ($storename == null) {
366
			$storename = $this->GetUser();
367
		}
368
		else {
369
			$storename = str_replace('principals/', '', $storename);
370
		}
371
		$this->logger->trace("storename %s", $storename);
372
373
		/* We already got the store */
374
		if (isset($this->stores[$storename]) && $this->stores[$storename] != null) {
375
			return $this->stores[$storename];
376
		}
377
378
		$this->stores[$storename] = $this->OpenMapiStore($storename);
379
		if (!$this->stores[$storename]) {
380
			$this->logger->info("Auth: ERROR - unable to open store for %s (0x%08X)", $storename, mapi_last_hresult());
381
382
			return false;
383
		}
384
385
		return $this->stores[$storename];
386
	}
387
388
	/**
389
	 * Returns store from the id.
390
	 *
391
	 * @param mixed $id
392
	 *
393
	 * @return mixed
394
	 */
395
	public function GetStoreById($id) {
396
		$arr = explode(':', $id);
397
398
		return $this->GetStore($arr[0]);
399
	}
400
401
	/**
402
	 * Returns logon session.
403
	 *
404
	 * @return mixed
405
	 */
406
	public function GetSession() {
407
		return $this->session;
408
	}
409
410
	/**
411
	 * Returns an object ID of a mapi object.
412
	 * If set, goid will be preferred. If not the PR_SOURCE_KEY of the message (as hex) will be returned.
413
	 *
414
	 * This order is reflected as well when searching for a message with these ids in GrommunioDavBackend->GetMapiMessageForId().
415
	 *
416
	 * @param string $folderId
417
	 * @param mixed  $mapimessage
418
	 *
419
	 * @return string
420
	 */
421
	public function GetIdOfMapiMessage($folderId, $mapimessage) {
422
		$this->logger->trace("Finding ID of %s", $mapimessage);
423
		$properties = $this->GetCustomProperties($folderId);
424
425
		// It's one of these, order:
426
		// - GOID (if set)
427
		// - PROP_VCARDUID (if set)
428
		// - PR_SOURCE_KEY
429
		$props = mapi_getprops($mapimessage, [$properties['goid'], PR_SOURCE_KEY]);
0 ignored issues
show
The constant grommunio\DAV\PR_SOURCE_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
430
		if (isset($props[$properties['goid']])) {
431
			$id = getUidFromGoid($props[$properties['goid']]);
0 ignored issues
show
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

431
			$id = /** @scrutinizer ignore-call */ getUidFromGoid($props[$properties['goid']]);
Loading history...
432
			$this->logger->debug("Found uid %s from goid: %s", $id, bin2hex($props[$properties['goid']]));
433
			if ($id != null) {
434
				return rawurlencode($id);
435
			}
436
		}
437
		// PR_SOURCE_KEY is always available
438
		$id = bin2hex($props[PR_SOURCE_KEY]);
439
		$this->logger->debug("Found PR_SOURCE_KEY: %s", $id);
440
441
		return $id;
442
	}
443
444
	/**
445
	 * Finds and opens a MapiMessage from an objectId.
446
	 * The id can be a PROP_APPTTSREF or a PR_SOURCE_KEY (as hex).
447
	 *
448
	 * @param string $folderId
449
	 * @param string $objectUri
450
	 * @param mixed  $mapifolder optional
451
	 * @param string $extension  optional
452
	 *
453
	 * @return mixed
454
	 */
455
	public function GetMapiMessageForId($folderId, $objectUri, $mapifolder = null, $extension = null) {
456
		$this->logger->trace("Searching for '%s' in '%s' (%s) (%s)", $objectUri, $folderId, $mapifolder, $extension);
457
458
		if (!$mapifolder) {
459
			$mapifolder = $this->GetMapiFolder($folderId);
460
		}
461
462
		$id = rawurldecode($this->GetObjectIdFromObjectUri($objectUri, $extension));
463
464
		/* The ID can be several different things:
465
		 * - a UID that is saved in goid
466
		 * - a PROP_VCARDUID
467
		 * - a PR_SOURCE_KEY
468
		 *
469
		 * If it's a sourcekey, we can open the message directly.
470
		 * If the $extension is set:
471
		 *      if it's ics:
472
		 *          - search GOID with this value
473
		 *      if it's vcf:
474
		 *          - search PROP_VCARDUID value
475
		 */
476
		$entryid = false;
477
		$restriction = false;
478
479
		if (ctype_xdigit($id) && strlen($id) % 2 == 0) {
480
			$this->logger->trace("Try PR_SOURCE_KEY %s", $id);
481
			$arr = explode(':', $folderId);
482
			$entryid = mapi_msgstore_entryidfromsourcekey($this->GetStoreById($arr[0]), hex2bin($arr[1]), hex2bin($id));
483
		}
484
485
		if (!$entryid) {
486
			$this->logger->trace("Entryid not found. Try goid/vcarduid %s", $id);
487
488
			$properties = $this->GetCustomProperties($folderId);
489
			$restriction = [];
490
491
			if ($extension) {
492
				if ($extension == GrommunioCalDavBackend::FILE_EXTENSION) {
493
					$this->logger->trace("Try goid %s", $id);
494
					$goid = getGoidFromUid($id);
0 ignored issues
show
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

494
					$goid = /** @scrutinizer ignore-call */ getGoidFromUid($id);
Loading history...
495
					$this->logger->trace("Try goid 0x%08X => %s", $properties["goid"], bin2hex($goid));
496
					$goid0 = getGoidFromUidZero($id);
0 ignored issues
show
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

496
					$goid0 = /** @scrutinizer ignore-call */ getGoidFromUidZero($id);
Loading history...
497
					$restriction[] = [RES_OR, [
0 ignored issues
show
The constant grommunio\DAV\RES_OR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
498
						[RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => $properties["goid"], VALUE => $goid]],
0 ignored issues
show
The constant grommunio\DAV\ULPROPTAG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\RELOP_EQ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\RELOP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\VALUE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\RES_PROPERTY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
499
						[RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => $properties["goid"], VALUE => $goid0]],
500
					]];
501
				}
502
				elseif ($extension == GrommunioCardDavBackend::FILE_EXTENSION) {
503
					$this->logger->trace("Try vcarduid %s", $id);
504
					$restriction[] = [RES_PROPERTY, [RELOP => RELOP_EQ, ULPROPTAG => $properties["vcarduid"], VALUE => $id]];
505
				}
506
			}
507
		}
508
509
		// find the message if we have a restriction
510
		if ($restriction) {
511
			$table = mapi_folder_getcontentstable($mapifolder, MAPI_DEFERRED_ERRORS);
0 ignored issues
show
The constant grommunio\DAV\MAPI_DEFERRED_ERRORS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
512
			mapi_table_restrict($table, [RES_OR, $restriction]);
513
			// Get requested properties, plus whatever we need
514
			$proplist = [PR_ENTRYID];
0 ignored issues
show
The constant grommunio\DAV\PR_ENTRYID was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
515
			$rows = mapi_table_queryallrows($table, $proplist);
516
			if (count($rows) > 1) {
517
				$this->logger->warn("Found %d entries for id '%s' searching for message, returnin first in the list", count($rows), $id);
518
			}
519
			if (isset($rows[0], $rows[0][PR_ENTRYID])) {
520
				$entryid = $rows[0][PR_ENTRYID];
521
			}
522
		}
523
		if (!$entryid) {
524
			$this->logger->debug("Try to get entryid from appttsref");
525
			$arr = explode(':', $folderId);
526
			$sk = $this->syncstate->getSourcekey($arr[1], $id);
527
			if ($sk !== null) {
528
				$this->logger->debug("Found sourcekey from appttsref %s", $sk);
529
				$entryid = mapi_msgstore_entryidfromsourcekey($this->GetStoreById($arr[0]), hex2bin($arr[1]), hex2bin($sk));
530
			}
531
		}
532
		if ($entryid) {
533
			$mapimessage = mapi_msgstore_openentry($this->GetStoreById($folderId), $entryid);
534
			if (!$mapimessage) {
535
				$this->logger->warn("Error, unable to open entry id: %s 0x%X", bin2hex($entryid), mapi_last_hresult());
536
537
				return null;
538
			}
539
540
			return $mapimessage;
541
		}
542
		$this->logger->debug("Nothing found for %s", $id);
543
544
		return null;
545
	}
546
547
	/**
548
	 * Returns the objectId from an objectUri. It strips the file extension
549
	 * if it matches the passed one.
550
	 *
551
	 * @param string $objectUri
552
	 * @param string $extension
553
	 *
554
	 * @return string
555
	 */
556
	public function GetObjectIdFromObjectUri($objectUri, $extension) {
557
		if (!$extension) {
558
			return $objectUri;
559
		}
560
		$extLength = strlen($extension);
561
		if (substr($objectUri, -$extLength) === $extension) {
562
			return substr($objectUri, 0, -$extLength);
563
		}
564
565
		return $objectUri;
566
	}
567
568
	/**
569
	 * Checks if the PHP-MAPI extension is available and in a requested version.
570
	 *
571
	 * @param string $version the version to be checked ("6.30.10-18495", parts or build number)
572
	 *
573
	 * @return bool installed version is superior to the checked string
574
	 */
575
	protected function checkMapiExtVersion($version = "") {
576
		if (!extension_loaded("mapi")) {
577
			return false;
578
		}
579
		// compare build number if requested
580
		if (preg_match('/^\d+$/', $version) && strlen($version) > 3) {
581
			$vs = preg_split('/-/', phpversion("mapi"));
582
583
			return $version <= $vs[1];
584
		}
585
		if (version_compare(phpversion("mapi"), $version) == -1) {
586
			return false;
587
		}
588
589
		return true;
590
	}
591
592
	/**
593
	 * Get named (custom) properties. Currently only PROP_APPTTSREF.
594
	 *
595
	 * @param string $id the folder id
596
	 *
597
	 * @return mixed
598
	 */
599
	protected function GetCustomProperties($id) {
600
		if (!isset($this->customprops[$id])) {
601
			$this->logger->trace("Fetching properties id:%s", $id);
602
			$store = $this->GetStoreById($id);
603
			$properties = getPropIdsFromStrings($store, [
0 ignored issues
show
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

603
			$properties = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, [
Loading history...
604
				"goid" => "PT_BINARY:PSETID_Meeting:" . PidLidGlobalObjectId,
0 ignored issues
show
The constant grommunio\DAV\PidLidGlobalObjectId was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
605
				"vcarduid" => MapiProps::PROP_VCARDUID,
606
			]);
607
			$this->customprops[$id] = $properties;
608
		}
609
610
		return $this->customprops[$id];
611
	}
612
613
	/**
614
	 * Create a MAPI restriction to use in the calendar which will
615
	 * return future calendar items (until $end), plus those since $start.
616
	 * Origins: Z-Push.
617
	 *
618
	 * @param mixed $store the MAPI store
619
	 * @param int   $start Timestamp since when to include messages
620
	 * @param int   $end   Ending timestamp
621
	 *
622
	 * @return array
623
	 */
624
	// TODO getting named properties
625
	public function GetCalendarRestriction($store, $start, $end) {
626
		$props = MapiProps::GetAppointmentProperties();
627
		$props = getPropIdsFromStrings($store, $props);
0 ignored issues
show
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

627
		$props = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, $props);
Loading history...
628
629
		return [RES_OR,
0 ignored issues
show
The constant grommunio\DAV\RES_OR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
630
			[
631
				// OR
632
				// item.end > window.start && item.start < window.end
633
				[RES_AND,
0 ignored issues
show
The constant grommunio\DAV\RES_AND was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
634
					[
635
						[RES_PROPERTY,
0 ignored issues
show
The constant grommunio\DAV\RES_PROPERTY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
636
							[RELOP => RELOP_LE,
0 ignored issues
show
The constant grommunio\DAV\RELOP was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\RELOP_LE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
637
								ULPROPTAG => $props["starttime"],
0 ignored issues
show
The constant grommunio\DAV\ULPROPTAG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
638
								VALUE => $end,
0 ignored issues
show
The constant grommunio\DAV\VALUE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
639
							],
640
						],
641
						[RES_PROPERTY,
642
							[RELOP => RELOP_GE,
0 ignored issues
show
The constant grommunio\DAV\RELOP_GE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
643
								ULPROPTAG => $props["endtime"],
644
								VALUE => $start,
645
							],
646
						],
647
					],
648
				],
649
				// OR
650
				[RES_OR,
651
					[
652
						// OR
653
						// (EXIST(recurrence_enddate_property) && item[isRecurring] == true && recurrence_enddate_property >= start)
654
						[RES_AND,
655
							[
656
								[RES_EXIST,
0 ignored issues
show
The constant grommunio\DAV\RES_EXIST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
657
									[ULPROPTAG => $props["recurrenceend"],
658
									],
659
								],
660
								[RES_PROPERTY,
661
									[RELOP => RELOP_EQ,
0 ignored issues
show
The constant grommunio\DAV\RELOP_EQ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
662
										ULPROPTAG => $props["isrecurring"],
663
										VALUE => true,
664
									],
665
								],
666
								[RES_PROPERTY,
667
									[RELOP => RELOP_GE,
668
										ULPROPTAG => $props["recurrenceend"],
669
										VALUE => $start,
670
									],
671
								],
672
							],
673
						],
674
						// OR
675
						// (!EXIST(recurrence_enddate_property) && item[isRecurring] == true && item[start] <= end)
676
						[RES_AND,
677
							[
678
								[RES_NOT,
0 ignored issues
show
The constant grommunio\DAV\RES_NOT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
679
									[
680
										[RES_EXIST,
681
											[ULPROPTAG => $props["recurrenceend"],
682
											],
683
										],
684
									],
685
								],
686
								[RES_PROPERTY,
687
									[RELOP => RELOP_LE,
688
										ULPROPTAG => $props["starttime"],
689
										VALUE => $end,
690
									],
691
								],
692
								[RES_PROPERTY,
693
									[RELOP => RELOP_EQ,
694
										ULPROPTAG => $props["isrecurring"],
695
										VALUE => true,
696
									],
697
								],
698
							],
699
						],
700
					],
701
				], // EXISTS OR
702
			],
703
		];        // global OR
704
	}
705
706
	/**
707
	 * Performs ICS based sync used from getChangesForAddressBook
708
	 * / getChangesForCalendar.
709
	 *
710
	 * @param string $folderId
711
	 * @param string $syncToken
712
	 * @param string $fileExtension
713
	 * @param int    $limit
714
	 * @param array  $filters
715
	 *
716
	 * @return null|array
717
	 */
718
	public function Sync($folderId, $syncToken, $fileExtension, $limit = null, $filters = []) {
719
		$arr = explode(':', $folderId);
720
		$phpwrapper = new PHPWrapper($this->GetStoreById($folderId), $this->logger, $this->GetCustomProperties($folderId), $fileExtension, $this->syncstate, $arr[1]);
721
		$mapiimporter = mapi_wrap_importcontentschanges($phpwrapper);
0 ignored issues
show
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

721
		$mapiimporter = /** @scrutinizer ignore-call */ mapi_wrap_importcontentschanges($phpwrapper);
Loading history...
722
723
		$mapifolder = $this->GetMapiFolder($folderId);
724
		$exporter = mapi_openproperty($mapifolder, PR_CONTENTS_SYNCHRONIZER, IID_IExchangeExportChanges, 0, 0);
0 ignored issues
show
The constant grommunio\DAV\IID_IExchangeExportChanges was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\PR_CONTENTS_SYNCHRONIZER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
725
		if (!$exporter) {
726
			$this->logger->error("Unable to get exporter");
727
728
			return null;
729
		}
730
731
		$stream = mapi_stream_create();
0 ignored issues
show
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

731
		$stream = /** @scrutinizer ignore-call */ mapi_stream_create();
Loading history...
732
		if ($syncToken == null) {
733
			mapi_stream_write($stream, hex2bin("0000000000000000"));
734
		}
735
		else {
736
			$value = $this->syncstate->getState($arr[1], $syncToken);
737
			if ($value === null) {
738
				$this->logger->error("Unable to get value from token: %s - folderId: %s", $syncToken, $folderId);
739
740
				return null;
741
			}
742
			mapi_stream_write($stream, hex2bin($value));
743
		}
744
745
		// force restriction of "types" to export only appointments or contacts
746
		$restriction = $this->getRestrictionForFilters($filters);
747
748
		// The last parameter in mapi_exportchanges_config is buffer size for mapi_exportchanges_synchronize - how many
749
		// changes will be processed in its call. Setting it to MAX_SYNC_ITEMS won't export more items than is set in
750
		// the config. If there are more changes than MAX_SYNC_ITEMS the client will eventually catch up and sync
751
		// the rest on the subsequent sync request(s).
752
		$bufferSize = ($limit !== null && $limit > 0) ? $limit : MAX_SYNC_ITEMS;
753
		mapi_exportchanges_config($exporter, $stream, SYNC_NORMAL | SYNC_UNICODE, $mapiimporter, $restriction, false, false, $bufferSize);
0 ignored issues
show
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

753
		/** @scrutinizer ignore-call */ 
754
  mapi_exportchanges_config($exporter, $stream, SYNC_NORMAL | SYNC_UNICODE, $mapiimporter, $restriction, false, false, $bufferSize);
Loading history...
The constant grommunio\DAV\SYNC_NORMAL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
The constant grommunio\DAV\SYNC_UNICODE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
754
		$changesCount = mapi_exportchanges_getchangecount($exporter);
0 ignored issues
show
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

754
		$changesCount = /** @scrutinizer ignore-call */ mapi_exportchanges_getchangecount($exporter);
Loading history...
755
		$this->logger->debug("Exporter found %d changes, buffer size for mapi_exportchanges_synchronize %d", $changesCount, $bufferSize);
756
		while (is_array(mapi_exportchanges_synchronize($exporter))) {
0 ignored issues
show
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

756
		while (is_array(/** @scrutinizer ignore-call */ mapi_exportchanges_synchronize($exporter))) {
Loading history...
757
			if ($changesCount > $bufferSize) {
758
				$this->logger->info("There were too many changes to be exported in this request. Total changes %d, exported %d.", $changesCount, $phpwrapper->Total());
759
760
				break;
761
			}
762
		}
763
		$exportedChanges = $phpwrapper->Total();
764
		$this->logger->debug("Exported %d changes, pending %d", $exportedChanges, $changesCount - $exportedChanges);
765
766
		mapi_exportchanges_updatestate($exporter, $stream);
0 ignored issues
show
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

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

767
		/** @scrutinizer ignore-call */ 
768
  mapi_stream_seek($stream, 0, STREAM_SEEK_SET);
Loading history...
The constant grommunio\DAV\STREAM_SEEK_SET was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
768
		$state = "";
769
		while (true) {
770
			$data = mapi_stream_read($stream, 4096);
0 ignored issues
show
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

770
			$data = /** @scrutinizer ignore-call */ mapi_stream_read($stream, 4096);
Loading history...
771
			if (strlen($data) > 0) {
772
				$state .= $data;
773
			}
774
			else {
775
				break;
776
			}
777
		}
778
779
		$newtoken = ($phpwrapper->Total() > 0) ? uniqid() : $syncToken;
780
781
		$this->syncstate->setState($arr[1], $newtoken, bin2hex($state));
782
783
		$result = [
784
			"syncToken" => $newtoken,
785
			"added" => $phpwrapper->GetAdded(),
786
			"modified" => $phpwrapper->GetModified(),
787
			"deleted" => $phpwrapper->GetDeleted(),
788
		];
789
790
		$this->logger->trace("Returning %s", $result);
791
792
		return $result;
793
	}
794
795
	/**
796
	 * Returns an array of necessary properties to set with default values.
797
	 *
798
	 * @see MapiProps::GetDefault...Properties()
799
	 *
800
	 * @param mixed $id           storeid
801
	 * @param mixed $mapimessage  mapi message to check
802
	 * @param array $propList     array of mapped properties
803
	 * @param array $defaultProps array of necessary properties with default values
804
	 *
805
	 * @return array
806
	 */
807
	public function GetPropsToSet($id, $mapimessage, $propList, $defaultProps) {
808
		$propsToSet = [];
809
		$store = $this->GetStoreById($id);
810
		$propList = getPropIdsFromStrings($store, $propList);
0 ignored issues
show
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

810
		$propList = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, $propList);
Loading history...
811
		$props = mapi_getprops($mapimessage);
812
813
		foreach ($defaultProps as $prop => $value) {
814
			if (!isset($props[$propList[$prop]])) {
815
				$propsToSet[$propList[$prop]] = $value;
816
			}
817
		}
818
819
		return $propsToSet;
820
	}
821
822
	/**
823
	 * Checks whether the user is enabled for grommunio-dav.
824
	 *
825
	 * @return bool
826
	 */
827
	private function isGdavEnabled() {
828
		$storeProps = mapi_getprops($this->GetStore($this->GetUser()), [PR_EC_ENABLED_FEATURES_L]);
0 ignored issues
show
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...
829
		if ($storeProps[PR_EC_ENABLED_FEATURES_L] & UP_DAV) {
0 ignored issues
show
The constant grommunio\DAV\UP_DAV was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
830
			$this->logger->debug("user %s is enabled for grommunio-dav", $this->user);
831
832
			return true;
833
		}
834
		$this->logger->debug("user %s is disabled for grommunio-dav", $this->user);
835
836
		return false;
837
	}
838
}
839