GrommunioDavBackend::GetMapiMessageForId()   F
last analyzed

Complexity

Conditions 15
Paths 900

Size

Total Lines 90
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 50
c 1
b 0
f 0
nc 900
nop 4
dl 0
loc 90
rs 1.8888

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

768
		$mapiimporter = /** @scrutinizer ignore-call */ mapi_wrap_importcontentschanges($phpwrapper);
Loading history...
769
770
		$mapifolder = $this->GetMapiFolder($folderId);
771
		$exporter = mapi_openproperty($mapifolder, PR_CONTENTS_SYNCHRONIZER, IID_IExchangeExportChanges, 0, 0);
0 ignored issues
show
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...
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...
772
		if (!$exporter) {
773
			$this->logger->error("Unable to get exporter");
774
775
			return null;
776
		}
777
778
		$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

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

800
		/** @scrutinizer ignore-call */ 
801
  mapi_exportchanges_config($exporter, $stream, SYNC_NORMAL | SYNC_UNICODE, $mapiimporter, $restriction, false, false, $bufferSize);
Loading history...
Bug introduced by
The constant grommunio\DAV\SYNC_NORMAL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
801
		$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

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

803
		while (is_array(/** @scrutinizer ignore-call */ mapi_exportchanges_synchronize($exporter))) {
Loading history...
804
			if ($changesCount > $bufferSize) {
805
				$this->logger->info("There were too many changes to be exported in this request. Total changes %d, exported %d.", $changesCount, $phpwrapper->Total());
806
807
				break;
808
			}
809
		}
810
		$exportedChanges = $phpwrapper->Total();
811
		$this->logger->debug("Exported %d changes, pending %d", $exportedChanges, $changesCount - $exportedChanges);
812
813
		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

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

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

814
		/** @scrutinizer ignore-call */ 
815
  mapi_stream_seek($stream, 0, STREAM_SEEK_SET);
Loading history...
Bug introduced by
The constant grommunio\DAV\STREAM_SEEK_SET was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
815
		$state = "";
816
		while (true) {
817
			$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

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

857
		$propList = /** @scrutinizer ignore-call */ getPropIdsFromStrings($store, $propList);
Loading history...
858
		$props = mapi_getprops($mapimessage);
859
860
		foreach ($defaultProps as $prop => $value) {
861
			if (!isset($props[$propList[$prop]])) {
862
				$propsToSet[$propList[$prop]] = $value;
863
			}
864
		}
865
866
		return $propsToSet;
867
	}
868
869
	/**
870
	 * Returns the current sync-token for the folder if one was issued.
871
	 *
872
	 * @param string $folderId composite id in form principal:sourcekey
873
	 *
874
	 * @return string
875
	*/
876
	public function GetCurrentSyncToken($folderId) {
877
		$arr = explode(':', $folderId, 2);
878
		if (count($arr) < 2 || $arr[1] === '') {
879
			return '0000000000';
880
		}
881
882
		$token = $this->syncstate->getCurrentToken($arr[1]);
883
884
		return (!is_string($token) || $token === '') ? '0000000000' : $token;
885
	}
886
887
	/**
888
	 * Checks whether the user is enabled for grommunio-dav.
889
	 *
890
	 * @return bool
891
	 */
892
	private function isGdavEnabled() {
893
		$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...
894
		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...
895
			$this->logger->debug("user %s is enabled for grommunio-dav", $this->user);
896
897
			return true;
898
		}
899
		$this->logger->debug("user %s is disabled for grommunio-dav", $this->user);
900
901
		return false;
902
	}
903
}
904