Passed
Branch master (f497d2)
by Mike
03:18
created

Grommunio::isGSyncEnabled()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 14
nop 0
dl 0
loc 21
rs 8.8333
c 0
b 0
f 0
nc 7
1
<?php
2
/*
3
 * SPDX-License-Identifier: AGPL-3.0-only
4
 * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH
5
 * SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH
6
 *
7
 * This is a backend for grommunio. It is an implementation of IBackend and also
8
 * implements ISearchProvider to search in the grommunio system. The backend
9
 * implements IStateMachine as well to save the devices' information in the
10
 * user's store and extends InterProcessData to access Redis.
11
 */
12
13
// include PHP-MAPI classes
14
include_once 'mapi/mapi.util.php';
15
include_once 'mapi/mapidefs.php';
16
include_once 'mapi/mapitags.php';
17
include_once 'mapi/mapicode.php';
18
include_once 'mapi/mapiguid.php';
19
20
// setlocale to UTF-8 in order to support properties containing Unicode characters
21
setlocale(LC_CTYPE, "en_US.UTF-8");
22
23
class Grommunio extends InterProcessData implements IBackend, ISearchProvider, IStateMachine {
24
	private $mainUser;
25
	private $session;
26
	private $defaultstore;
27
	private $store;
28
	private $storeName;
29
	private $storeCache;
30
	private $notifications;
31
	private $changesSink;
32
	private $changesSinkFolders;
33
	private $changesSinkHierarchyHash;
34
	private $changesSinkStores;
35
	private $wastebasket;
36
	private $addressbook;
37
	private $folderStatCache;
38
	private $impersonateUser;
39
	private $stateFolder;
40
	private $userDeviceData;
41
42
	// KC config parameter for PR_EC_ENABLED_FEATURES / PR_EC_DISABLED_FEATURES
43
	public const MOBILE_ENABLED = 'mobile';
44
45
	public const MAXAMBIGUOUSRECIPIENTS = 9999;
46
	public const FREEBUSYENUMBLOCKS = 50;
47
	public const MAXFREEBUSYSLOTS = 32767; // max length of 32k for the MergedFreeBusy element is allowed
48
	public const HALFHOURSECONDS = 1800;
49
50
	/**
51
	 * Constructor of the grommunio Backend.
52
	 */
53
	public function __construct() {
54
		$this->session = false;
55
		$this->store = false;
56
		$this->storeName = false;
57
		$this->storeCache = [];
58
		$this->notifications = false;
59
		$this->changesSink = false;
60
		$this->changesSinkFolders = [];
61
		$this->changesSinkStores = [];
62
		$this->changesSinkHierarchyHash = false;
63
		$this->wastebasket = false;
64
		$this->session = false;
65
		$this->folderStatCache = [];
66
		$this->impersonateUser = false;
67
		$this->stateFolder = null;
68
69
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio using PHP-MAPI version: %s - PHP version: %s", phpversion("mapi"), phpversion()));
70
71
		# Interprocessdata
72
		$this->allocate = 0;
73
		$this->type = "grommunio-sync:userdevices";
74
		$this->userDeviceData = "grommunio-sync:statefoldercache";
75
		parent::__construct();
76
	}
77
78
	/**
79
	 * Indicates which StateMachine should be used.
80
	 *
81
	 * @return bool Grommunio uses own state machine
82
	 */
83
	public function GetStateMachine() {
84
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Grommunio which is incompatible with the documented return type boolean.
Loading history...
85
	}
86
87
	/**
88
	 * Returns the Grommunio as it implements the ISearchProvider interface
89
	 * This could be overwritten by the global configuration.
90
	 *
91
	 * @return object Implementation of ISearchProvider
92
	 */
93
	public function GetSearchProvider() {
94
		return $this;
95
	}
96
97
	/**
98
	 * Indicates which AS version is supported by the backend.
99
	 *
100
	 * @return string AS version constant
101
	 */
102
	public function GetSupportedASVersion() {
103
		return GSync::ASV_141;
104
	}
105
106
	/**
107
	 * Authenticates the user with the configured grommunio server.
108
	 *
109
	 * @param string $username
110
	 * @param string $domain
111
	 * @param string $password
112
	 * @param mixed  $user
113
	 * @param mixed  $pass
114
	 *
115
	 * @throws AuthenticationRequiredException
116
	 *
117
	 * @return bool
118
	 */
119
	public function Logon($user, $domain, $pass) {
120
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): Trying to authenticate user '%s'..", $user));
121
122
		$this->mainUser = strtolower($user);
123
		// TODO the impersonated user should be passed directly to IBackend->Logon() - ZP-1351
124
		if (Request::GetImpersonatedUser()) {
125
			$this->impersonateUser = strtolower(Request::GetImpersonatedUser());
0 ignored issues
show
Bug introduced by
It seems like Request::GetImpersonatedUser() can also be of type false; however, parameter $string of strtolower() 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

125
			$this->impersonateUser = strtolower(/** @scrutinizer ignore-type */ Request::GetImpersonatedUser());
Loading history...
126
		}
127
128
		// check if we are impersonating someone
129
		// $defaultUser will be used for $this->defaultStore
130
		if ($this->impersonateUser !== false) {
131
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): Impersonation active - authenticating: '%s' - impersonating '%s'", $this->mainUser, $this->impersonateUser));
0 ignored issues
show
Bug introduced by
$this->impersonateUser of type true is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

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

131
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): Impersonation active - authenticating: '%s' - impersonating '%s'", $this->mainUser, /** @scrutinizer ignore-type */ $this->impersonateUser));
Loading history...
132
			$defaultUser = $this->impersonateUser;
133
		}
134
		else {
135
			$defaultUser = $this->mainUser;
136
		}
137
138
		$deviceId = Request::GetDeviceID();
139
140
		try {
141
			// check if notifications are available in php-mapi
142
			if (function_exists('mapi_feature') && mapi_feature('LOGONFLAGS')) {
143
				// send grommunio-sync version and user agent to ZCP - ZP-589
144
				if (Utils::CheckMapiExtVersion('7.2.0')) {
145
					$gsync_version = 'Grommunio-Sync_' . @constant('GROMMUNIOSYNC_VERSION');
146
					$user_agent = ($deviceId) ? GSync::GetDeviceManager()->GetUserAgent() : "unknown";
147
					$this->session = @mapi_logon_zarafa($this->mainUser, $pass, MAPI_SERVER, null, null, 0, $gsync_version, $user_agent);
1 ignored issue
show
Bug introduced by
The function mapi_logon_zarafa 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

147
					$this->session = @/** @scrutinizer ignore-call */ mapi_logon_zarafa($this->mainUser, $pass, MAPI_SERVER, null, null, 0, $gsync_version, $user_agent);
Loading history...
148
				}
149
				else {
150
					$this->session = @mapi_logon_zarafa($this->mainUser, $pass, MAPI_SERVER, null, null, 0);
151
				}
152
				$this->notifications = true;
153
			}
154
			// old fashioned session
155
			else {
156
				$this->session = @mapi_logon_zarafa($this->mainUser, $pass, MAPI_SERVER);
157
				$this->notifications = false;
158
			}
159
160
			if (mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

160
			if (/** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
161
				SLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->Logon(): login failed with error code: 0x%X", mapi_last_hresult()));
162
				if (mapi_last_hresult() == MAPI_E_NETWORK_ERROR) {
163
					throw new ServiceUnavailableException("Error connecting to KC (login)");
164
				}
165
			}
166
		}
167
		catch (MAPIException $ex) {
168
			throw new AuthenticationRequiredException($ex->getDisplayMessage());
169
		}
170
171
		if (!$this->session) {
172
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): logon failed for user '%s'", $this->mainUser));
173
			$this->defaultstore = false;
174
175
			return false;
176
		}
177
178
		// Get/open default store
179
		$this->defaultstore = $this->openMessageStore($this->mainUser);
180
181
		// To impersonate, we overwrite the defaultstore. We still need to open it before we can do that.
182
		if ($this->impersonateUser) {
183
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): Impersonating user '%s'", $defaultUser));
184
			$this->defaultstore = $this->openMessageStore($defaultUser);
0 ignored issues
show
Bug introduced by
It seems like $defaultUser can also be of type true; however, parameter $user of Grommunio::openMessageStore() 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

184
			$this->defaultstore = $this->openMessageStore(/** @scrutinizer ignore-type */ $defaultUser);
Loading history...
185
		}
186
187
		if (mapi_last_hresult() == MAPI_E_FAILONEPROVIDER) {
188
			throw new ServiceUnavailableException("Error connecting to KC (open store)");
189
		}
190
191
		if ($this->defaultstore === false) {
192
			throw new AuthenticationRequiredException(sprintf("Grommunio->Logon(): User '%s' has no default store", $defaultUser));
193
		}
194
195
		$this->store = $this->defaultstore;
196
		$this->storeName = $defaultUser;
197
198
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): User '%s' is authenticated%s", $this->mainUser, ($this->impersonateUser ? " impersonating '" . $this->impersonateUser . "'" : '')));
0 ignored issues
show
Bug introduced by
Are you sure $this->impersonateUser of type true can be used in concatenation? ( Ignorable by Annotation )

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

198
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Logon(): User '%s' is authenticated%s", $this->mainUser, ($this->impersonateUser ? " impersonating '" . /** @scrutinizer ignore-type */ $this->impersonateUser . "'" : '')));
Loading history...
199
200
		$this->isGSyncEnabled();
201
202
		// check if this is a Zarafa 7 store with unicode support
203
		MAPIUtils::IsUnicodeStore($this->store);
0 ignored issues
show
Bug introduced by
$this->store of type true is incompatible with the type MAPIStore expected by parameter $store of MAPIUtils::IsUnicodeStore(). ( Ignorable by Annotation )

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

203
		MAPIUtils::IsUnicodeStore(/** @scrutinizer ignore-type */ $this->store);
Loading history...
204
205
		// open the state folder
206
		$this->getStateFolder($deviceId);
0 ignored issues
show
Bug introduced by
It seems like $deviceId can also be of type false; however, parameter $devid of Grommunio::getStateFolder() 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

206
		$this->getStateFolder(/** @scrutinizer ignore-type */ $deviceId);
Loading history...
207
208
		return true;
209
	}
210
211
	/**
212
	 * Setup the backend to work on a specific store or checks ACLs there.
213
	 * If only the $store is submitted, all Import/Export/Fetch/Etc operations should be
214
	 * performed on this store (switch operations store).
215
	 * If the ACL check is enabled, this operation should just indicate the ACL status on
216
	 * the submitted store, without changing the store for operations.
217
	 * For the ACL status, the currently logged on user MUST have access rights on
218
	 *  - the entire store - admin access if no folderid is sent, or
219
	 *  - on a specific folderid in the store (secretary/full access rights).
220
	 *
221
	 * The ACLcheck MUST fail if a folder of the authenticated user is checked!
222
	 *
223
	 * @param string $store        target store, could contain a "domain\user" value
224
	 * @param bool   $checkACLonly if set to true, Setup() should just check ACLs
225
	 * @param string $folderid     if set, only ACLs on this folderid are relevant
226
	 *
227
	 * @return bool
228
	 */
229
	public function Setup($store, $checkACLonly = false, $folderid = false) {
230
		list($user, $domain) = Utils::SplitDomainUser($store);
231
232
		if (!isset($this->mainUser)) {
233
			return false;
234
		}
235
236
		$mainUser = $this->mainUser;
237
		// when impersonating we need to check against the impersonated user
238
		if ($this->impersonateUser) {
239
			$mainUser = $this->impersonateUser;
240
		}
241
242
		if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
243
			$user = $mainUser;
244
		}
245
246
		// This is a special case. A user will get his entire folder structure by the foldersync by default.
247
		// The ACL check is executed when an additional folder is going to be sent to the mobile.
248
		// Configured that way the user could receive the same folderid twice, with two different names.
249
		if ($mainUser == $user && $checkACLonly && $folderid && !$this->impersonateUser) {
250
			SLog::Write(LOGLEVEL_DEBUG, "Grommunio->Setup(): Checking ACLs for folder of the users defaultstore. Fail is forced to avoid folder duplications on mobile.");
251
252
			return false;
253
		}
254
255
		// get the users store
256
		$userstore = $this->openMessageStore($user);
257
258
		// only proceed if a store was found, else return false
259
		if ($userstore) {
260
			// only check permissions
261
			if ($checkACLonly == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
262
				// check for admin rights
263
				if (!$folderid) {
264
					if ($user != $this->mainUser) {
265
						if ($this->impersonateUser) {
266
							$storeProps = mapi_getprops($userstore, [PR_IPM_SUBTREE_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

266
							$storeProps = /** @scrutinizer ignore-call */ mapi_getprops($userstore, [PR_IPM_SUBTREE_ENTRYID]);
Loading history...
267
							$rights = $this->HasSecretaryACLs($userstore, '', $storeProps[PR_IPM_SUBTREE_ENTRYID]);
0 ignored issues
show
Bug introduced by
$userstore of type true is incompatible with the type resource expected by parameter $store of Grommunio::HasSecretaryACLs(). ( Ignorable by Annotation )

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

267
							$rights = $this->HasSecretaryACLs(/** @scrutinizer ignore-type */ $userstore, '', $storeProps[PR_IPM_SUBTREE_ENTRYID]);
Loading history...
268
							SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Setup(): Checking for secretary ACLs on root folder of impersonated store '%s': '%s'", $user, Utils::PrintAsString($rights)));
269
						}
270
						else {
271
							$zarafauserinfo = @nsp_getuserinfo($this->mainUser);
1 ignored issue
show
Bug introduced by
The function nsp_getuserinfo 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

271
							$zarafauserinfo = @/** @scrutinizer ignore-call */ nsp_getuserinfo($this->mainUser);
Loading history...
272
							$rights = (isset($zarafauserinfo['admin']) && $zarafauserinfo['admin']) ? true : false;
273
							SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Setup(): Checking for admin ACLs on store '%s': '%s'", $user, Utils::PrintAsString($rights)));
274
						}
275
					}
276
					// the user has always full access to his own store
277
					else {
278
						$rights = true;
279
						SLog::Write(LOGLEVEL_DEBUG, "Grommunio->Setup(): the user has always full access to his own store");
280
					}
281
282
					return $rights;
283
				}
284
				// check permissions on this folder
285
286
				$rights = $this->HasSecretaryACLs($userstore, $folderid);
287
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->Setup(): Checking for secretary ACLs on '%s' of store '%s': '%s'", $folderid, $user, Utils::PrintAsString($rights)));
288
289
				return $rights;
290
			}
291
292
			// switch operations store
293
			// this should also be done if called with user = mainuser or user = false
294
			// which means to switch back to the default store
295
296
			// switch active store
297
			$this->store = $userstore;
298
			$this->storeName = $user;
299
300
			return true;
301
		}
302
303
		return false;
304
	}
305
306
	/**
307
	 * Logs off
308
	 * Free/Busy information is updated for modified calendars
309
	 * This is done after the synchronization process is completed.
310
	 *
311
	 * @return bool
312
	 */
313
	public function Logoff() {
314
		return true;
315
	}
316
317
	/**
318
	 * Returns an array of SyncFolder types with the entire folder hierarchy
319
	 * on the server (the array itself is flat, but refers to parents via the 'parent' property.
320
	 *
321
	 * provides AS 1.0 compatibility
322
	 *
323
	 * @return array SYNC_FOLDER
324
	 */
325
	public function GetHierarchy() {
326
		$folders = [];
327
		$mapiprovider = new MAPIProvider($this->session, $this->store);
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of MAPIProvider::__construct(). ( Ignorable by Annotation )

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

327
		$mapiprovider = new MAPIProvider($this->session, /** @scrutinizer ignore-type */ $this->store);
Loading history...
Bug introduced by
$this->session of type boolean is incompatible with the type resource expected by parameter $session of MAPIProvider::__construct(). ( Ignorable by Annotation )

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

327
		$mapiprovider = new MAPIProvider(/** @scrutinizer ignore-type */ $this->session, $this->store);
Loading history...
328
		$storeProps = $mapiprovider->GetStoreProps();
329
330
		// for SYSTEM user open the public folders
331
		if (strtoupper($this->storeName) == "SYSTEM") {
0 ignored issues
show
Bug introduced by
$this->storeName of type boolean is incompatible with the type string expected by parameter $string of strtoupper(). ( Ignorable by Annotation )

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

331
		if (strtoupper(/** @scrutinizer ignore-type */ $this->storeName) == "SYSTEM") {
Loading history...
332
			$rootfolder = mapi_msgstore_openentry($this->store, $storeProps[PR_IPM_PUBLIC_FOLDERS_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

332
			$rootfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $storeProps[PR_IPM_PUBLIC_FOLDERS_ENTRYID]);
Loading history...
333
		}
334
		else {
335
			$rootfolder = mapi_msgstore_openentry($this->store);
336
		}
337
338
		$rootfolderprops = mapi_getprops($rootfolder, [PR_SOURCE_KEY]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

338
		$rootfolderprops = /** @scrutinizer ignore-call */ mapi_getprops($rootfolder, [PR_SOURCE_KEY]);
Loading history...
339
340
		$hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable 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

340
		$hierarchy = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
341
		$rows = mapi_table_queryallrows($hierarchy, [PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_CONTAINER_CLASS, PR_ATTR_HIDDEN, PR_EXTENDED_FOLDER_FLAGS, PR_FOLDER_TYPE]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

341
		$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($hierarchy, [PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_CONTAINER_CLASS, PR_ATTR_HIDDEN, PR_EXTENDED_FOLDER_FLAGS, PR_FOLDER_TYPE]);
Loading history...
342
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetHierarchy(): fetched %d folders from MAPI", count($rows)));
343
344
		foreach ($rows as $row) {
345
			// do not display hidden and search folders
346
			if ((isset($row[PR_ATTR_HIDDEN]) && $row[PR_ATTR_HIDDEN]) ||
347
				(isset($row[PR_FOLDER_TYPE]) && $row[PR_FOLDER_TYPE] == FOLDER_SEARCH) ||
348
				// for SYSTEM user $row[PR_PARENT_SOURCE_KEY] == $rootfolderprops[PR_SOURCE_KEY] is true, but we need those folders
349
				(isset($row[PR_PARENT_SOURCE_KEY]) && $row[PR_PARENT_SOURCE_KEY] == $rootfolderprops[PR_SOURCE_KEY] && strtoupper($this->storeName) != "SYSTEM")) {
350
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetHierarchy(): ignoring folder '%s' as it's a hidden/search/root folder", (isset($row[PR_DISPLAY_NAME]) ? $row[PR_DISPLAY_NAME] : "unknown")));
351
352
				continue;
353
			}
354
			$folder = $mapiprovider->GetFolder($row);
355
			if ($folder) {
356
				$folders[] = $folder;
357
			}
358
			else {
359
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetHierarchy(): ignoring folder '%s' as MAPIProvider->GetFolder() did not return a SyncFolder object", (isset($row[PR_DISPLAY_NAME]) ? $row[PR_DISPLAY_NAME] : "unknown")));
360
			}
361
		}
362
363
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetHierarchy(): processed %d folders, starting parent remap", count($folders)));
364
		// reloop the folders to make sure all parentids are mapped correctly
365
		$dm = GSync::GetDeviceManager();
366
		foreach ($folders as $folder) {
367
			if ($folder->parentid !== "0") {
368
				// SYSTEM user's parentid points to $rootfolderprops[PR_SOURCE_KEY], but they need to be on the top level
369
				$folder->parentid = (strtoupper($this->storeName) == "SYSTEM" && $folder->parentid == bin2hex($rootfolderprops[PR_SOURCE_KEY])) ? '0' : $dm->GetFolderIdForBackendId($folder->parentid);
370
			}
371
		}
372
373
		return $folders;
374
	}
375
376
	/**
377
	 * Returns the importer to process changes from the mobile
378
	 * If no $folderid is given, hierarchy importer is expected.
379
	 *
380
	 * @param string $folderid (opt)
381
	 *
382
	 * @return object(ImportChanges)
383
	 */
384
	public function GetImporter($folderid = false) {
385
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetImporter() folderid: '%s'", Utils::PrintAsString($folderid)));
0 ignored issues
show
Bug introduced by
It seems like $folderid can also be of type false; however, parameter $var of Utils::PrintAsString() 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

385
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetImporter() folderid: '%s'", Utils::PrintAsString(/** @scrutinizer ignore-type */ $folderid)));
Loading history...
386
		if ($folderid !== false) {
387
			// check if the user of the current store has permissions to import to this folderid
388
			if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs($this->store, $folderid)) {
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of Grommunio::HasSecretaryACLs(). ( Ignorable by Annotation )

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

388
			if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs(/** @scrutinizer ignore-type */ $this->store, $folderid)) {
Loading history...
389
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetImporter(): missing permissions on folderid: '%s'.", Utils::PrintAsString($folderid)));
390
391
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type object.
Loading history...
392
			}
393
394
			return new ImportChangesICS($this->session, $this->store, hex2bin($folderid));
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type mapistore expected by parameter $store of ImportChangesICS::__construct(). ( Ignorable by Annotation )

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

394
			return new ImportChangesICS($this->session, /** @scrutinizer ignore-type */ $this->store, hex2bin($folderid));
Loading history...
Bug introduced by
$this->session of type boolean is incompatible with the type mapisession expected by parameter $session of ImportChangesICS::__construct(). ( Ignorable by Annotation )

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

394
			return new ImportChangesICS(/** @scrutinizer ignore-type */ $this->session, $this->store, hex2bin($folderid));
Loading history...
395
		}
396
397
		return new ImportChangesICS($this->session, $this->store);
398
	}
399
400
	/**
401
	 * Returns the exporter to send changes to the mobile
402
	 * If no $folderid is given, hierarchy exporter is expected.
403
	 *
404
	 * @param string $folderid (opt)
405
	 *
406
	 * @throws StatusException
407
	 *
408
	 * @return object(ExportChanges)
409
	 */
410
	public function GetExporter($folderid = false) {
411
		if ($folderid !== false) {
412
			// check if the user of the current store has permissions to export from this folderid
413
			if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs($this->store, $folderid)) {
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of Grommunio::HasSecretaryACLs(). ( Ignorable by Annotation )

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

413
			if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs(/** @scrutinizer ignore-type */ $this->store, $folderid)) {
Loading history...
414
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetExporter(): missing permissions on folderid: '%s'.", Utils::PrintAsString($folderid)));
415
416
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type object.
Loading history...
417
			}
418
419
			return new ExportChangesICS($this->session, $this->store, hex2bin($folderid));
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type mapistore expected by parameter $store of ExportChangesICS::__construct(). ( Ignorable by Annotation )

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

419
			return new ExportChangesICS($this->session, /** @scrutinizer ignore-type */ $this->store, hex2bin($folderid));
Loading history...
Bug introduced by
$this->session of type boolean is incompatible with the type mapisession expected by parameter $session of ExportChangesICS::__construct(). ( Ignorable by Annotation )

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

419
			return new ExportChangesICS(/** @scrutinizer ignore-type */ $this->session, $this->store, hex2bin($folderid));
Loading history...
420
		}
421
422
		return new ExportChangesICS($this->session, $this->store);
423
	}
424
425
	/**
426
	 * Sends an e-mail
427
	 * This messages needs to be saved into the 'sent items' folder.
428
	 *
429
	 * @param SyncSendMail $sm SyncSendMail object
430
	 *
431
	 * @throws StatusException
432
	 *
433
	 * @return bool
434
	 */
435
	public function SendMail($sm) {
436
		// Check if imtomapi function is available and use it to send the mime message.
437
		// It is available since ZCP 7.0.6
438
		// @see http://jira.zarafa.com/browse/ZCP-9508
439
		if (!(function_exists('mapi_feature') && mapi_feature('INETMAPI_IMTOMAPI'))) {
440
			throw new StatusException("Grommunio->SendMail(): ZCP/KC version is too old, INETMAPI_IMTOMAPI is not available. Install at least ZCP version 7.0.6 or later.", SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED, null, LOGLEVEL_FATAL);
441
442
			return false;
0 ignored issues
show
Unused Code introduced by
return false is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
443
		}
444
		$mimeLength = strlen($sm->mime);
445
		SLog::Write(LOGLEVEL_DEBUG, sprintf(
446
			"Grommunio->SendMail(): RFC822: %d bytes  forward-id: '%s' reply-id: '%s' parent-id: '%s' SaveInSent: '%s' ReplaceMIME: '%s'",
447
			$mimeLength,
448
			Utils::PrintAsString($sm->forwardflag),
449
			Utils::PrintAsString($sm->replyflag),
450
			Utils::PrintAsString((isset($sm->source->folderid) ? $sm->source->folderid : false)),
0 ignored issues
show
Bug introduced by
It seems like IssetNode ? $sm->source->folderid : false can also be of type false; however, parameter $var of Utils::PrintAsString() 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

450
			Utils::PrintAsString(/** @scrutinizer ignore-type */ (isset($sm->source->folderid) ? $sm->source->folderid : false)),
Loading history...
451
			Utils::PrintAsString(($sm->saveinsent)),
452
			Utils::PrintAsString(isset($sm->replacemime))
453
		));
454
		if ($mimeLength == 0) {
455
			throw new StatusException("Grommunio->SendMail(): empty mail data", SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED);
456
		}
457
458
		$sendMailProps = MAPIMapping::GetSendMailProperties();
459
		$sendMailProps = getPropIdsFromStrings($this->defaultstore, $sendMailProps);
460
461
		// Open the outbox and create the message there
462
		$storeprops = mapi_getprops($this->defaultstore, [$sendMailProps["outboxentryid"], $sendMailProps["ipmsentmailentryid"]]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

462
		$storeprops = /** @scrutinizer ignore-call */ mapi_getprops($this->defaultstore, [$sendMailProps["outboxentryid"], $sendMailProps["ipmsentmailentryid"]]);
Loading history...
463
		if (isset($storeprops[$sendMailProps["outboxentryid"]])) {
464
			$outbox = mapi_msgstore_openentry($this->defaultstore, $storeprops[$sendMailProps["outboxentryid"]]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

464
			$outbox = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->defaultstore, $storeprops[$sendMailProps["outboxentryid"]]);
Loading history...
465
		}
466
467
		if (!$outbox) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $outbox does not seem to be defined for all execution paths leading up to this point.
Loading history...
468
			throw new StatusException(sprintf("Grommunio->SendMail(): No Outbox found or unable to create message: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_SERVERERROR);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

468
			throw new StatusException(sprintf("Grommunio->SendMail(): No Outbox found or unable to create message: 0x%X", /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_COMMONSTATUS_SERVERERROR);
Loading history...
469
		}
470
471
		$mapimessage = mapi_folder_createmessage($outbox);
1 ignored issue
show
Bug introduced by
The function mapi_folder_createmessage 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

471
		$mapimessage = /** @scrutinizer ignore-call */ mapi_folder_createmessage($outbox);
Loading history...
472
473
		// message properties to be set
474
		$mapiprops = [];
475
		// only save the outgoing in sent items folder if the mobile requests it
476
		$mapiprops[$sendMailProps["sentmailentryid"]] = $storeprops[$sendMailProps["ipmsentmailentryid"]];
477
478
		SLog::Write(LOGLEVEL_DEBUG, "Grommunio->SendMail(): Use the mapi_inetmapi_imtomapi function");
479
		$ab = mapi_openaddressbook($this->session);
1 ignored issue
show
Bug introduced by
The function mapi_openaddressbook 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

479
		$ab = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
480
		mapi_inetmapi_imtomapi($this->session, $this->defaultstore, $ab, $mapimessage, $sm->mime, []);
1 ignored issue
show
Bug introduced by
The function mapi_inetmapi_imtomapi 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

480
		/** @scrutinizer ignore-call */ 
481
  mapi_inetmapi_imtomapi($this->session, $this->defaultstore, $ab, $mapimessage, $sm->mime, []);
Loading history...
481
482
		// Set the appSeqNr so that tracking tab can be updated for meeting request updates
483
		// @see http://jira.zarafa.com/browse/ZP-68
484
		$meetingRequestProps = MAPIMapping::GetMeetingRequestProperties();
485
		$meetingRequestProps = getPropIdsFromStrings($this->defaultstore, $meetingRequestProps);
486
		$props = mapi_getprops($mapimessage, [PR_MESSAGE_CLASS, $meetingRequestProps["goidtag"], $sendMailProps["internetcpid"], $sendMailProps["body"], $sendMailProps["html"], $sendMailProps["rtf"], $sendMailProps["rtfinsync"]]);
487
488
		// Convert sent message's body to UTF-8 if it was a HTML message.
489
		// @see http://jira.zarafa.com/browse/ZP-505 and http://jira.zarafa.com/browse/ZP-555
490
		if (isset($props[$sendMailProps["internetcpid"]]) && $props[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8 && MAPIUtils::GetNativeBodyType($props) == SYNC_BODYPREFERENCE_HTML) {
491
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->SendMail(): Sent email cpid is not unicode (%d). Set it to unicode and convert email html body.", $props[$sendMailProps["internetcpid"]]));
492
			$mapiprops[$sendMailProps["internetcpid"]] = INTERNET_CPID_UTF8;
493
494
			$bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML);
495
			$bodyHtml = Utils::ConvertCodepageStringToUtf8($props[$sendMailProps["internetcpid"]], $bodyHtml);
496
			$mapiprops[$sendMailProps["html"]] = $bodyHtml;
497
498
			mapi_setprops($mapimessage, $mapiprops);
1 ignored issue
show
Bug introduced by
The function mapi_setprops 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

498
			/** @scrutinizer ignore-call */ 
499
   mapi_setprops($mapimessage, $mapiprops);
Loading history...
499
		}
500
		if (stripos($props[PR_MESSAGE_CLASS], "IPM.Schedule.Meeting.Resp.") === 0) {
501
			// search for calendar items using goid
502
			$mr = new Meetingrequest($this->defaultstore, $mapimessage);
503
			$appointments = $mr->findCalendarItems($props[$meetingRequestProps["goidtag"]]);
504
			if (is_array($appointments) && !empty($appointments)) {
505
				$app = mapi_msgstore_openentry($this->defaultstore, $appointments[0]);
506
				$appprops = mapi_getprops($app, [$meetingRequestProps["appSeqNr"]]);
507
				if (isset($appprops[$meetingRequestProps["appSeqNr"]]) && $appprops[$meetingRequestProps["appSeqNr"]]) {
508
					$mapiprops[$meetingRequestProps["appSeqNr"]] = $appprops[$meetingRequestProps["appSeqNr"]];
509
					SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->SendMail(): Set sequence number to:%d", $appprops[$meetingRequestProps["appSeqNr"]]));
510
				}
511
			}
512
		}
513
514
		// Delete the PR_SENT_REPRESENTING_* properties because some android devices
515
		// do not send neither From nor Sender header causing empty PR_SENT_REPRESENTING_NAME and
516
		// PR_SENT_REPRESENTING_EMAIL_ADDRESS properties and "broken" PR_SENT_REPRESENTING_ENTRYID
517
		// which results in spooler not being able to send the message.
518
		// @see http://jira.zarafa.com/browse/ZP-85
519
		mapi_deleteprops(
1 ignored issue
show
Bug introduced by
The function mapi_deleteprops 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

519
		/** @scrutinizer ignore-call */ 
520
  mapi_deleteprops(
Loading history...
520
			$mapimessage,
521
			[
522
				$sendMailProps["sentrepresentingname"],
523
				$sendMailProps["sentrepresentingemail"],
524
				$sendMailProps["representingentryid"],
525
				$sendMailProps["sentrepresentingaddt"],
526
				$sendMailProps["sentrepresentinsrchk"],
527
			]
528
		);
529
530
		if (isset($sm->source->itemid) && $sm->source->itemid) {
531
			// answering an email in a public/shared folder
532
			// TODO as the store is setup, we should actually user $this->store instead of $this->defaultstore - nevertheless we need to make sure this store is able to send mail (has an outbox)
533
			if (!$this->Setup(GSync::GetAdditionalSyncFolderStore($sm->source->folderid))) {
534
				throw new StatusException(sprintf("Grommunio->SendMail() could not Setup() the backend for folder id '%s'", $sm->source->folderid), SYNC_COMMONSTATUS_SERVERERROR);
535
			}
536
537
			$entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey 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

537
			$entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid));
Loading history...
538
			if ($entryid) {
539
				$fwmessage = mapi_msgstore_openentry($this->store, $entryid);
540
			}
541
542
			if (isset($fwmessage) && $fwmessage) {
543
				// update icon and last_verb when forwarding or replying message
544
				// reply-all (verb 103) is not supported, as we cannot really detect this case
545
				if ($sm->forwardflag) {
546
					$updateProps = [
547
						PR_ICON_INDEX => 262,
548
						PR_LAST_VERB_EXECUTED => 104,
549
					];
550
				}
551
				elseif ($sm->replyflag) {
552
					$updateProps = [
553
						PR_ICON_INDEX => 261,
554
						PR_LAST_VERB_EXECUTED => 102,
555
					];
556
				}
557
				if (isset($updateProps)) {
558
					$updateProps[PR_LAST_VERB_EXECUTION_TIME] = time();
559
					mapi_setprops($fwmessage, $updateProps);
560
					mapi_savechanges($fwmessage);
1 ignored issue
show
Bug introduced by
The function mapi_savechanges 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

560
					/** @scrutinizer ignore-call */ 
561
     mapi_savechanges($fwmessage);
Loading history...
561
				}
562
563
				// only attach the original message if the mobile does not send it itself
564
				if (!isset($sm->replacemime)) {
565
					// get message's body in order to append forward or reply text
566
					if (!isset($body)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $body seems to never exist and therefore isset should always be false.
Loading history...
567
						$body = MAPIUtils::readPropStream($mapimessage, PR_BODY);
568
					}
569
					if (!isset($bodyHtml)) {
570
						$bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML);
571
					}
572
					$cpid = mapi_getprops($fwmessage, [$sendMailProps["internetcpid"]]);
573
					if ($sm->forwardflag) {
574
						// attach the original attachments to the outgoing message
575
						$this->copyAttachments($mapimessage, $fwmessage);
576
					}
577
578
					// regarding the conversion @see ZP-470
579
					if (strlen($body) > 0) {
580
						$fwbody = MAPIUtils::readPropStream($fwmessage, PR_BODY);
581
						// if only the old message's cpid is set, convert from old charset to utf-8
582
						if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) {
583
							SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->SendMail(): convert plain forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]]));
584
							$fwbody = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbody);
585
						}
586
						// otherwise to the general conversion
587
						else {
588
							SLog::Write(LOGLEVEL_DEBUG, "Grommunio->SendMail(): no charset conversion done for plain forwarded message");
589
							$fwbody = w2u($fwbody);
590
						}
591
592
						$mapiprops[$sendMailProps["body"]] = $body . "\r\n\r\n" . $fwbody;
0 ignored issues
show
Bug introduced by
Are you sure $fwbody of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

592
						$mapiprops[$sendMailProps["body"]] = $body . "\r\n\r\n" . /** @scrutinizer ignore-type */ $fwbody;
Loading history...
593
					}
594
595
					if (strlen($bodyHtml) > 0) {
596
						$fwbodyHtml = MAPIUtils::readPropStream($fwmessage, PR_HTML);
597
						// if only new message's cpid is set, convert to UTF-8
598
						if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) {
599
							SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->SendMail(): convert html forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]]));
600
							$fwbodyHtml = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbodyHtml);
601
						}
602
						// otherwise to the general conversion
603
						else {
604
							SLog::Write(LOGLEVEL_DEBUG, "Grommunio->SendMail(): no charset conversion done for html forwarded message");
605
							$fwbodyHtml = w2u($fwbodyHtml);
606
						}
607
608
						$mapiprops[$sendMailProps["html"]] = $bodyHtml . "<br><br>" . $fwbodyHtml;
0 ignored issues
show
Bug introduced by
Are you sure $fwbodyHtml of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

608
						$mapiprops[$sendMailProps["html"]] = $bodyHtml . "<br><br>" . /** @scrutinizer ignore-type */ $fwbodyHtml;
Loading history...
609
					}
610
				}
611
			}
612
			else {
613
				// no fwmessage could be opened and we need it because we do not replace mime
614
				if (!isset($sm->replacemime) || $sm->replacemime == false) {
615
					throw new StatusException(sprintf("Grommunio->SendMail(): Could not open message id '%s' in folder id '%s' to be replied/forwarded: 0x%X", $sm->source->itemid, $sm->source->folderid, mapi_last_hresult()), SYNC_COMMONSTATUS_ITEMNOTFOUND);
616
				}
617
			}
618
		}
619
620
		mapi_setprops($mapimessage, $mapiprops);
621
		mapi_savechanges($mapimessage);
622
		mapi_message_submitmessage($mapimessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_submitmessage 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

622
		/** @scrutinizer ignore-call */ 
623
  mapi_message_submitmessage($mapimessage);
Loading history...
623
		$hr = mapi_last_hresult();
624
625
		if ($hr) {
626
			switch ($hr) {
627
				case MAPI_E_STORE_FULL:
628
					$code = SYNC_COMMONSTATUS_MAILBOXQUOTAEXCEEDED;
629
					break;
630
631
				default:
632
					$code = SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED;
633
					break;
634
			}
635
636
			throw new StatusException(sprintf("Grommunio->SendMail(): Error saving/submitting the message to the Outbox: 0x%X", $hr), $code);
637
		}
638
639
		SLog::Write(LOGLEVEL_DEBUG, "Grommunio->SendMail(): email submitted");
640
641
		return true;
642
	}
643
644
	/**
645
	 * Returns all available data of a single message.
646
	 *
647
	 * @param string            $folderid
648
	 * @param string            $id
649
	 * @param ContentParameters $contentparameters flag
650
	 *
651
	 * @throws StatusException
652
	 *
653
	 * @return object(SyncObject)
654
	 */
655
	public function Fetch($folderid, $id, $contentparameters) {
656
		// SEARCH fetches with folderid == false and PR_ENTRYID as ID
657
		if (!$folderid) {
658
			$entryid = hex2bin($id);
659
			$sk = $id;
660
		}
661
		else {
662
			// id might be in the new longid format, so we have to split it here
663
			list($fsk, $sk) = Utils::SplitMessageId($id);
664
			// get the entry id of the message
665
			$entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($sk));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey 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

665
			$entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($sk));
Loading history...
666
		}
667
		if (!$entryid) {
668
			throw new StatusException(sprintf("Grommunio->Fetch('%s','%s'): Error getting entryid: 0x%X", $folderid, $sk, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

668
			throw new StatusException(sprintf("Grommunio->Fetch('%s','%s'): Error getting entryid: 0x%X", $folderid, $sk, /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
Loading history...
669
		}
670
671
		// open the message
672
		$message = mapi_msgstore_openentry($this->store, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

672
		$message = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $entryid);
Loading history...
673
		if (!$message) {
674
			throw new StatusException(sprintf("Grommunio->Fetch('%s','%s'): Error, unable to open message: 0x%X", $folderid, $sk, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
675
		}
676
677
		// convert the mapi message into a SyncObject and return it
678
		$mapiprovider = new MAPIProvider($this->session, $this->store);
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of MAPIProvider::__construct(). ( Ignorable by Annotation )

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

678
		$mapiprovider = new MAPIProvider($this->session, /** @scrutinizer ignore-type */ $this->store);
Loading history...
Bug introduced by
$this->session of type boolean is incompatible with the type resource expected by parameter $session of MAPIProvider::__construct(). ( Ignorable by Annotation )

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

678
		$mapiprovider = new MAPIProvider(/** @scrutinizer ignore-type */ $this->session, $this->store);
Loading history...
679
680
		// override truncation
681
		$contentparameters->SetTruncation(SYNC_TRUNCATION_ALL);
0 ignored issues
show
Bug introduced by
The method SetTruncation() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

681
		$contentparameters->/** @scrutinizer ignore-call */ 
682
                      SetTruncation(SYNC_TRUNCATION_ALL);
Loading history...
682
		// TODO check for body preferences
683
		return $mapiprovider->GetMessage($message, $contentparameters);
684
	}
685
686
	/**
687
	 * Returns the waste basket.
688
	 *
689
	 * @return string
690
	 */
691
	public function GetWasteBasket() {
692
		if ($this->wastebasket) {
693
			return $this->wastebasket;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->wastebasket returns the type true which is incompatible with the documented return type string.
Loading history...
694
		}
695
696
		$storeprops = mapi_getprops($this->defaultstore, [PR_IPM_WASTEBASKET_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

696
		$storeprops = /** @scrutinizer ignore-call */ mapi_getprops($this->defaultstore, [PR_IPM_WASTEBASKET_ENTRYID]);
Loading history...
697
		if (isset($storeprops[PR_IPM_WASTEBASKET_ENTRYID])) {
698
			$wastebasket = mapi_msgstore_openentry($this->defaultstore, $storeprops[PR_IPM_WASTEBASKET_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

698
			$wastebasket = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->defaultstore, $storeprops[PR_IPM_WASTEBASKET_ENTRYID]);
Loading history...
699
			$wastebasketprops = mapi_getprops($wastebasket, [PR_SOURCE_KEY]);
700
			if (isset($wastebasketprops[PR_SOURCE_KEY])) {
701
				$this->wastebasket = bin2hex($wastebasketprops[PR_SOURCE_KEY]);
702
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetWasteBasket(): Got waste basket with id '%s'", $this->wastebasket));
703
704
				return $this->wastebasket;
705
			}
706
		}
707
708
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
709
	}
710
711
	/**
712
	 * Returns the content of the named attachment as stream.
713
	 *
714
	 * @param string $attname
715
	 *
716
	 * @throws StatusException
717
	 *
718
	 * @return SyncItemOperationsAttachment
719
	 */
720
	public function GetAttachmentData($attname) {
721
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetAttachmentData('%s')", $attname));
722
723
		if (!strpos($attname, ":")) {
724
			throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, attachment requested for non-existing item", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
725
		}
726
727
		list($id, $attachnum, $parentEntryid) = explode(":", $attname);
728
		if (isset($parentEntryid)) {
729
			$this->Setup(GSync::GetAdditionalSyncFolderStore($parentEntryid));
730
		}
731
732
		$entryid = hex2bin($id);
733
		$message = mapi_msgstore_openentry($this->store, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

733
		$message = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $entryid);
Loading history...
734
		if (!$message) {
735
			throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, unable to open item for attachment data for id '%s' with: 0x%X", $attname, $id, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

735
			throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, unable to open item for attachment data for id '%s' with: 0x%X", $attname, $id, /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
Loading history...
736
		}
737
738
		MAPIUtils::ParseSmime($this->session, $this->defaultstore, $this->getAddressbook(), $message);
0 ignored issues
show
Bug introduced by
$this->session of type boolean is incompatible with the type MAPISession expected by parameter $session of MAPIUtils::ParseSmime(). ( Ignorable by Annotation )

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

738
		MAPIUtils::ParseSmime(/** @scrutinizer ignore-type */ $this->session, $this->defaultstore, $this->getAddressbook(), $message);
Loading history...
739
		$attach = mapi_message_openattach($message, $attachnum);
1 ignored issue
show
Bug introduced by
The function mapi_message_openattach 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

739
		$attach = /** @scrutinizer ignore-call */ mapi_message_openattach($message, $attachnum);
Loading history...
740
		if (!$attach) {
741
			throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, unable to open attachment number '%s' with: 0x%X", $attname, $attachnum, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
742
		}
743
744
		// get necessary attachment props
745
		$attprops = mapi_getprops($attach, [PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W, PR_ATTACH_METHOD]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

745
		$attprops = /** @scrutinizer ignore-call */ mapi_getprops($attach, [PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W, PR_ATTACH_METHOD]);
Loading history...
746
		$attachment = new SyncItemOperationsAttachment();
747
		// check if it's an embedded message and open it in such a case
748
		if (isset($attprops[PR_ATTACH_METHOD]) && $attprops[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG) {
749
			$embMessage = mapi_attach_openobj($attach);
1 ignored issue
show
Bug introduced by
The function mapi_attach_openobj 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

749
			$embMessage = /** @scrutinizer ignore-call */ mapi_attach_openobj($attach);
Loading history...
750
			$addrbook = $this->getAddressbook();
751
			$stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $embMessage, ['use_tnef' => -1]);
1 ignored issue
show
Bug introduced by
The function mapi_inetmapi_imtoinet 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

751
			$stream = /** @scrutinizer ignore-call */ mapi_inetmapi_imtoinet($this->session, $addrbook, $embMessage, ['use_tnef' => -1]);
Loading history...
752
			// set the default contenttype for this kind of messages
753
			$attachment->contenttype = "message/rfc822";
754
		}
755
		else {
756
			$stream = mapi_openproperty($attach, PR_ATTACH_DATA_BIN, IID_IStream, 0, 0);
1 ignored issue
show
Bug introduced by
The function mapi_openproperty 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
			$stream = /** @scrutinizer ignore-call */ mapi_openproperty($attach, PR_ATTACH_DATA_BIN, IID_IStream, 0, 0);
Loading history...
757
		}
758
759
		if (!$stream) {
760
			throw new StatusException(sprintf("Grommunio->GetAttachmentData('%s'): Error, unable to open attachment data stream: 0x%X", $attname, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
761
		}
762
763
		// put the mapi stream into a wrapper to get a standard stream
764
		$attachment->data = MAPIStreamWrapper::Open($stream);
765
		if (isset($attprops[PR_ATTACH_MIME_TAG])) {
766
			$attachment->contenttype = $attprops[PR_ATTACH_MIME_TAG];
767
		}
768
		elseif (isset($attprops[PR_ATTACH_MIME_TAG_W])) {
769
			$attachment->contenttype = $attprops[PR_ATTACH_MIME_TAG_W];
770
		}
771
		// TODO default contenttype
772
		return $attachment;
773
	}
774
775
	/**
776
	 * Deletes all contents of the specified folder.
777
	 * This is generally used to empty the trash (wastebasked), but could also be used on any
778
	 * other folder.
779
	 *
780
	 * @param string $folderid
781
	 * @param bool   $includeSubfolders (opt) also delete sub folders, default true
782
	 *
783
	 * @throws StatusException
784
	 *
785
	 * @return bool
786
	 */
787
	public function EmptyFolder($folderid, $includeSubfolders = true) {
788
		$folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey 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

788
		$folderentryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
Loading history...
789
		if (!$folderentryid) {
790
			throw new StatusException(sprintf("Grommunio->EmptyFolder('%s','%s'): Error, unable to open folder (no entry id)", $folderid, Utils::PrintAsString($includeSubfolders)), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR);
791
		}
792
		$folder = mapi_msgstore_openentry($this->store, $folderentryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

792
		$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $folderentryid);
Loading history...
793
794
		if (!$folder) {
795
			throw new StatusException(sprintf("Grommunio->EmptyFolder('%s','%s'): Error, unable to open parent folder (open entry)", $folderid, Utils::PrintAsString($includeSubfolders)), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR);
796
		}
797
798
		$flags = 0;
799
		if ($includeSubfolders) {
800
			$flags = DEL_ASSOCIATED;
801
		}
802
803
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->EmptyFolder('%s','%s'): emptying folder", $folderid, Utils::PrintAsString($includeSubfolders)));
804
805
		// empty folder!
806
		mapi_folder_emptyfolder($folder, $flags);
1 ignored issue
show
Bug introduced by
The function mapi_folder_emptyfolder 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

806
		/** @scrutinizer ignore-call */ 
807
  mapi_folder_emptyfolder($folder, $flags);
Loading history...
807
		if (mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

807
		if (/** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
808
			throw new StatusException(sprintf("Grommunio->EmptyFolder('%s','%s'): Error, mapi_folder_emptyfolder() failed: 0x%X", $folderid, Utils::PrintAsString($includeSubfolders), mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR);
809
		}
810
811
		return true;
812
	}
813
814
	/**
815
	 * Processes a response to a meeting request.
816
	 * CalendarID is a reference and has to be set if a new calendar item is created.
817
	 *
818
	 * @param string $requestid id of the object containing the request
819
	 * @param string $folderid  id of the parent folder of $requestid
820
	 * @param string $response
821
	 *
822
	 * @throws StatusException
823
	 *
824
	 * @return string id of the created/updated calendar obj
825
	 */
826
	public function MeetingResponse($requestid, $folderid, $response) {
827
		// Use standard meeting response code to process meeting request
828
		list($fid, $requestid) = Utils::SplitMessageId($requestid);
829
		$reqentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($requestid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey 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

829
		$reqentryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($requestid));
Loading history...
830
		if (!$reqentryid) {
831
			throw new StatusException(sprintf("Grommunio->MeetingResponse('%s', '%s', '%s'): Error, unable to entryid of the message 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

831
			throw new StatusException(sprintf("Grommunio->MeetingResponse('%s', '%s', '%s'): Error, unable to entryid of the message 0x%X", $requestid, $folderid, $response, /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
Loading history...
832
		}
833
834
		$mapimessage = mapi_msgstore_openentry($this->store, $reqentryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

834
		$mapimessage = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $reqentryid);
Loading history...
835
		if (!$mapimessage) {
836
			throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error, unable to open request message for response 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
837
		}
838
839
		// ios sends calendar item in MeetingResponse
840
		// @see https://jira.z-hub.io/browse/ZP-1524
841
		$folderClass = GSync::GetDeviceManager()->GetFolderClassFromCacheByID($fid);
842
		// find the corresponding meeting request
843
		if ($folderClass != 'Email') {
844
			$props = MAPIMapping::GetMeetingRequestProperties();
845
			$props = getPropIdsFromStrings($this->store, $props);
846
847
			$messageprops = mapi_getprops($mapimessage, [$props["goidtag"]]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

847
			$messageprops = /** @scrutinizer ignore-call */ mapi_getprops($mapimessage, [$props["goidtag"]]);
Loading history...
848
			$goid = $messageprops[$props["goidtag"]];
849
850
			$mapiprovider = new MAPIProvider($this->session, $this->store);
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type resource expected by parameter $store of MAPIProvider::__construct(). ( Ignorable by Annotation )

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

850
			$mapiprovider = new MAPIProvider($this->session, /** @scrutinizer ignore-type */ $this->store);
Loading history...
Bug introduced by
$this->session of type boolean is incompatible with the type resource expected by parameter $session of MAPIProvider::__construct(). ( Ignorable by Annotation )

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

850
			$mapiprovider = new MAPIProvider(/** @scrutinizer ignore-type */ $this->session, $this->store);
Loading history...
851
			$inboxprops = $mapiprovider->GetInboxProps();
852
			$folder = mapi_msgstore_openentry($this->store, $inboxprops[PR_ENTRYID]);
853
854
			// Find the item by restricting all items to the correct ID
855
			$restrict = [RES_AND, [
856
				[RES_PROPERTY,
857
					[
858
						RELOP => RELOP_EQ,
859
						ULPROPTAG => $props["goidtag"],
860
						VALUE => $goid,
861
					],
862
				],
863
			]];
864
865
			$inboxcontents = mapi_folder_getcontentstable($folder);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable 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

865
			$inboxcontents = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($folder);
Loading history...
866
867
			$rows = mapi_table_queryallrows($inboxcontents, [PR_ENTRYID], $restrict);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

867
			$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($inboxcontents, [PR_ENTRYID], $restrict);
Loading history...
868
			if (empty($rows)) {
869
				throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error, meeting request not found in the inbox", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
870
			}
871
			SLog::Write(LOGLEVEL_DEBUG, "Grommunio->MeetingResponse found meeting request in the inbox");
872
			$mapimessage = mapi_msgstore_openentry($this->store, $rows[0][PR_ENTRYID]);
873
			$reqentryid = $rows[0][PR_ENTRYID];
874
		}
875
876
		$meetingrequest = new Meetingrequest($this->store, $mapimessage, $this->session);
877
878
		if (!$meetingrequest->isMeetingRequest()) {
879
			throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error, attempt to respond to non-meeting request", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
880
		}
881
882
		if ($meetingrequest->isLocalOrganiser()) {
883
			throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error, attempt to response to meeting request that we organized", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
884
		}
885
886
		// Process the meeting response. We don't have to send the actual meeting response
887
		// e-mail, because the device will send it itself. This seems not to be the case
888
		// anymore for the ios devices since at least version 12.4. grommunio-sync will send the
889
		// accepted email in such a case.
890
		// @see https://jira.z-hub.io/browse/ZP-1524
891
		$sendresponse = false;
892
		$deviceType = strtolower(Request::GetDeviceType());
0 ignored issues
show
Bug introduced by
It seems like Request::GetDeviceType() can also be of type false; however, parameter $string of strtolower() 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

892
		$deviceType = strtolower(/** @scrutinizer ignore-type */ Request::GetDeviceType());
Loading history...
893
		if ($deviceType == 'iphone' || $deviceType == 'ipad' || $deviceType == 'ipod') {
894
			$matches = [];
895
			if (preg_match("/^Apple-.*?\\/(\\d{4})\\./", Request::GetUserAgent(), $matches) && isset($matches[1]) && $matches[1] >= 1607) {
896
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->MeetingResponse: iOS device %s->%s", Request::GetDeviceType(), Request::GetUserAgent()));
0 ignored issues
show
Bug introduced by
It seems like Request::GetDeviceType() can also be of type false; however, parameter $values of sprintf() does only seem to accept double|integer|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

896
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->MeetingResponse: iOS device %s->%s", /** @scrutinizer ignore-type */ Request::GetDeviceType(), Request::GetUserAgent()));
Loading history...
897
				$sendresponse = true;
898
			}
899
		}
900
901
		switch ($response) {
902
			case 1:     // accept
903
			default:
904
				$entryid = $meetingrequest->doAccept(false, $sendresponse, false, false, false, false, true); // last true is the $userAction
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $newProposedEndTime of Meetingrequest::doAccept(). ( Ignorable by Annotation )

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

904
				$entryid = $meetingrequest->doAccept(false, $sendresponse, false, false, /** @scrutinizer ignore-type */ false, false, true); // last true is the $userAction
Loading history...
Bug introduced by
false of type false is incompatible with the type string expected by parameter $newProposedStartTime of Meetingrequest::doAccept(). ( Ignorable by Annotation )

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

904
				$entryid = $meetingrequest->doAccept(false, $sendresponse, false, /** @scrutinizer ignore-type */ false, false, false, true); // last true is the $userAction
Loading history...
905
				break;
906
907
			case 2:        // tentative
908
				$entryid = $meetingrequest->doAccept(true, $sendresponse, false, false, false, false, true); // last true is the $userAction
909
				break;
910
911
			case 3:        // decline
912
				$meetingrequest->doDecline(false);
913
				break;
914
		}
915
916
		// F/B will be updated on logoff
917
918
		// We have to return the ID of the new calendar item, so do that here
919
		$calendarid = "";
920
		$calFolderId = "";
921
		if (isset($entryid)) {
922
			$newitem = mapi_msgstore_openentry($this->store, $entryid);
923
			// new item might be in a delegator's store. ActiveSync does not support accepting them.
924
			if (!$newitem) {
925
				throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Object with entryid '%s' was not found in user's store (0x%X). It might be in a delegator's store.", $requestid, $folderid, $response, bin2hex($entryid), mapi_last_hresult()), SYNC_MEETRESPSTATUS_SERVERERROR, null, LOGLEVEL_WARN);
926
			}
927
928
			$newprops = mapi_getprops($newitem, [PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY]);
929
			$calendarid = bin2hex($newprops[PR_SOURCE_KEY]);
930
			$calFolderId = bin2hex($newprops[PR_PARENT_SOURCE_KEY]);
931
		}
932
933
		// on recurring items, the MeetingRequest class responds with a wrong entryid
934
		if ($requestid == $calendarid) {
935
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): returned calendar id is the same as the requestid - re-searching", $requestid, $folderid, $response));
936
937
			if (empty($props)) {
938
				$props = MAPIMapping::GetMeetingRequestProperties();
939
				$props = getPropIdsFromStrings($this->store, $props);
940
941
				$messageprops = mapi_getprops($mapimessage, [$props["goidtag"]]);
942
				$goid = $messageprops[$props["goidtag"]];
943
			}
944
945
			$items = $meetingrequest->findCalendarItems($goid);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $goid does not seem to be defined for all execution paths leading up to this point.
Loading history...
946
947
			if (is_array($items)) {
948
				$newitem = mapi_msgstore_openentry($this->store, $items[0]);
949
				$newprops = mapi_getprops($newitem, [PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY]);
950
				$calendarid = bin2hex($newprops[PR_SOURCE_KEY]);
951
				$calFolderId = bin2hex($newprops[PR_PARENT_SOURCE_KEY]);
952
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): found other calendar entryid", $requestid, $folderid, $response));
953
			}
954
955
			if ($requestid == $calendarid) {
956
				throw new StatusException(sprintf("Grommunio->MeetingResponse('%s','%s', '%s'): Error finding the accepted meeting response in the calendar", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ);
957
			}
958
		}
959
960
		// delete meeting request from Inbox
961
		if ($folderClass == 'Email') {
962
			$folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
963
			$folder = mapi_msgstore_openentry($this->store, $folderentryid);
964
		}
965
		mapi_folder_deletemessages($folder, [$reqentryid], 0);
1 ignored issue
show
Bug introduced by
The function mapi_folder_deletemessages 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

965
		/** @scrutinizer ignore-call */ 
966
  mapi_folder_deletemessages($folder, [$reqentryid], 0);
Loading history...
Comprehensibility Best Practice introduced by
The variable $folder does not seem to be defined for all execution paths leading up to this point.
Loading history...
966
967
		$prefix = '';
968
		// prepend the short folderid of the target calendar: if available and short ids are used
969
		if ($calFolderId) {
970
			$shortFolderId = GSync::GetDeviceManager()->GetFolderIdForBackendId($calFolderId);
971
			if ($calFolderId != $shortFolderId) {
972
				$prefix = $shortFolderId . ':';
973
			}
974
		}
975
976
		return $prefix . $calendarid;
977
	}
978
979
	/**
980
	 * Indicates if the backend has a ChangesSink.
981
	 * A sink is an active notification mechanism which does not need polling.
982
	 * Since Zarafa 7.0.5 such a sink is available.
983
	 * The grommunio backend uses this method to initialize the sink with mapi.
984
	 *
985
	 * @return bool
986
	 */
987
	public function HasChangesSink() {
988
		if (!$this->notifications) {
989
			SLog::Write(LOGLEVEL_DEBUG, "Grommunio->HasChangesSink(): sink is not available");
990
991
			return false;
992
		}
993
994
		$this->changesSink = @mapi_sink_create();
1 ignored issue
show
Bug introduced by
The function mapi_sink_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

994
		$this->changesSink = @/** @scrutinizer ignore-call */ mapi_sink_create();
Loading history...
995
996
		if (!$this->changesSink || mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

996
		if (!$this->changesSink || /** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
997
			SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->HasChangesSink(): sink could not be created with  0x%X", mapi_last_hresult()));
998
999
			return false;
1000
		}
1001
1002
		$this->changesSinkHierarchyHash = $this->getHierarchyHash();
1003
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->HasChangesSink(): created - HierarchyHash: %s", $this->changesSinkHierarchyHash));
1004
1005
		// advise the main store and also to check if the connection supports it
1006
		return $this->adviseStoreToSink($this->defaultstore);
1007
	}
1008
1009
	/**
1010
	 * The folder should be considered by the sink.
1011
	 * Folders which were not initialized should not result in a notification
1012
	 * of IBackend->ChangesSink().
1013
	 *
1014
	 * @param string $folderid
1015
	 *
1016
	 * @return bool false if entryid can not be found for that folder
1017
	 */
1018
	public function ChangesSinkInitialize($folderid) {
1019
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->ChangesSinkInitialize(): folderid '%s'", $folderid));
1020
1021
		$entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey 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

1021
		$entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
Loading history...
1022
		if (!$entryid) {
1023
			return false;
1024
		}
1025
1026
		// add entryid to the monitored folders
1027
		$this->changesSinkFolders[$entryid] = $folderid;
1028
1029
		// advise the current store to the sink
1030
		return $this->adviseStoreToSink($this->store);
0 ignored issues
show
Bug introduced by
$this->store of type boolean is incompatible with the type mapistore expected by parameter $store of Grommunio::adviseStoreToSink(). ( Ignorable by Annotation )

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

1030
		return $this->adviseStoreToSink(/** @scrutinizer ignore-type */ $this->store);
Loading history...
1031
	}
1032
1033
	/**
1034
	 * The actual ChangesSink.
1035
	 * For max. the $timeout value this method should block and if no changes
1036
	 * are available return an empty array.
1037
	 * If changes are available a list of folderids is expected.
1038
	 *
1039
	 * @param int $timeout max. amount of seconds to block
1040
	 *
1041
	 * @return array
1042
	 */
1043
	public function ChangesSink($timeout = 30) {
1044
		// clear the folder stats cache
1045
		unset($this->folderStatCache);
1046
1047
		$notifications = [];
1048
		$hierarchyNotifications = [];
1049
		$sinkresult = @mapi_sink_timedwait($this->changesSink, $timeout * 1000);
1 ignored issue
show
Bug introduced by
The function mapi_sink_timedwait 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

1049
		$sinkresult = @/** @scrutinizer ignore-call */ mapi_sink_timedwait($this->changesSink, $timeout * 1000);
Loading history...
1050
1051
		if (!is_array($sinkresult)) {
1052
			throw new StatusException("Grommunio->ChangesSink(): Sink returned invalid notification, aborting", SyncCollections::OBSOLETE_CONNECTION);
1053
		}
1054
1055
		// reverse array so that the changes on folders are before changes on messages and
1056
		// it's possible to filter such notifications
1057
		$sinkresult = array_reverse($sinkresult, true);
1058
		foreach ($sinkresult as $sinknotif) {
1059
			// add a notification on a folder
1060
			if ($sinknotif['objtype'] == MAPI_FOLDER) {
1061
				$hierarchyNotifications[$sinknotif['entryid']] = IBackend::HIERARCHYNOTIFICATION;
1062
			}
1063
			// change on a message, remove hierarchy notification
1064
			if (isset($sinknotif['parentid']) && $sinknotif['objtype'] == MAPI_MESSAGE && isset($notifications[$sinknotif['parentid']])) {
1065
				unset($hierarchyNotifications[$sinknotif['parentid']]);
1066
			}
1067
1068
			// TODO check if adding $sinknotif['objtype'] = MAPI_MESSAGE wouldn't break anything
1069
			// check if something in the monitored folders changed
1070
			if (isset($sinknotif['parentid']) && array_key_exists($sinknotif['parentid'], $this->changesSinkFolders)) {
1071
				$notifications[] = $this->changesSinkFolders[$sinknotif['parentid']];
1072
			}
1073
			// deletes and moves
1074
			if (isset($sinknotif['oldparentid']) && array_key_exists($sinknotif['oldparentid'], $this->changesSinkFolders)) {
1075
				$notifications[] = $this->changesSinkFolders[$sinknotif['oldparentid']];
1076
			}
1077
		}
1078
1079
		// validate hierarchy notifications by comparing the hierarchy hashes (too many false positives otherwise)
1080
		if (!empty($hierarchyNotifications)) {
1081
			$hash = $this->getHierarchyHash();
1082
			if ($hash !== $this->changesSinkHierarchyHash) {
1083
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->ChangesSink() Hierarchy notification, pending validation. New hierarchyHash: %s", $hash));
1084
				$notifications[] = IBackend::HIERARCHYNOTIFICATION;
1085
				$this->changesSinkHierarchyHash = $hash;
1086
			}
1087
		}
1088
1089
		return $notifications;
1090
	}
1091
1092
	/**
1093
	 * Applies settings to and gets information from the device.
1094
	 *
1095
	 * @param SyncObject $settings (SyncOOF, SyncUserInformation, SyncRightsManagementTemplates possible)
1096
	 *
1097
	 * @return SyncObject $settings
1098
	 */
1099
	public function Settings($settings) {
1100
		if ($settings instanceof SyncOOF) {
1101
			$this->settingsOOF($settings);
1102
		}
1103
1104
		if ($settings instanceof SyncUserInformation) {
1105
			$this->settingsUserInformation($settings);
1106
		}
1107
1108
		if ($settings instanceof SyncRightsManagementTemplates) {
1109
			$this->settingsRightsManagementTemplates($settings);
1110
		}
1111
1112
		return $settings;
1113
	}
1114
1115
	/**
1116
	 * Resolves recipients.
1117
	 *
1118
	 * @param SyncObject $resolveRecipients
1119
	 *
1120
	 * @return SyncObject $resolveRecipients
1121
	 */
1122
	public function ResolveRecipients($resolveRecipients) {
1123
		if ($resolveRecipients instanceof SyncResolveRecipients) {
1124
			$resolveRecipients->status = SYNC_RESOLVERECIPSSTATUS_SUCCESS;
1125
			$resolveRecipients->response = [];
1126
			$resolveRecipientsOptions = new SyncResolveRecipientsOptions();
1127
			$maxAmbiguousRecipients = self::MAXAMBIGUOUSRECIPIENTS;
1128
1129
			if (isset($resolveRecipients->options)) {
1130
				$resolveRecipientsOptions = $resolveRecipients->options;
1131
				// only limit ambiguous recipients if the client requests it.
1132
1133
				if (isset($resolveRecipientsOptions->maxambiguousrecipients) &&
1134
						$resolveRecipientsOptions->maxambiguousrecipients >= 0 &&
1135
						$resolveRecipientsOptions->maxambiguousrecipients <= self::MAXAMBIGUOUSRECIPIENTS) {
1136
					$maxAmbiguousRecipients = $resolveRecipientsOptions->maxambiguousrecipients;
1137
					SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->ResolveRecipients(): The client requested %d max ambiguous recipients to resolve.", $maxAmbiguousRecipients));
1138
				}
1139
			}
1140
1141
			foreach ($resolveRecipients->to as $i => $to) {
1142
				$response = new SyncResolveRecipientsResponse();
1143
				$response->to = $to;
1144
				$response->status = SYNC_RESOLVERECIPSSTATUS_SUCCESS;
1145
1146
				// do not expand distlists here
1147
				$recipient = $this->resolveRecipient($to, $maxAmbiguousRecipients, false);
1148
				if (is_array($recipient) && !empty($recipient)) {
1149
					$response->recipientcount = 0;
1150
					foreach ($recipient as $entry) {
1151
						if ($entry instanceof SyncResolveRecipient) {
1152
							// certificates are already set. Unset them if they weren't required.
1153
							if (!isset($resolveRecipientsOptions->certificateretrieval)) {
1154
								unset($entry->certificates);
1155
							}
1156
							if (isset($resolveRecipientsOptions->availability)) {
1157
								if (!isset($resolveRecipientsOptions->starttime)) {
1158
									// TODO error, the request must include a valid StartTime element value
1159
								}
1160
								$entry->availability = $this->getAvailability($to, $entry, $resolveRecipientsOptions);
1161
							}
1162
							if (isset($resolveRecipientsOptions->picture)) {
1163
								// TODO implement picture retrieval of the recipient
1164
							}
1165
							++$response->recipientcount;
1166
							$response->recipient[] = $entry;
1167
						}
1168
						elseif (is_int($recipient)) {
1169
							$response->status = $recipient;
1170
						}
1171
					}
1172
				}
1173
1174
				$resolveRecipients->response[$i] = $response;
1175
			}
1176
1177
			return $resolveRecipients;
1178
		}
1179
1180
		SLog::Write(LOGLEVEL_WARN, "Grommunio->ResolveRecipients(): Not a valid SyncResolveRecipients object.");
1181
		// return a SyncResolveRecipients object so that sync doesn't fail
1182
		$r = new SyncResolveRecipients();
1183
		$r->status = SYNC_RESOLVERECIPSSTATUS_PROTOCOLERROR;
1184
1185
		return $r;
1186
	}
1187
1188
	/*----------------------------------------------------------------------------------------------------------
1189
	 * Implementation of the ISearchProvider interface
1190
	 */
1191
1192
	/**
1193
	 * Indicates if a search type is supported by this SearchProvider
1194
	 * Currently only the type ISearchProvider::SEARCH_GAL (Global Address List) is implemented.
1195
	 *
1196
	 * @param string $searchtype
1197
	 *
1198
	 * @return bool
1199
	 */
1200
	public function SupportsType($searchtype) {
1201
		return ($searchtype == ISearchProvider::SEARCH_GAL) || ($searchtype == ISearchProvider::SEARCH_MAILBOX);
1202
	}
1203
1204
	/**
1205
	 * Searches the GAB of Grommunio
1206
	 * Can be overwritten globally by configuring a SearchBackend.
1207
	 *
1208
	 * @param string                       $searchquery   string to be searched for
1209
	 * @param string                       $searchrange   specified searchrange
1210
	 * @param SyncResolveRecipientsPicture $searchpicture limitations for picture
1211
	 *
1212
	 * @throws StatusException
1213
	 *
1214
	 * @return array search results
1215
	 */
1216
	public function GetGALSearchResults($searchquery, $searchrange, $searchpicture) {
1217
		// only return users whose displayName or the username starts with $name
1218
		// TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and PR_ACCOUNT
1219
		$addrbook = $this->getAddressbook();
1220
		// FIXME: create a function to get the adressbook contentstable
1221
		if ($addrbook) {
0 ignored issues
show
introduced by
$addrbook is of type MAPIAddressbook, thus it always evaluated to true.
Loading history...
1222
			$ab_entryid = mapi_ab_getdefaultdir($addrbook);
0 ignored issues
show
Bug introduced by
The function mapi_ab_getdefaultdir 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

1222
			$ab_entryid = /** @scrutinizer ignore-call */ mapi_ab_getdefaultdir($addrbook);
Loading history...
1223
		}
1224
		if ($ab_entryid) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ab_entryid does not seem to be defined for all execution paths leading up to this point.
Loading history...
1225
			$ab_dir = mapi_ab_openentry($addrbook, $ab_entryid);
1 ignored issue
show
Bug introduced by
The function mapi_ab_openentry 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

1225
			$ab_dir = /** @scrutinizer ignore-call */ mapi_ab_openentry($addrbook, $ab_entryid);
Loading history...
1226
		}
1227
		if ($ab_dir) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ab_dir does not seem to be defined for all execution paths leading up to this point.
Loading history...
1228
			$table = mapi_folder_getcontentstable($ab_dir);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable 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

1228
			$table = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($ab_dir);
Loading history...
1229
		}
1230
1231
		if (!$table) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $table does not seem to be defined for all execution paths leading up to this point.
Loading history...
1232
			throw new StatusException(sprintf("Grommunio->GetGALSearchResults(): could not open addressbook: 0x%X", mapi_last_hresult()), SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED);
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

1232
			throw new StatusException(sprintf("Grommunio->GetGALSearchResults(): could not open addressbook: 0x%X", /** @scrutinizer ignore-call */ mapi_last_hresult()), SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED);
Loading history...
1233
		}
1234
1235
		$restriction = MAPIUtils::GetSearchRestriction(u2w($searchquery));
0 ignored issues
show
Bug introduced by
It seems like u2w($searchquery) can also be of type false; however, parameter $query of MAPIUtils::GetSearchRestriction() 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

1235
		$restriction = MAPIUtils::GetSearchRestriction(/** @scrutinizer ignore-type */ u2w($searchquery));
Loading history...
1236
		mapi_table_restrict($table, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict 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

1236
		/** @scrutinizer ignore-call */ 
1237
  mapi_table_restrict($table, $restriction);
Loading history...
1237
		mapi_table_sort($table, [PR_DISPLAY_NAME => TABLE_SORT_ASCEND]);
0 ignored issues
show
Bug introduced by
The function mapi_table_sort 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

1237
		/** @scrutinizer ignore-call */ 
1238
  mapi_table_sort($table, [PR_DISPLAY_NAME => TABLE_SORT_ASCEND]);
Loading history...
1238
1239
		if (mapi_last_hresult()) {
1240
			throw new StatusException(sprintf("Grommunio->GetGALSearchResults(): could not apply restriction: 0x%X", mapi_last_hresult()), SYNC_SEARCHSTATUS_STORE_TOOCOMPLEX);
1241
		}
1242
1243
		// range for the search results, default symbian range end is 50, wm 99,
1244
		// so we'll use that of nokia
1245
		$rangestart = 0;
1246
		$rangeend = 50;
1247
1248
		if ($searchrange != '0') {
1249
			$pos = strpos($searchrange, '-');
1250
			$rangestart = substr($searchrange, 0, $pos);
1251
			$rangeend = substr($searchrange, ($pos + 1));
1252
		}
1253
		$items = [];
1254
1255
		$querycnt = mapi_table_getrowcount($table);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount 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

1255
		$querycnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($table);
Loading history...
1256
		// do not return more results as requested in range
1257
		$querylimit = (($rangeend + 1) < $querycnt) ? ($rangeend + 1) : $querycnt;
1258
1259
		if ($querycnt > 0) {
1260
			$abentries = mapi_table_queryrows($table, [PR_ENTRYID, PR_ACCOUNT, PR_DISPLAY_NAME, PR_SMTP_ADDRESS, PR_BUSINESS_TELEPHONE_NUMBER, PR_GIVEN_NAME, PR_SURNAME, PR_MOBILE_TELEPHONE_NUMBER, PR_HOME_TELEPHONE_NUMBER, PR_TITLE, PR_COMPANY_NAME, PR_OFFICE_LOCATION, PR_EMS_AB_THUMBNAIL_PHOTO], $rangestart, $querylimit);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows 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

1260
			$abentries = /** @scrutinizer ignore-call */ mapi_table_queryrows($table, [PR_ENTRYID, PR_ACCOUNT, PR_DISPLAY_NAME, PR_SMTP_ADDRESS, PR_BUSINESS_TELEPHONE_NUMBER, PR_GIVEN_NAME, PR_SURNAME, PR_MOBILE_TELEPHONE_NUMBER, PR_HOME_TELEPHONE_NUMBER, PR_TITLE, PR_COMPANY_NAME, PR_OFFICE_LOCATION, PR_EMS_AB_THUMBNAIL_PHOTO], $rangestart, $querylimit);
Loading history...
1261
		}
1262
1263
		for ($i = 0; $i < $querylimit; ++$i) {
1264
			if (!isset($abentries[$i][PR_SMTP_ADDRESS])) {
1265
				SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->GetGALSearchResults(): The GAL entry '%s' does not have an email address and will be ignored.", w2u($abentries[$i][PR_DISPLAY_NAME])));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $abentries does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug introduced by
It seems like w2u($abentries[$i][PR_DISPLAY_NAME]) can also be of type false; however, parameter $values of sprintf() does only seem to accept double|integer|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

1265
				SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->GetGALSearchResults(): The GAL entry '%s' does not have an email address and will be ignored.", /** @scrutinizer ignore-type */ w2u($abentries[$i][PR_DISPLAY_NAME])));
Loading history...
1266
1267
				continue;
1268
			}
1269
1270
			$items[$i][SYNC_GAL_DISPLAYNAME] = w2u($abentries[$i][PR_DISPLAY_NAME]);
1271
1272
			if (strlen(trim($items[$i][SYNC_GAL_DISPLAYNAME])) == 0) {
1273
				$items[$i][SYNC_GAL_DISPLAYNAME] = w2u($abentries[$i][PR_ACCOUNT]);
1274
			}
1275
1276
			$items[$i][SYNC_GAL_ALIAS] = w2u($abentries[$i][PR_ACCOUNT]);
1277
			// it's not possible not get first and last name of an user
1278
			// from the gab and user functions, so we just set lastname
1279
			// to displayname and leave firstname unset
1280
			// this was changed in Zarafa 6.40, so we try to get first and
1281
			// last name and fall back to the old behaviour if these values are not set
1282
			if (isset($abentries[$i][PR_GIVEN_NAME])) {
1283
				$items[$i][SYNC_GAL_FIRSTNAME] = w2u($abentries[$i][PR_GIVEN_NAME]);
1284
			}
1285
			if (isset($abentries[$i][PR_SURNAME])) {
1286
				$items[$i][SYNC_GAL_LASTNAME] = w2u($abentries[$i][PR_SURNAME]);
1287
			}
1288
1289
			if (!isset($items[$i][SYNC_GAL_LASTNAME])) {
1290
				$items[$i][SYNC_GAL_LASTNAME] = $items[$i][SYNC_GAL_DISPLAYNAME];
1291
			}
1292
1293
			$items[$i][SYNC_GAL_EMAILADDRESS] = w2u($abentries[$i][PR_SMTP_ADDRESS]);
1294
			// check if an user has an office number or it might produce warnings in the log
1295
			if (isset($abentries[$i][PR_BUSINESS_TELEPHONE_NUMBER])) {
1296
				$items[$i][SYNC_GAL_PHONE] = w2u($abentries[$i][PR_BUSINESS_TELEPHONE_NUMBER]);
1297
			}
1298
			// check if an user has a mobile number or it might produce warnings in the log
1299
			if (isset($abentries[$i][PR_MOBILE_TELEPHONE_NUMBER])) {
1300
				$items[$i][SYNC_GAL_MOBILEPHONE] = w2u($abentries[$i][PR_MOBILE_TELEPHONE_NUMBER]);
1301
			}
1302
			// check if an user has a home number or it might produce warnings in the log
1303
			if (isset($abentries[$i][PR_HOME_TELEPHONE_NUMBER])) {
1304
				$items[$i][SYNC_GAL_HOMEPHONE] = w2u($abentries[$i][PR_HOME_TELEPHONE_NUMBER]);
1305
			}
1306
1307
			if (isset($abentries[$i][PR_COMPANY_NAME])) {
1308
				$items[$i][SYNC_GAL_COMPANY] = w2u($abentries[$i][PR_COMPANY_NAME]);
1309
			}
1310
1311
			if (isset($abentries[$i][PR_TITLE])) {
1312
				$items[$i][SYNC_GAL_TITLE] = w2u($abentries[$i][PR_TITLE]);
1313
			}
1314
1315
			if (isset($abentries[$i][PR_OFFICE_LOCATION])) {
1316
				$items[$i][SYNC_GAL_OFFICE] = w2u($abentries[$i][PR_OFFICE_LOCATION]);
1317
			}
1318
1319
			if ($searchpicture !== false && isset($abentries[$i][PR_EMS_AB_THUMBNAIL_PHOTO])) {
1320
				$items[$i][SYNC_GAL_PICTURE] = StringStreamWrapper::Open($abentries[$i][PR_EMS_AB_THUMBNAIL_PHOTO]);
1321
			}
1322
		}
1323
		$nrResults = count($items);
1324
		$items['range'] = ($nrResults > 0) ? $rangestart . '-' . ($nrResults - 1) : '0-0';
1325
		$items['searchtotal'] = $nrResults;
1326
1327
		return $items;
1328
	}
1329
1330
	/**
1331
	 * Searches for the emails on the server.
1332
	 *
1333
	 * @param ContentParameter $cpo
1334
	 *
1335
	 * @return array
1336
	 */
1337
	public function GetMailboxSearchResults($cpo) {
1338
		$searchFolder = $this->getSearchFolder();
1339
		$searchRestriction = $this->getSearchRestriction($cpo);
1340
		$searchRange = explode('-', $cpo->GetSearchRange());
1341
		$searchFolderId = $cpo->GetSearchFolderid();
1342
		$searchFolders = [];
1343
		// search only in required folders
1344
		if (!empty($searchFolderId)) {
1345
			$searchFolderEntryId = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($searchFolderId));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey 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

1345
			$searchFolderEntryId = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($searchFolderId));
Loading history...
1346
			$searchFolders[] = $searchFolderEntryId;
1347
		}
1348
		// if no folder was required then search in the entire store
1349
		else {
1350
			$tmp = mapi_getprops($this->store, [PR_ENTRYID, PR_DISPLAY_NAME, PR_IPM_SUBTREE_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

1350
			$tmp = /** @scrutinizer ignore-call */ mapi_getprops($this->store, [PR_ENTRYID, PR_DISPLAY_NAME, PR_IPM_SUBTREE_ENTRYID]);
Loading history...
1351
			$searchFolders[] = $tmp[PR_IPM_SUBTREE_ENTRYID];
1352
		}
1353
		$items = [];
1354
		$flags = 0;
1355
		// if subfolders are required, do a recursive search
1356
		if ($cpo->GetSearchDeepTraversal()) {
1357
			$flags |= SEARCH_RECURSIVE;
1358
		}
1359
1360
		mapi_folder_setsearchcriteria($searchFolder, $searchRestriction, $searchFolders, $flags);
0 ignored issues
show
Bug introduced by
The function mapi_folder_setsearchcriteria 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

1360
		/** @scrutinizer ignore-call */ 
1361
  mapi_folder_setsearchcriteria($searchFolder, $searchRestriction, $searchFolders, $flags);
Loading history...
1361
1362
		$table = mapi_folder_getcontentstable($searchFolder);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable 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

1362
		$table = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($searchFolder);
Loading history...
1363
		$searchStart = time();
1364
		// do the search and wait for all the results available
1365
		while (time() - $searchStart < SEARCH_WAIT) {
1366
			$searchcriteria = mapi_folder_getsearchcriteria($searchFolder);
0 ignored issues
show
Bug introduced by
The function mapi_folder_getsearchcriteria 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

1366
			$searchcriteria = /** @scrutinizer ignore-call */ mapi_folder_getsearchcriteria($searchFolder);
Loading history...
1367
			if (($searchcriteria["searchstate"] & SEARCH_REBUILD) == 0) {
1368
				break;
1369
			} // Search is done
1370
			sleep(1);
1371
		}
1372
1373
		// if the search range is set limit the result to it, otherwise return all found messages
1374
		$rows = (is_array($searchRange) && isset($searchRange[0], $searchRange[1])) ?
1375
			mapi_table_queryrows($table, [PR_ENTRYID], $searchRange[0], $searchRange[1] - $searchRange[0] + 1) :
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows 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

1375
			/** @scrutinizer ignore-call */ 
1376
   mapi_table_queryrows($table, [PR_ENTRYID], $searchRange[0], $searchRange[1] - $searchRange[0] + 1) :
Loading history...
1376
			mapi_table_queryrows($table, [PR_ENTRYID], 0, SEARCH_MAXRESULTS);
1377
1378
		$cnt = count($rows);
1379
		$items['searchtotal'] = $cnt;
1380
		$items["range"] = $cpo->GetSearchRange();
1381
		for ($i = 0; $i < $cnt; ++$i) {
1382
			$items[$i]['class'] = 'Email';
1383
			$items[$i]['longid'] = bin2hex($rows[$i][PR_ENTRYID]);
1384
			// $items[$i]['folderid'] = bin2hex($rows[$i][PR_PARENT_SOURCE_KEY]);
1385
		}
1386
1387
		return $items;
1388
	}
1389
1390
	/**
1391
	 * Terminates a search for a given PID.
1392
	 *
1393
	 * @param int $pid
1394
	 *
1395
	 * @return bool
1396
	 */
1397
	public function TerminateSearch($pid) {
1398
		SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->TerminateSearch(): terminating search for pid %d", $pid));
1399
		if (!isset($this->store) || $this->store === false) {
1400
			SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->TerminateSearch(): The store is not available. It is not possible to remove search folder with pid %d", $pid));
1401
1402
			return false;
1403
		}
1404
1405
		$storeProps = mapi_getprops($this->store, [PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

1405
		$storeProps = /** @scrutinizer ignore-call */ mapi_getprops($this->store, [PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID]);
Loading history...
1406
		if (($storeProps[PR_STORE_SUPPORT_MASK] & STORE_SEARCH_OK) != STORE_SEARCH_OK) {
1407
			SLog::Write(LOGLEVEL_WARN, "Grommunio->TerminateSearch(): Store doesn't support search folders. Public store doesn't have FINDER_ROOT folder");
1408
1409
			return false;
1410
		}
1411
1412
		$finderfolder = mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

1412
		$finderfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]);
Loading history...
1413
		if (mapi_last_hresult() != NOERROR) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

1413
		if (/** @scrutinizer ignore-call */ mapi_last_hresult() != NOERROR) {
Loading history...
1414
			SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->TerminateSearch(): Unable to open search folder (0x%X)", mapi_last_hresult()));
1415
1416
			return false;
1417
		}
1418
1419
		$hierarchytable = mapi_folder_gethierarchytable($finderfolder);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable 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

1419
		$hierarchytable = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($finderfolder);
Loading history...
1420
		mapi_table_restrict(
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict 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

1420
		/** @scrutinizer ignore-call */ 
1421
  mapi_table_restrict(
Loading history...
1421
			$hierarchytable,
1422
			[RES_CONTENT,
1423
				[
1424
					FUZZYLEVEL => FL_PREFIX,
1425
					ULPROPTAG => PR_DISPLAY_NAME,
1426
					VALUE => [PR_DISPLAY_NAME => "grommunio-sync Search Folder " . $pid],
1427
				],
1428
			],
1429
			TBL_BATCH
1430
		);
1431
1432
		$folders = mapi_table_queryallrows($hierarchytable, [PR_ENTRYID, PR_DISPLAY_NAME, PR_LAST_MODIFICATION_TIME]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

1432
		$folders = /** @scrutinizer ignore-call */ mapi_table_queryallrows($hierarchytable, [PR_ENTRYID, PR_DISPLAY_NAME, PR_LAST_MODIFICATION_TIME]);
Loading history...
1433
		foreach ($folders as $folder) {
1434
			mapi_folder_deletefolder($finderfolder, $folder[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

1434
			/** @scrutinizer ignore-call */ 
1435
   mapi_folder_deletefolder($finderfolder, $folder[PR_ENTRYID]);
Loading history...
1435
		}
1436
1437
		return true;
1438
	}
1439
1440
	/**
1441
	 * Disconnects from the current search provider.
1442
	 *
1443
	 * @return bool
1444
	 */
1445
	public function Disconnect() {
1446
		return true;
1447
	}
1448
1449
	/**
1450
	 * Returns the MAPI store resource for a folderid
1451
	 * This is not part of IBackend but necessary for the ImportChangesICS->MoveMessage() operation if
1452
	 * the destination folder is not in the default store
1453
	 * Note: The current backend store might be changed as IBackend->Setup() is executed.
1454
	 *
1455
	 * @param string $store    target store, could contain a "domain\user" value - if empty default store is returned
1456
	 * @param string $folderid
1457
	 *
1458
	 * @return Resource/boolean
0 ignored issues
show
Documentation Bug introduced by
The doc comment Resource/boolean at position 0 could not be parsed: Unknown type name 'Resource/boolean' at position 0 in Resource/boolean.
Loading history...
1459
	 */
1460
	public function GetMAPIStoreForFolderId($store, $folderid) {
1461
		if ($store == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $store of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
1462
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetMAPIStoreForFolderId('%s', '%s'): no store specified, returning default store", $store, $folderid));
1463
1464
			return $this->defaultstore;
1465
		}
1466
1467
		// setup the correct store
1468
		if ($this->Setup($store, false, $folderid)) {
1469
			return $this->store;
1470
		}
1471
1472
		SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->GetMAPIStoreForFolderId('%s', '%s'): store is not available", $store, $folderid));
1473
1474
		return false;
1475
	}
1476
1477
	/**
1478
	 * Returns the email address and the display name of the user. Used by autodiscover.
1479
	 *
1480
	 * @param string $username The username
1481
	 *
1482
	 * @return array
1483
	 */
1484
	public function GetUserDetails($username) {
1485
		SLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->GetUserDetails for '%s'.", $username));
1486
		$zarafauserinfo = @nsp_getuserinfo($username);
1 ignored issue
show
Bug introduced by
The function nsp_getuserinfo 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

1486
		$zarafauserinfo = @/** @scrutinizer ignore-call */ nsp_getuserinfo($username);
Loading history...
1487
		$userDetails['emailaddress'] = (isset($zarafauserinfo['primary_email']) && $zarafauserinfo['primary_email']) ? $zarafauserinfo['primary_email'] : false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$userDetails was never initialized. Although not strictly required by PHP, it is generally a good practice to add $userDetails = array(); before regardless.
Loading history...
1488
		$userDetails['fullname'] = (isset($zarafauserinfo['fullname']) && $zarafauserinfo['fullname']) ? $zarafauserinfo['fullname'] : false;
1489
1490
		return $userDetails;
1491
	}
1492
1493
	/**
1494
	 * Returns the username of the currently active user.
1495
	 *
1496
	 * @return string
1497
	 */
1498
	public function GetCurrentUsername() {
1499
		return $this->storeName;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->storeName returns the type boolean which is incompatible with the documented return type string.
Loading history...
1500
	}
1501
1502
	/**
1503
	 * Returns the impersonated user name.
1504
	 *
1505
	 * @return string or false if no user is impersonated
1506
	 */
1507
	public function GetImpersonatedUser() {
1508
		return $this->impersonateUser;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->impersonateUser returns the type boolean which is incompatible with the documented return type string.
Loading history...
1509
	}
1510
1511
	/**
1512
	 * Returns the authenticated user name.
1513
	 *
1514
	 * @return string
1515
	 */
1516
	public function GetMainUser() {
1517
		return $this->mainUser;
1518
	}
1519
1520
	/**
1521
	 * Indicates if the Backend supports folder statistics.
1522
	 *
1523
	 * @return bool
1524
	 */
1525
	public function HasFolderStats() {
1526
		return true;
1527
	}
1528
1529
	/**
1530
	 * Returns a status indication of the folder.
1531
	 * If there are changes in the folder, the returned value must change.
1532
	 * The returned values are compared with '===' to determine if a folder needs synchronization or not.
1533
	 *
1534
	 * @param string $store    the store where the folder resides
1535
	 * @param string $folderid the folder id
1536
	 *
1537
	 * @return string
1538
	 */
1539
	public function GetFolderStat($store, $folderid) {
1540
		list($user, $domain) = Utils::SplitDomainUser($store);
1541
		if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
1542
			$user = $this->mainUser;
1543
			if ($this->impersonateUser) {
1544
				$user = $this->impersonateUser;
1545
			}
1546
		}
1547
1548
		if (!isset($this->folderStatCache[$user])) {
1549
			$this->folderStatCache[$user] = [];
1550
		}
1551
1552
		// if there is nothing in the cache for a store, load the data for all folders of it
1553
		if (empty($this->folderStatCache[$user])) {
1554
			// get the store
1555
			$userstore = $this->openMessageStore($user);
1556
			$rootfolder = mapi_msgstore_openentry($userstore);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

1556
			$rootfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($userstore);
Loading history...
1557
			$hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable 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

1557
			$hierarchy = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
1558
			$rows = mapi_table_queryallrows($hierarchy, [PR_SOURCE_KEY, PR_LOCAL_COMMIT_TIME_MAX, PR_CONTENT_COUNT, PR_CONTENT_UNREAD, PR_DELETED_MSG_COUNT]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

1558
			$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($hierarchy, [PR_SOURCE_KEY, PR_LOCAL_COMMIT_TIME_MAX, PR_CONTENT_COUNT, PR_CONTENT_UNREAD, PR_DELETED_MSG_COUNT]);
Loading history...
1559
1560
			if (count($rows) == 0) {
1561
				SLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->GetFolderStat(): could not access folder statistics for user '%s'. Probably missing 'read' permissions on the root folder! Folders of this store will be synchronized ONCE per hour only!", $user));
1562
			}
1563
1564
			foreach ($rows as $folder) {
1565
				$commit_time = isset($folder[PR_LOCAL_COMMIT_TIME_MAX]) ? $folder[PR_LOCAL_COMMIT_TIME_MAX] : "0000000000";
1566
				$content_count = isset($folder[PR_CONTENT_COUNT]) ? $folder[PR_CONTENT_COUNT] : -1;
1567
				$content_unread = isset($folder[PR_CONTENT_UNREAD]) ? $folder[PR_CONTENT_UNREAD] : -1;
1568
				$content_deleted = isset($folder[PR_DELETED_MSG_COUNT]) ? $folder[PR_DELETED_MSG_COUNT] : -1;
1569
1570
				$this->folderStatCache[$user][bin2hex($folder[PR_SOURCE_KEY])] = $commit_time . "/" . $content_count . "/" . $content_unread . "/" . $content_deleted;
1571
			}
1572
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetFolderStat() fetched status information of %d folders for store '%s'", count($this->folderStatCache[$user]), $user));
1573
		}
1574
1575
		if (isset($this->folderStatCache[$user][$folderid])) {
1576
			return $this->folderStatCache[$user][$folderid];
1577
		}
1578
1579
		// a timestamp that changes once per hour is returned in case there is no data found for this folder. It will be synchronized only once per hour.
1580
		return gmdate("Y-m-d-H");
1581
	}
1582
1583
	/**
1584
	 * Returns information about the user's store:
1585
	 * number of folders, store size, full name, email address.
1586
	 *
1587
	 * @return UserStoreInfo
1588
	 */
1589
	public function GetUserStoreInfo() {
1590
		$userStoreInfo = new UserStoreInfo();
1591
1592
		$rootfolder = mapi_msgstore_openentry($this->store);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

1592
		$rootfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store);
Loading history...
1593
		$hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable 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

1593
		$hierarchy = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
1594
		// Do not take hidden and system folders into account
1595
		// TODO make this restriction generic and use for hierarchy?
1596
		$restrict = [
1597
			RES_AND,
1598
			[
1599
				[
1600
					RES_PROPERTY,
1601
					[
1602
						RELOP => RELOP_NE,
1603
						ULPROPTAG => PR_ATTR_HIDDEN,
1604
						VALUE => true, ],
1605
				],
1606
				[
1607
					RES_PROPERTY,
1608
					[
1609
						RELOP => RELOP_EQ,
1610
						ULPROPTAG => PR_FOLDER_TYPE,
1611
						VALUE => FOLDER_GENERIC, ],
1612
				],
1613
				[
1614
					RES_EXIST,
1615
					[ULPROPTAG => PR_CONTAINER_CLASS],
1616
				],
1617
			], ];
1618
		mapi_table_restrict($hierarchy, $restrict);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict 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

1618
		/** @scrutinizer ignore-call */ 
1619
  mapi_table_restrict($hierarchy, $restrict);
Loading history...
1619
		$foldercount = mapi_table_getrowcount($hierarchy);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount 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

1619
		$foldercount = /** @scrutinizer ignore-call */ mapi_table_getrowcount($hierarchy);
Loading history...
1620
1621
		$storeProps = mapi_getprops($this->store, [PR_MESSAGE_SIZE_EXTENDED]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

1621
		$storeProps = /** @scrutinizer ignore-call */ mapi_getprops($this->store, [PR_MESSAGE_SIZE_EXTENDED]);
Loading history...
1622
		$storesize = isset($storeProps[PR_MESSAGE_SIZE_EXTENDED]) ? $storeProps[PR_MESSAGE_SIZE_EXTENDED] : 0;
1623
1624
		$userDetails = $this->GetUserDetails($this->impersonateUser ?: $this->mainUser);
0 ignored issues
show
Bug introduced by
It seems like $this->impersonateUser ?: $this->mainUser can also be of type true; however, parameter $username of Grommunio::GetUserDetails() 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

1624
		$userDetails = $this->GetUserDetails(/** @scrutinizer ignore-type */ $this->impersonateUser ?: $this->mainUser);
Loading history...
1625
		$userStoreInfo->SetData($foldercount, $storesize, $userDetails['fullname'], $userDetails['emailaddress']);
1626
		SLog::Write(LOGLEVEL_DEBUG, sprintf(
1627
			"Grommunio->GetUserStoreInfo(): user %s (%s) store size is %d bytes and contains %d folders",
1628
			Utils::PrintAsString($userDetails['fullname']),
1629
			Utils::PrintAsString($userDetails['emailaddress']),
1630
			$storesize,
1631
			$foldercount
1632
		));
1633
1634
		return $userStoreInfo;
1635
	}
1636
1637
	/*----------------------------------------------------------------------------------------------------------
1638
	 * Implementation of the IStateMachine interface
1639
	 */
1640
1641
	/**
1642
	 * Gets a hash value indicating the latest dataset of the named
1643
	 * state with a specified key and counter.
1644
	 * If the state is changed between two calls of this method
1645
	 * the returned hash should be different.
1646
	 *
1647
	 * @param string $devid   the device id
1648
	 * @param string $type    the state type
1649
	 * @param string $key     (opt)
1650
	 * @param string $counter (opt)
1651
	 *
1652
	 * @throws StateNotFoundException
1653
	 *
1654
	 * @return string
1655
	 */
1656
	public function GetStateHash($devid, $type, $key = false, $counter = false) {
1657
		try {
1658
			$stateMessage = $this->getStateMessage($devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::getStateMessage() 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

1658
			$stateMessage = $this->getStateMessage($devid, $type, $key, /** @scrutinizer ignore-type */ $counter);
Loading history...
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::getStateMessage() 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

1658
			$stateMessage = $this->getStateMessage($devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
1659
			$stateMessageProps = mapi_getprops($stateMessage, [PR_LAST_MODIFICATION_TIME]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

1659
			$stateMessageProps = /** @scrutinizer ignore-call */ mapi_getprops($stateMessage, [PR_LAST_MODIFICATION_TIME]);
Loading history...
1660
			if (isset($stateMessageProps[PR_LAST_MODIFICATION_TIME])) {
1661
				return $stateMessageProps[PR_LAST_MODIFICATION_TIME];
1662
			}
1663
		}
1664
		catch (StateNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1665
		}
1666
1667
		return "0";
1668
	}
1669
1670
	/**
1671
	 * Gets a state for a specified key and counter.
1672
	 * This method should call IStateMachine->CleanStates()
1673
	 * to remove older states (same key, previous counters).
1674
	 *
1675
	 * @param string $devid       the device id
1676
	 * @param string $type        the state type
1677
	 * @param string $key         (opt)
1678
	 * @param string $counter     (opt)
1679
	 * @param string $cleanstates (opt)
1680
	 *
1681
	 * @throws StateNotFoundException, StateInvalidException, UnavailableException
1682
	 *
1683
	 * @return mixed
1684
	 */
1685
	public function GetState($devid, $type, $key = false, $counter = false, $cleanstates = true) {
1686
		if ($counter && $cleanstates) {
1687
			$this->CleanStates($devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::CleanStates() 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

1687
			$this->CleanStates($devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
1688
			// also clean Failsave state for previous counter
1689
			if ($key == false) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $key of type false|string against false; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1690
				$this->CleanStates($devid, $type, IStateMachine::FAILSAVE, $counter);
1691
			}
1692
		}
1693
		$stateMessage = $this->getStateMessage($devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::getStateMessage() 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

1693
		$stateMessage = $this->getStateMessage($devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::getStateMessage() 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

1693
		$stateMessage = $this->getStateMessage($devid, $type, $key, /** @scrutinizer ignore-type */ $counter);
Loading history...
1694
		$state = base64_decode(MAPIUtils::readPropStream($stateMessage, PR_BODY));
1695
1696
		if ($state && $state[0] === '{') {
1697
			$jsonDec = json_decode($state);
1698
			if (isset($jsonDec->gsSyncStateClass)) {
1699
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->GetState(): top class '%s'", $jsonDec->gsSyncStateClass));
1700
				$gsObj = new $jsonDec->gsSyncStateClass();
1701
				$gsObj->jsonDeserialize($jsonDec);
1702
				$gsObj->postUnserialize();
1703
			}
1704
		}
1705
1706
		return isset($gsObj) && is_object($gsObj) ? $gsObj : $state;
1707
	}
1708
1709
	/**
1710
	 * Writes ta state to for a key and counter.
1711
	 *
1712
	 * @param mixed  $state
1713
	 * @param string $devid   the device id
1714
	 * @param string $type    the state type
1715
	 * @param string $key     (opt)
1716
	 * @param int    $counter (opt)
1717
	 *
1718
	 * @throws StateInvalidException, UnavailableException
1719
	 *
1720
	 * @return bool
1721
	 */
1722
	public function SetState($state, $devid, $type, $key = false, $counter = false) {
1723
		return $this->setStateMessage($state, $devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::setStateMessage() does only seem to accept integer, 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

1723
		return $this->setStateMessage($state, $devid, $type, $key, /** @scrutinizer ignore-type */ $counter);
Loading history...
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::setStateMessage() 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

1723
		return $this->setStateMessage($state, $devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
1724
	}
1725
1726
	/**
1727
	 * Cleans up all older states.
1728
	 * If called with a $counter, all states previous state counter can be removed.
1729
	 * If additionally the $thisCounterOnly flag is true, only that specific counter will be removed.
1730
	 * If called without $counter, all keys (independently from the counter) can be removed.
1731
	 *
1732
	 * @param string $devid           the device id
1733
	 * @param string $type            the state type
1734
	 * @param string $key
1735
	 * @param string $counter         (opt)
1736
	 * @param string $thisCounterOnly (opt) if provided, the exact counter only will be removed
1737
	 *
1738
	 * @throws StateInvalidException
1739
	 *
1740
	 * @return
1741
	 */
1742
	public function CleanStates($devid, $type, $key, $counter = false, $thisCounterOnly = false) {
1743
		if (!$this->stateFolder) {
1744
			$this->getStateFolder($devid);
1745
			if (!$this->stateFolder) {
1746
				throw new StateNotFoundException(sprintf(
1747
					"Grommunio->getStateMessage(): Could not locate the state folder for device '%s'",
1748
					$devid
1749
				));
1750
			}
1751
		}
1752
		$messageName = rtrim((($key !== false) ? $key . "-" : "") . (($type !== "") ? $type : ""), "-");
0 ignored issues
show
introduced by
The condition $key !== false is always true.
Loading history...
1753
		$restriction = $this->getStateMessageRestriction($messageName, $counter, $thisCounterOnly);
0 ignored issues
show
Bug introduced by
It seems like $thisCounterOnly can also be of type false; however, parameter $thisCounterOnly of Grommunio::getStateMessageRestriction() 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

1753
		$restriction = $this->getStateMessageRestriction($messageName, $counter, /** @scrutinizer ignore-type */ $thisCounterOnly);
Loading history...
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::getStateMessageRestriction() 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

1753
		$restriction = $this->getStateMessageRestriction($messageName, /** @scrutinizer ignore-type */ $counter, $thisCounterOnly);
Loading history...
1754
		$stateFolderContents = mapi_folder_getcontentstable($this->stateFolder, MAPI_ASSOCIATED);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable 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

1754
		$stateFolderContents = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($this->stateFolder, MAPI_ASSOCIATED);
Loading history...
1755
		if ($stateFolderContents) {
1756
			mapi_table_restrict($stateFolderContents, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict 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

1756
			/** @scrutinizer ignore-call */ 
1757
   mapi_table_restrict($stateFolderContents, $restriction);
Loading history...
1757
			$rowCnt = mapi_table_getrowcount($stateFolderContents);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount 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

1757
			$rowCnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($stateFolderContents);
Loading history...
1758
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->CleanStates(): Found %d states to clean (%s)", $rowCnt, $messageName));
1759
			if ($rowCnt > 0) {
1760
				$rows = mapi_table_queryallrows($stateFolderContents, [PR_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

1760
				$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($stateFolderContents, [PR_ENTRYID]);
Loading history...
1761
				$entryids = [];
1762
				foreach ($rows as $row) {
1763
					$entryids[] = $row[PR_ENTRYID];
1764
				}
1765
				mapi_folder_deletemessages($this->stateFolder, $entryids, DELETE_HARD_DELETE);
1 ignored issue
show
Bug introduced by
The function mapi_folder_deletemessages 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

1765
				/** @scrutinizer ignore-call */ 
1766
    mapi_folder_deletemessages($this->stateFolder, $entryids, DELETE_HARD_DELETE);
Loading history...
1766
			}
1767
		}
1768
	}
1769
1770
	/**
1771
	 * Links a user to a device.
1772
	 *
1773
	 * @param string $username
1774
	 * @param string $devid
1775
	 *
1776
	 * @return bool indicating if the user was added or not (existed already)
1777
	 */
1778
	public function LinkUserDevice($username, $devid) {
1779
		$device = [$devid => time()];
1780
		$this->setDeviceUserData($this->type, $device, $username, -1, $subkey = -1, $doCas = "merge");
1781
1782
		return false;
1783
	}
1784
1785
	/**
1786
	 * Unlinks a device from a user.
1787
	 *
1788
	 * @param string $username
1789
	 * @param string $devid
1790
	 *
1791
	 * @return bool
1792
	 */
1793
	public function UnLinkUserDevice($username, $devid) {
1794
		// TODO: Implement
1795
		return false;
1796
	}
1797
1798
	/**
1799
	 * Returns the current version of the state files
1800
	 * grommunio:  This is not relevant atm. IStateMachine::STATEVERSION_02 will match GSync::GetLatestStateVersion().
1801
	 *          If it might be required to update states in the future, this could be implemented on a store level,
1802
	 *          where states are then migrated "on-the-fly"
1803
	 *          or
1804
	 *          in a global settings where all states in all stores are migrated once.
1805
	 *
1806
	 * @return int
1807
	 */
1808
	public function GetStateVersion() {
1809
		return IStateMachine::STATEVERSION_02;
0 ignored issues
show
Bug Best Practice introduced by
The expression return IStateMachine::STATEVERSION_02 returns the type string which is incompatible with the documented return type integer.
Loading history...
1810
	}
1811
1812
	/**
1813
	 * Sets the current version of the state files.
1814
	 *
1815
	 * @param int $version the new supported version
1816
	 *
1817
	 * @return bool
1818
	 */
1819
	public function SetStateVersion($version) {
1820
		return true;
1821
	}
1822
1823
	/**
1824
	 * Returns MAPIFolder object which contains the state information.
1825
	 * Creates this folder if it is not available yet.
1826
	 *
1827
	 * @param string $devid the device id
1828
	 *
1829
	 * @return MAPIFolder
0 ignored issues
show
Bug introduced by
The type MAPIFolder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1830
	 */
1831
	private function getStateFolder($devid) {
1832
		// Options request doesn't send device id
1833
		if (strlen($devid) == 0) {
1834
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type MAPIFolder.
Loading history...
1835
		}
1836
		// Try to get the state folder id from redis
1837
		if (!$this->stateFolder) {
1838
			$folderentryid = $this->getDeviceUserData($this->userDeviceData, $devid, $this->mainUser, "statefolder");
1839
			if ($folderentryid) {
1840
				$this->stateFolder = mapi_msgstore_openentry($this->store, hex2bin($folderentryid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

1840
				$this->stateFolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, hex2bin($folderentryid));
Loading history...
1841
			}
1842
		}
1843
1844
		// fallback code
1845
		if (!$this->stateFolder) {
1846
			SLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->getStateFolder(): state folder not set. Use fallback"));
1847
			$rootfolder = mapi_msgstore_openentry($this->store);
1848
			$hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable 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

1848
			$hierarchy = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
1849
			$restriction = $this->getStateFolderRestriction($devid);
1850
			// restrict the hierarchy to the grommunio-sync search folder only
1851
			mapi_table_restrict($hierarchy, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict 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

1851
			/** @scrutinizer ignore-call */ 
1852
   mapi_table_restrict($hierarchy, $restriction);
Loading history...
1852
			$rowCnt = mapi_table_getrowcount($hierarchy);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount 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

1852
			$rowCnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($hierarchy);
Loading history...
1853
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->getStateFolder(): found %d device state folders", $rowCnt));
1854
			if ($rowCnt == 1) {
1855
				$hierarchyRows = mapi_table_queryrows($hierarchy, [PR_ENTRYID], 0, 1);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows 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

1855
				$hierarchyRows = /** @scrutinizer ignore-call */ mapi_table_queryrows($hierarchy, [PR_ENTRYID], 0, 1);
Loading history...
1856
				$this->stateFolder = mapi_msgstore_openentry($this->store, $hierarchyRows[0][PR_ENTRYID]);
1857
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->getStateFolder(): %s", bin2hex($hierarchyRows[0][PR_ENTRYID])));
1858
				// put found id in redis
1859
				if ($devid) {
1860
					$this->setDeviceUserData($this->userDeviceData, bin2hex($hierarchyRows[0][PR_ENTRYID]), $devid, $this->mainUser, "statefolder");
1861
				}
1862
			}
1863
			elseif ($rowCnt == 0) {
1864
				// legacy code: create the hidden state folder and the device subfolder
1865
				// this should happen when the user configures the device (autodiscover or first sync if no autodiscover)
1866
1867
				$hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1868
				$restriction = $this->getStateFolderRestriction(STORE_STATE_FOLDER);
1869
				mapi_table_restrict($hierarchy, $restriction);
1870
				$rowCnt = mapi_table_getrowcount($hierarchy);
1871
				SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->getStateFolder(): found %d store state folders", $rowCnt));
1872
				if ($rowCnt == 1) {
1873
					$hierarchyRows = mapi_table_queryrows($hierarchy, [PR_ENTRYID], 0, 1);
1874
					$stateFolder = mapi_msgstore_openentry($this->store, $hierarchyRows[0][PR_ENTRYID]);
1875
				}
1876
				elseif ($rowCnt == 0) {
1877
					$stateFolder = mapi_folder_createfolder($rootfolder, STORE_STATE_FOLDER, "");
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

1877
					$stateFolder = /** @scrutinizer ignore-call */ mapi_folder_createfolder($rootfolder, STORE_STATE_FOLDER, "");
Loading history...
1878
					mapi_setprops($stateFolder, [PR_ATTR_HIDDEN => true]);
1 ignored issue
show
Bug introduced by
The function mapi_setprops 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

1878
					/** @scrutinizer ignore-call */ 
1879
     mapi_setprops($stateFolder, [PR_ATTR_HIDDEN => true]);
Loading history...
1879
				}
1880
1881
				// TODO: handle this
1882
1883
				if (isset($stateFolder) && $stateFolder) {
1884
					$devStateFolder = mapi_folder_createfolder($stateFolder, $devid, "");
1885
					$devStateFolderProps = mapi_getprops($devStateFolder);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

1885
					$devStateFolderProps = /** @scrutinizer ignore-call */ mapi_getprops($devStateFolder);
Loading history...
1886
					$this->stateFolder = mapi_msgstore_openentry($this->store, $devStateFolderProps[PR_ENTRYID]);
1887
					mapi_setprops($this->stateFolder, [PR_ATTR_HIDDEN => true]);
1888
					// we don't cache the entryid in redis, because this will happen on the next request anyway
1889
				}
1890
1891
				// TODO: unable to create state folder - throw exception
1892
			}
1893
1894
			// This case is rather unlikely that there would be several
1895
				// hidden folders having PR_DISPLAY_NAME the same as device id.
1896
1897
				// TODO: get the hierarchy table again, get entry id of STORE_STATE_FOLDER
1898
				// and compare it to the parent id of those folders.
1899
		}
1900
1901
		return $this->stateFolder;
1902
	}
1903
1904
	/**
1905
	 * Returns the associated MAPIMessage which contains the state information.
1906
	 *
1907
	 * @param string $devid   the device id
1908
	 * @param string $type    the state type
1909
	 * @param string $key     (opt)
1910
	 * @param string $counter state counter
1911
	 *
1912
	 * @throws StateNotFoundException
1913
	 *
1914
	 * @return MAPIMessage
0 ignored issues
show
Bug introduced by
The type MAPIMessage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1915
	 */
1916
	private function getStateMessage($devid, $type, $key, $counter) {
1917
		if (!$this->stateFolder) {
1918
			$this->getStateFolder(Request::GetDeviceID());
0 ignored issues
show
Bug introduced by
It seems like Request::GetDeviceID() can also be of type false; however, parameter $devid of Grommunio::getStateFolder() 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

1918
			$this->getStateFolder(/** @scrutinizer ignore-type */ Request::GetDeviceID());
Loading history...
1919
			if (!$this->stateFolder) {
1920
				throw new StateNotFoundException(sprintf(
1921
					"Grommunio->getStateMessage(): Could not locate the state folder for device '%s'",
1922
					$devid
1923
				));
1924
			}
1925
		}
1926
		$messageName = rtrim((($key !== false) ? $key . "-" : "") . (($type !== "") ? $type : ""), "-");
0 ignored issues
show
introduced by
The condition $key !== false is always true.
Loading history...
1927
		$restriction = $this->getStateMessageRestriction($messageName, $counter, true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $thisCounterOnly of Grommunio::getStateMessageRestriction(). ( Ignorable by Annotation )

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

1927
		$restriction = $this->getStateMessageRestriction($messageName, $counter, /** @scrutinizer ignore-type */ true);
Loading history...
1928
		$stateFolderContents = mapi_folder_getcontentstable($this->stateFolder, MAPI_ASSOCIATED);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable 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

1928
		$stateFolderContents = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($this->stateFolder, MAPI_ASSOCIATED);
Loading history...
1929
		if ($stateFolderContents) {
1930
			mapi_table_restrict($stateFolderContents, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict 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

1930
			/** @scrutinizer ignore-call */ 
1931
   mapi_table_restrict($stateFolderContents, $restriction);
Loading history...
1931
			$rowCnt = mapi_table_getrowcount($stateFolderContents);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount 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

1931
			$rowCnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($stateFolderContents);
Loading history...
1932
			if ($rowCnt == 1) {
1933
				$stateFolderRows = mapi_table_queryrows($stateFolderContents, [PR_ENTRYID], 0, 1);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows 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

1933
				$stateFolderRows = /** @scrutinizer ignore-call */ mapi_table_queryrows($stateFolderContents, [PR_ENTRYID], 0, 1);
Loading history...
1934
1935
				return mapi_msgstore_openentry($this->store, $stateFolderRows[0][PR_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

1935
				return /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $stateFolderRows[0][PR_ENTRYID]);
Loading history...
1936
			}
1937
			if ($rowCnt > 1) {
1938
				SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->getStateMessage(): Found several (%d) states for '%s'", $rowCnt, $messageName));
1939
			}
1940
		}
1941
1942
		throw new StateNotFoundException(sprintf(
1943
			"Grommunio->getStateMessage(): Could not locate the state message '%s-%s'",
1944
			$messageName,
1945
			Utils::PrintAsString($counter)
1946
		));
1947
	}
1948
1949
	/**
1950
	 * Writes ta state to for a key and counter.
1951
	 *
1952
	 * @param mixed  $state
1953
	 * @param string $devid   the device id
1954
	 * @param string $type    the state type
1955
	 * @param string $key     (opt)
1956
	 * @param int    $counter (opt)
1957
	 *
1958
	 * @throws StateInvalidException, UnavailableException
1959
	 *
1960
	 * @return bool
1961
	 */
1962
	private function setStateMessage($state, $devid, $type, $key = false, $counter = false) {
1963
		if (!$this->stateFolder) {
1964
			throw new StateNotFoundException(sprintf("Grommunio->setStateMessage(): Could not locate the state folder for device '%s'", $devid));
1965
		}
1966
1967
		try {
1968
			$stateMessage = $this->getStateMessage($devid, $type, $key, $counter);
0 ignored issues
show
Bug introduced by
It seems like $key can also be of type false; however, parameter $key of Grommunio::getStateMessage() 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

1968
			$stateMessage = $this->getStateMessage($devid, $type, /** @scrutinizer ignore-type */ $key, $counter);
Loading history...
Bug introduced by
It seems like $counter can also be of type false; however, parameter $counter of Grommunio::getStateMessage() 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

1968
			$stateMessage = $this->getStateMessage($devid, $type, $key, /** @scrutinizer ignore-type */ $counter);
Loading history...
1969
		}
1970
		catch (StateNotFoundException $e) {
1971
			// if message is not available, try to create a new one
1972
			$stateMessage = mapi_folder_createmessage($this->stateFolder, MAPI_ASSOCIATED);
1 ignored issue
show
Bug introduced by
The function mapi_folder_createmessage 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

1972
			$stateMessage = /** @scrutinizer ignore-call */ mapi_folder_createmessage($this->stateFolder, MAPI_ASSOCIATED);
Loading history...
1973
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->setStateMessage(): mapi_folder_createmessage 0x%08X", mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

1973
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->setStateMessage(): mapi_folder_createmessage 0x%08X", /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
1974
1975
			$messageName = rtrim((($key !== false) ? $key . "-" : "") . (($type !== "") ? $type : ""), "-");
1976
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->setStateMessage(): creating new state message '%s-%d'", $messageName, is_int($counter) ? $counter : 0));
1977
			mapi_setprops($stateMessage, [PR_DISPLAY_NAME => $messageName, PR_MESSAGE_CLASS => 'IPM.Note.GrommunioState']);
1 ignored issue
show
Bug introduced by
The function mapi_setprops 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

1977
			/** @scrutinizer ignore-call */ 
1978
   mapi_setprops($stateMessage, [PR_DISPLAY_NAME => $messageName, PR_MESSAGE_CLASS => 'IPM.Note.GrommunioState']);
Loading history...
1978
		}
1979
		if (isset($stateMessage)) {
1980
			$jsonEncodedState = is_object($state) || is_array($state) ? json_encode($state, JSON_INVALID_UTF8_IGNORE | JSON_UNESCAPED_UNICODE) : $state;
1981
1982
			$encodedState = base64_encode($jsonEncodedState);
1983
			$encodedStateLength = strlen($encodedState);
1984
			mapi_setprops($stateMessage, [PR_LAST_VERB_EXECUTED => is_int($counter) ? $counter : 0]);
1985
			$stream = mapi_openproperty($stateMessage, PR_BODY, IID_IStream, STGM_DIRECT, MAPI_CREATE | MAPI_MODIFY);
1 ignored issue
show
Bug introduced by
The function mapi_openproperty 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

1985
			$stream = /** @scrutinizer ignore-call */ mapi_openproperty($stateMessage, PR_BODY, IID_IStream, STGM_DIRECT, MAPI_CREATE | MAPI_MODIFY);
Loading history...
1986
			mapi_stream_setsize($stream, $encodedStateLength);
1 ignored issue
show
Bug introduced by
The function mapi_stream_setsize 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

1986
			/** @scrutinizer ignore-call */ 
1987
   mapi_stream_setsize($stream, $encodedStateLength);
Loading history...
1987
			mapi_stream_write($stream, $encodedState);
1 ignored issue
show
Bug introduced by
The function mapi_stream_write 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

1987
			/** @scrutinizer ignore-call */ 
1988
   mapi_stream_write($stream, $encodedState);
Loading history...
1988
			mapi_stream_commit($stream);
1 ignored issue
show
Bug introduced by
The function mapi_stream_commit 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

1988
			/** @scrutinizer ignore-call */ 
1989
   mapi_stream_commit($stream);
Loading history...
1989
			mapi_savechanges($stateMessage);
1 ignored issue
show
Bug introduced by
The function mapi_savechanges 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

1989
			/** @scrutinizer ignore-call */ 
1990
   mapi_savechanges($stateMessage);
Loading history...
1990
1991
			return $encodedStateLength;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $encodedStateLength returns the type integer which is incompatible with the documented return type boolean.
Loading history...
1992
		}
1993
1994
		return false;
1995
	}
1996
1997
	/**
1998
	 * Returns the restriction for the state folder name.
1999
	 *
2000
	 * @param string $folderName the state folder name
2001
	 *
2002
	 * @return array
2003
	 */
2004
	private function getStateFolderRestriction($folderName) {
2005
		return [RES_AND, [
2006
			[RES_PROPERTY,
2007
				[RELOP => RELOP_EQ,
2008
					ULPROPTAG => PR_DISPLAY_NAME,
2009
					VALUE => $folderName,
2010
				],
2011
			],
2012
			[RES_PROPERTY,
2013
				[RELOP => RELOP_EQ,
2014
					ULPROPTAG => PR_ATTR_HIDDEN,
2015
					VALUE => true,
2016
				],
2017
			],
2018
		]];
2019
	}
2020
2021
	/**
2022
	 * Returns the restriction for the associated message in the state folder.
2023
	 *
2024
	 * @param string $messageName     the message name
2025
	 * @param string $counter         counter
2026
	 * @param string $thisCounterOnly (opt) if provided, restrict to the exact counter
2027
	 *
2028
	 * @return array
2029
	 */
2030
	private function getStateMessageRestriction($messageName, $counter, $thisCounterOnly = false) {
2031
		return [RES_AND, [
2032
			[RES_PROPERTY,
2033
				[RELOP => RELOP_EQ,
2034
					ULPROPTAG => PR_DISPLAY_NAME,
2035
					VALUE => $messageName,
2036
				],
2037
			],
2038
			[RES_PROPERTY,
2039
				[RELOP => RELOP_EQ,
2040
					ULPROPTAG => PR_MESSAGE_CLASS,
2041
					VALUE => 'IPM.Note.GrommunioState',
2042
				],
2043
			],
2044
			[RES_PROPERTY,
2045
				[RELOP => $thisCounterOnly ? RELOP_EQ : RELOP_LT,
2046
					ULPROPTAG => PR_LAST_VERB_EXECUTED,
2047
					VALUE => $counter,
2048
				],
2049
			],
2050
		]];
2051
	}
2052
2053
	/*----------------------------------------------------------------------------------------------------------
2054
	 * Private methods
2055
	 */
2056
2057
	/**
2058
	 * Returns a hash representing changes in the hierarchy of the main user.
2059
	 * It changes if a folder is added, renamed or deleted.
2060
	 *
2061
	 * @return string
2062
	 */
2063
	private function getHierarchyHash() {
2064
		$rootfolder = mapi_msgstore_openentry($this->defaultstore);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

2064
		$rootfolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->defaultstore);
Loading history...
2065
		$hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable 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

2065
		$hierarchy = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH);
Loading history...
2066
2067
		return md5(serialize(mapi_table_queryallrows($hierarchy, [PR_DISPLAY_NAME, PR_PARENT_ENTRYID])));
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

2067
		return md5(serialize(/** @scrutinizer ignore-call */ mapi_table_queryallrows($hierarchy, [PR_DISPLAY_NAME, PR_PARENT_ENTRYID])));
Loading history...
2068
	}
2069
2070
	/**
2071
	 * Advises a store to the changes sink.
2072
	 *
2073
	 * @param mapistore $store store to be advised
0 ignored issues
show
Bug introduced by
The type mapistore was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2074
	 *
2075
	 * @return bool
2076
	 */
2077
	private function adviseStoreToSink($store) {
2078
		// check if we already advised the store
2079
		if (!in_array($store, $this->changesSinkStores)) {
2080
			mapi_msgstore_advise($store, null, fnevObjectModified | fnevObjectCreated | fnevObjectMoved | fnevObjectDeleted, $this->changesSink);
0 ignored issues
show
Bug introduced by
The function mapi_msgstore_advise 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

2080
			/** @scrutinizer ignore-call */ 
2081
   mapi_msgstore_advise($store, null, fnevObjectModified | fnevObjectCreated | fnevObjectMoved | fnevObjectDeleted, $this->changesSink);
Loading history...
2081
2082
			if (mapi_last_hresult()) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

2082
			if (/** @scrutinizer ignore-call */ mapi_last_hresult()) {
Loading history...
2083
				SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->adviseStoreToSink(): failed to advised store '%s' with code 0x%X. Polling will be performed.", $store, mapi_last_hresult()));
2084
2085
				return false;
2086
			}
2087
2088
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->adviseStoreToSink(): advised store '%s'", $store));
2089
			$this->changesSinkStores[] = $store;
2090
		}
2091
2092
		return true;
2093
	}
2094
2095
	/**
2096
	 * Open the store marked with PR_DEFAULT_STORE = TRUE
2097
	 * if $return_public is set, the public store is opened.
2098
	 *
2099
	 * @param string $user User which store should be opened
2100
	 *
2101
	 * @return bool
2102
	 */
2103
	private function openMessageStore($user) {
2104
		// During PING requests the operations store has to be switched constantly
2105
		// the cache prevents the same store opened several times
2106
		if (isset($this->storeCache[$user])) {
2107
			return $this->storeCache[$user];
2108
		}
2109
2110
		$entryid = false;
2111
		$return_public = false;
2112
2113
		if (strtoupper($user) == 'SYSTEM') {
2114
			$return_public = true;
2115
		}
2116
2117
		// loop through the storestable if authenticated user of public folder
2118
		if ($user == $this->mainUser || $return_public === true) {
2119
			// Find the default store
2120
			$storestables = mapi_getmsgstorestable($this->session);
1 ignored issue
show
Bug introduced by
The function mapi_getmsgstorestable 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

2120
			$storestables = /** @scrutinizer ignore-call */ mapi_getmsgstorestable($this->session);
Loading history...
2121
			$result = mapi_last_hresult();
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

2121
			$result = /** @scrutinizer ignore-call */ mapi_last_hresult();
Loading history...
2122
2123
			if ($result == NOERROR) {
2124
				$rows = mapi_table_queryallrows($storestables, [PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

2124
				$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($storestables, [PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER]);
Loading history...
2125
2126
				foreach ($rows as $row) {
2127
					if (!$return_public && isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE] == true) {
2128
						$entryid = $row[PR_ENTRYID];
2129
2130
						break;
2131
					}
2132
					if ($return_public && isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
2133
						$entryid = $row[PR_ENTRYID];
2134
2135
						break;
2136
					}
2137
				}
2138
			}
2139
		}
2140
		else {
2141
			$entryid = @mapi_msgstore_createentryid($this->defaultstore, $user);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_createentryid 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

2141
			$entryid = @/** @scrutinizer ignore-call */ mapi_msgstore_createentryid($this->defaultstore, $user);
Loading history...
2142
		}
2143
2144
		if ($entryid) {
2145
			$store = @mapi_openmsgstore($this->session, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_openmsgstore 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

2145
			$store = @/** @scrutinizer ignore-call */ mapi_openmsgstore($this->session, $entryid);
Loading history...
2146
2147
			if (!$store) {
2148
				SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->openMessageStore('%s'): Could not open store", $user));
2149
2150
				return false;
2151
			}
2152
2153
			// add this store to the cache
2154
			if (!isset($this->storeCache[$user])) {
2155
				$this->storeCache[$user] = $store;
2156
			}
2157
2158
			SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->openMessageStore('%s'): Found '%s' store: '%s'", $user, (($return_public) ? 'PUBLIC' : 'DEFAULT'), $store));
2159
2160
			return $store;
2161
		}
2162
2163
		SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->openMessageStore('%s'): No store found for this user", $user));
2164
2165
		return false;
2166
	}
2167
2168
	/**
2169
	 * Checks if the logged in user has secretary permissions on a folder.
2170
	 *
2171
	 * @param resource $store
2172
	 * @param string   $folderid
2173
	 * @param mixed    $entryid
2174
	 *
2175
	 * @return bool
2176
	 */
2177
	public function HasSecretaryACLs($store, $folderid, $entryid = false) {
2178
		if (!$entryid) {
2179
			$entryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($folderid));
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_entryidfromsourcekey 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

2179
			$entryid = /** @scrutinizer ignore-call */ mapi_msgstore_entryidfromsourcekey($store, hex2bin($folderid));
Loading history...
2180
			if (!$entryid) {
2181
				SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->HasSecretaryACLs(): error, no entryid resolved for %s on store %s", $folderid, $store));
0 ignored issues
show
Bug introduced by
$store of type resource is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

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

2181
				SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->HasSecretaryACLs(): error, no entryid resolved for %s on store %s", $folderid, /** @scrutinizer ignore-type */ $store));
Loading history...
2182
2183
				return false;
2184
			}
2185
		}
2186
2187
		$folder = mapi_msgstore_openentry($store, $entryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

2187
		$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $entryid);
Loading history...
2188
		if (!$folder) {
2189
			SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->HasSecretaryACLs(): error, could not open folder with entryid %s on store %s", bin2hex($entryid), $store));
2190
2191
			return false;
2192
		}
2193
2194
		$props = mapi_getprops($folder, [PR_RIGHTS]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

2194
		$props = /** @scrutinizer ignore-call */ mapi_getprops($folder, [PR_RIGHTS]);
Loading history...
2195
		if (isset($props[PR_RIGHTS]) &&
2196
			($props[PR_RIGHTS] & ecRightsReadAny) &&
2197
			($props[PR_RIGHTS] & ecRightsCreate) &&
2198
			($props[PR_RIGHTS] & ecRightsEditOwned) &&
2199
			($props[PR_RIGHTS] & ecRightsDeleteOwned) &&
2200
			($props[PR_RIGHTS] & ecRightsEditAny) &&
2201
			($props[PR_RIGHTS] & ecRightsDeleteAny) &&
2202
			($props[PR_RIGHTS] & ecRightsFolderVisible)) {
2203
			return true;
2204
		}
2205
2206
		return false;
2207
	}
2208
2209
	/**
2210
	 * The meta function for out of office settings.
2211
	 *
2212
	 * @param SyncObject $oof
2213
	 */
2214
	private function settingsOOF(&$oof) {
2215
		// if oof state is set it must be set of oof and get otherwise
2216
		if (isset($oof->oofstate)) {
2217
			$this->settingsOofSet($oof);
2218
		}
2219
		else {
2220
			$this->settingsOofGet($oof);
2221
		}
2222
	}
2223
2224
	/**
2225
	 * Gets the out of office settings.
2226
	 *
2227
	 * @param SyncObject $oof
2228
	 */
2229
	private function settingsOofGet(&$oof) {
2230
		$oofprops = mapi_getprops($this->defaultstore, [PR_EC_OUTOFOFFICE, PR_EC_OUTOFOFFICE_MSG, PR_EC_OUTOFOFFICE_SUBJECT, PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

2230
		$oofprops = /** @scrutinizer ignore-call */ mapi_getprops($this->defaultstore, [PR_EC_OUTOFOFFICE, PR_EC_OUTOFOFFICE_MSG, PR_EC_OUTOFOFFICE_SUBJECT, PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL]);
Loading history...
2231
		$oof->oofstate = SYNC_SETTINGSOOF_DISABLED;
2232
		$oof->Status = SYNC_SETTINGSSTATUS_SUCCESS;
2233
		if ($oofprops != false) {
2234
			$oof->oofstate = isset($oofprops[PR_EC_OUTOFOFFICE]) ? ($oofprops[PR_EC_OUTOFOFFICE] ? SYNC_SETTINGSOOF_GLOBAL : SYNC_SETTINGSOOF_DISABLED) : SYNC_SETTINGSOOF_DISABLED;
2235
			// TODO external and external unknown
2236
			$oofmessage = new SyncOOFMessage();
2237
			$oofmessage->appliesToInternal = "";
2238
			$oofmessage->enabled = $oof->oofstate;
2239
			$oofmessage->replymessage = (isset($oofprops[PR_EC_OUTOFOFFICE_MSG])) ? w2u($oofprops[PR_EC_OUTOFOFFICE_MSG]) : "";
2240
			$oofmessage->bodytype = $oof->bodytype;
2241
			unset($oofmessage->appliesToExternal, $oofmessage->appliesToExternalUnknown);
2242
			$oof->oofmessage[] = $oofmessage;
2243
2244
			// check whether time based out of office is set
2245
			if ($oof->oofstate == SYNC_SETTINGSOOF_GLOBAL && isset($oofprops[PR_EC_OUTOFOFFICE_FROM], $oofprops[PR_EC_OUTOFOFFICE_UNTIL])) {
2246
				$now = time();
2247
				if ($now > $oofprops[PR_EC_OUTOFOFFICE_FROM] && $now > $oofprops[PR_EC_OUTOFOFFICE_UNTIL]) {
2248
					// Out of office is set but the date is in the past. Set the state to disabled.
2249
					// @see https://jira.z-hub.io/browse/ZP-1188 for details
2250
					$oof->oofstate = SYNC_SETTINGSOOF_DISABLED;
2251
					@mapi_setprops($this->defaultstore, [PR_EC_OUTOFOFFICE => false]);
1 ignored issue
show
Bug introduced by
The function mapi_setprops 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

2251
					@/** @scrutinizer ignore-call */ mapi_setprops($this->defaultstore, [PR_EC_OUTOFOFFICE => false]);
Loading history...
2252
					@mapi_deleteprops($this->defaultstore, [PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL]);
1 ignored issue
show
Bug introduced by
The function mapi_deleteprops 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

2252
					@/** @scrutinizer ignore-call */ mapi_deleteprops($this->defaultstore, [PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL]);
Loading history...
2253
					SLog::Write(LOGLEVEL_INFO, "Grommunio->settingsOofGet(): Out of office is set but the from and until are in the past. Disabling out of office.");
2254
				}
2255
				elseif ($oofprops[PR_EC_OUTOFOFFICE_FROM] < $oofprops[PR_EC_OUTOFOFFICE_UNTIL]) {
2256
					$oof->oofstate = SYNC_SETTINGSOOF_TIMEBASED;
2257
					$oof->starttime = $oofprops[PR_EC_OUTOFOFFICE_FROM];
2258
					$oof->endtime = $oofprops[PR_EC_OUTOFOFFICE_UNTIL];
2259
				}
2260
				else {
2261
					SLog::Write(LOGLEVEL_WARN, sprintf(
2262
						"Grommunio->settingsOofGet(): Time based out of office set but end time ('%s') is before startime ('%s').",
2263
						date("Y-m-d H:i:s", $oofprops[PR_EC_OUTOFOFFICE_FROM]),
2264
						date("Y-m-d H:i:s", $oofprops[PR_EC_OUTOFOFFICE_UNTIL])
2265
					));
2266
					$oof->Status = SYNC_SETTINGSSTATUS_PROTOCOLLERROR;
2267
				}
2268
			}
2269
			elseif ($oof->oofstate == SYNC_SETTINGSOOF_GLOBAL && (isset($oofprops[PR_EC_OUTOFOFFICE_FROM]) || isset($oofprops[PR_EC_OUTOFOFFICE_UNTIL]))) {
2270
				SLog::Write(LOGLEVEL_WARN, sprintf(
2271
					"Grommunio->settingsOofGet(): Time based out of office set but either start time ('%s') or end time ('%s') is missing.",
2272
					(isset($oofprops[PR_EC_OUTOFOFFICE_FROM]) ? date("Y-m-d H:i:s", $oofprops[PR_EC_OUTOFOFFICE_FROM]) : 'empty'),
2273
					(isset($oofprops[PR_EC_OUTOFOFFICE_UNTIL]) ? date("Y-m-d H:i:s", $oofprops[PR_EC_OUTOFOFFICE_UNTIL]) : 'empty')
2274
				));
2275
				$oof->Status = SYNC_SETTINGSSTATUS_PROTOCOLLERROR;
2276
			}
2277
		}
2278
		else {
2279
			SLog::Write(LOGLEVEL_WARN, "Grommunio->Unable to get out of office information");
2280
		}
2281
2282
		// unset body type for oof in order not to stream it
2283
		unset($oof->bodytype);
2284
	}
2285
2286
	/**
2287
	 * Sets the out of office settings.
2288
	 *
2289
	 * @param SyncObject $oof
2290
	 */
2291
	private function settingsOofSet(&$oof) {
2292
		$oof->Status = SYNC_SETTINGSSTATUS_SUCCESS;
2293
		$props = [];
2294
		if ($oof->oofstate == SYNC_SETTINGSOOF_GLOBAL || $oof->oofstate == SYNC_SETTINGSOOF_TIMEBASED) {
2295
			$props[PR_EC_OUTOFOFFICE] = true;
2296
			foreach ($oof->oofmessage as $oofmessage) {
2297
				if (isset($oofmessage->appliesToInternal)) {
2298
					$props[PR_EC_OUTOFOFFICE_MSG] = isset($oofmessage->replymessage) ? u2w($oofmessage->replymessage) : "";
2299
					$props[PR_EC_OUTOFOFFICE_SUBJECT] = "Out of office";
2300
				}
2301
			}
2302
			if ($oof->oofstate == SYNC_SETTINGSOOF_TIMEBASED) {
2303
				if (isset($oof->starttime, $oof->endtime)) {
2304
					$props[PR_EC_OUTOFOFFICE_FROM] = $oof->starttime;
2305
					$props[PR_EC_OUTOFOFFICE_UNTIL] = $oof->endtime;
2306
				}
2307
				elseif (isset($oof->starttime) || isset($oof->endtime)) {
2308
					$oof->Status = SYNC_SETTINGSSTATUS_PROTOCOLLERROR;
2309
				}
2310
			}
2311
			else {
2312
				$deleteProps = [PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL];
2313
			}
2314
		}
2315
		elseif ($oof->oofstate == SYNC_SETTINGSOOF_DISABLED) {
2316
			$props[PR_EC_OUTOFOFFICE] = false;
2317
			$deleteProps = [PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL];
2318
		}
2319
2320
		if (!empty($props)) {
2321
			@mapi_setprops($this->defaultstore, $props);
1 ignored issue
show
Bug introduced by
The function mapi_setprops 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

2321
			@/** @scrutinizer ignore-call */ mapi_setprops($this->defaultstore, $props);
Loading history...
2322
			$result = mapi_last_hresult();
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

2322
			$result = /** @scrutinizer ignore-call */ mapi_last_hresult();
Loading history...
2323
			if ($result != NOERROR) {
2324
				SLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->settingsOofSet(): Setting oof information failed (%X)", $result));
2325
2326
				return false;
2327
			}
2328
		}
2329
2330
		if (!empty($deleteProps)) {
2331
			@mapi_deleteprops($this->defaultstore, $deleteProps);
1 ignored issue
show
Bug introduced by
The function mapi_deleteprops 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

2331
			@/** @scrutinizer ignore-call */ mapi_deleteprops($this->defaultstore, $deleteProps);
Loading history...
2332
		}
2333
2334
		return true;
2335
	}
2336
2337
	/**
2338
	 * Gets the user's email address from server.
2339
	 *
2340
	 * @param SyncObject $userinformation
2341
	 */
2342
	private function settingsUserInformation(&$userinformation) {
2343
		if (!isset($this->defaultstore) || !isset($this->mainUser)) {
2344
			SLog::Write(LOGLEVEL_ERROR, "Grommunio->settingsUserInformation(): The store or user are not available for getting user information");
2345
2346
			return false;
2347
		}
2348
		$user = nsp_getuserinfo($this->mainUser);
1 ignored issue
show
Bug introduced by
The function nsp_getuserinfo 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

2348
		$user = /** @scrutinizer ignore-call */ nsp_getuserinfo($this->mainUser);
Loading history...
2349
		if ($user != false) {
2350
			$userinformation->Status = SYNC_SETTINGSSTATUS_USERINFO_SUCCESS;
2351
			if (Request::GetProtocolVersion() >= 14.1) {
2352
				$account = new SyncAccount();
2353
				$emailaddresses = new SyncEmailAddresses();
2354
				$emailaddresses->smtpaddress[] = $user["primary_email"];
2355
				$emailaddresses->primarysmtpaddress = $user["primary_email"];
2356
				$account->emailaddresses = $emailaddresses;
2357
				$userinformation->accounts[] = $account;
2358
			}
2359
			else {
2360
				$userinformation->emailaddresses[] = $user["primary_email"];
2361
			}
2362
2363
			return true;
2364
		}
2365
		SLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->settingsUserInformation(): Getting user information failed: nsp_getuserinfo(%X)", mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

2365
		SLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->settingsUserInformation(): Getting user information failed: nsp_getuserinfo(%X)", /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
2366
2367
		return false;
2368
	}
2369
2370
	/**
2371
	 * Gets the rights management templates from the server.
2372
	 *
2373
	 * @param SyncObject $rmTemplates
2374
	 */
2375
	private function settingsRightsManagementTemplates(&$rmTemplates) {
2376
		/* Currently there is no information rights management feature in
2377
		 * the grommunio backend, so just return the status and empty
2378
		 * SyncRightsManagementTemplates tag.
2379
		 * Once it's available, it would be something like:
2380
2381
		$rmTemplate = new SyncRightsManagementTemplate();
2382
		$rmTemplate->id = "some-template-id-eg-guid";
2383
		$rmTemplate->name = "Template name";
2384
		$rmTemplate->description = "What does the template do. E.g. it disables forward and reply.";
2385
		$rmTemplates->rmtemplates[] = $rmTemplate;
2386
		 */
2387
		$rmTemplates->Status = SYNC_COMMONSTATUS_IRMFEATUREDISABLED;
2388
		$rmTemplates->rmtemplates = [];
2389
	}
2390
2391
	/**
2392
	 * Sets the importance and priority of a message from a RFC822 message headers.
2393
	 *
2394
	 * @param int   $xPriority
2395
	 * @param array $mapiprops
2396
	 * @param mixed $sendMailProps
2397
	 */
2398
	private function getImportanceAndPriority($xPriority, &$mapiprops, $sendMailProps) {
2399
		switch ($xPriority) {
2400
			case 1:
2401
			case 2:
2402
				$priority = PRIO_URGENT;
2403
				$importance = IMPORTANCE_HIGH;
2404
				break;
2405
2406
			case 4:
2407
			case 5:
2408
				$priority = PRIO_NONURGENT;
2409
				$importance = IMPORTANCE_LOW;
2410
				break;
2411
2412
			case 3:
2413
			default:
2414
				$priority = PRIO_NORMAL;
2415
				$importance = IMPORTANCE_NORMAL;
2416
				break;
2417
		}
2418
		$mapiprops[$sendMailProps["importance"]] = $importance;
2419
		$mapiprops[$sendMailProps["priority"]] = $priority;
2420
	}
2421
2422
	/**
2423
	 * Copies attachments from one message to another.
2424
	 *
2425
	 * @param MAPIMessage $toMessage
2426
	 * @param MAPIMessage $fromMessage
2427
	 */
2428
	private function copyAttachments(&$toMessage, $fromMessage) {
2429
		$attachtable = mapi_message_getattachmenttable($fromMessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_getattachmenttable 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

2429
		$attachtable = /** @scrutinizer ignore-call */ mapi_message_getattachmenttable($fromMessage);
Loading history...
2430
		$rows = mapi_table_queryallrows($attachtable, [PR_ATTACH_NUM]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

2430
		$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($attachtable, [PR_ATTACH_NUM]);
Loading history...
2431
2432
		foreach ($rows as $row) {
2433
			if (isset($row[PR_ATTACH_NUM])) {
2434
				$attach = mapi_message_openattach($fromMessage, $row[PR_ATTACH_NUM]);
1 ignored issue
show
Bug introduced by
The function mapi_message_openattach 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

2434
				$attach = /** @scrutinizer ignore-call */ mapi_message_openattach($fromMessage, $row[PR_ATTACH_NUM]);
Loading history...
2435
				$newattach = mapi_message_createattach($toMessage);
1 ignored issue
show
Bug introduced by
The function mapi_message_createattach 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

2435
				$newattach = /** @scrutinizer ignore-call */ mapi_message_createattach($toMessage);
Loading history...
2436
				mapi_copyto($attach, [], [], $newattach, 0);
1 ignored issue
show
Bug introduced by
The function mapi_copyto 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

2436
				/** @scrutinizer ignore-call */ 
2437
    mapi_copyto($attach, [], [], $newattach, 0);
Loading history...
2437
				mapi_savechanges($newattach);
1 ignored issue
show
Bug introduced by
The function mapi_savechanges 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

2437
				/** @scrutinizer ignore-call */ 
2438
    mapi_savechanges($newattach);
Loading history...
2438
			}
2439
		}
2440
	}
2441
2442
	/**
2443
	 * Function will create a search folder in FINDER_ROOT folder
2444
	 * if folder exists then it will open it.
2445
	 *
2446
	 * @see createSearchFolder($store, $openIfExists = true) function in the webaccess
2447
	 *
2448
	 * @return mapiFolderObject $folder created search folder
0 ignored issues
show
Bug introduced by
The type mapiFolderObject was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2449
	 */
2450
	private function getSearchFolder() {
2451
		// create new or open existing search folder
2452
		$searchFolderRoot = $this->getSearchFoldersRoot($this->store);
0 ignored issues
show
Unused Code introduced by
The call to Grommunio::getSearchFoldersRoot() has too many arguments starting with $this->store. ( Ignorable by Annotation )

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

2452
		/** @scrutinizer ignore-call */ 
2453
  $searchFolderRoot = $this->getSearchFoldersRoot($this->store);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
2453
		if ($searchFolderRoot === false) {
2454
			// error in finding search root folder
2455
			// or store doesn't support search folders
2456
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type mapiFolderObject.
Loading history...
2457
		}
2458
2459
		$searchFolder = $this->createSearchFolder($searchFolderRoot);
2460
2461
		if ($searchFolder !== false && mapi_last_hresult() == NOERROR) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

2461
		if ($searchFolder !== false && /** @scrutinizer ignore-call */ mapi_last_hresult() == NOERROR) {
Loading history...
2462
			return $searchFolder;
2463
		}
2464
2465
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type mapiFolderObject.
Loading history...
2466
	}
2467
2468
	/**
2469
	 * Function will open FINDER_ROOT folder in root container
2470
	 * public folder's don't have FINDER_ROOT folder.
2471
	 *
2472
	 * @see getSearchFoldersRoot($store) function in the webaccess
2473
	 *
2474
	 * @return mapiFolderObject root folder for search folders
2475
	 */
2476
	private function getSearchFoldersRoot() {
2477
		// check if we can create search folders
2478
		$storeProps = mapi_getprops($this->store, [PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

2478
		$storeProps = /** @scrutinizer ignore-call */ mapi_getprops($this->store, [PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID]);
Loading history...
2479
		if (($storeProps[PR_STORE_SUPPORT_MASK] & STORE_SEARCH_OK) != STORE_SEARCH_OK) {
2480
			SLog::Write(LOGLEVEL_WARN, "Grommunio->getSearchFoldersRoot(): Store doesn't support search folders. Public store doesn't have FINDER_ROOT folder");
2481
2482
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type mapiFolderObject.
Loading history...
2483
		}
2484
2485
		// open search folders root
2486
		$searchRootFolder = mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

2486
		$searchRootFolder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]);
Loading history...
2487
		if (mapi_last_hresult() != NOERROR) {
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

2487
		if (/** @scrutinizer ignore-call */ mapi_last_hresult() != NOERROR) {
Loading history...
2488
			SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->getSearchFoldersRoot(): Unable to open search folder (0x%X)", mapi_last_hresult()));
2489
2490
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type mapiFolderObject.
Loading history...
2491
		}
2492
2493
		return $searchRootFolder;
2494
	}
2495
2496
	/**
2497
	 * Creates a search folder if it not exists or opens an existing one
2498
	 * and returns it.
2499
	 *
2500
	 * @param mapiFolderObject $searchFolderRoot
2501
	 *
2502
	 * @return mapiFolderObject
2503
	 */
2504
	private function createSearchFolder($searchFolderRoot) {
2505
		$folderName = "grommunio-sync Search Folder " . @getmypid();
0 ignored issues
show
Bug introduced by
Are you sure @getmypid() of type false|integer can be used in concatenation? ( Ignorable by Annotation )

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

2505
		$folderName = "grommunio-sync Search Folder " . /** @scrutinizer ignore-type */ @getmypid();
Loading history...
2506
		$searchFolders = mapi_folder_gethierarchytable($searchFolderRoot);
1 ignored issue
show
Bug introduced by
The function mapi_folder_gethierarchytable 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

2506
		$searchFolders = /** @scrutinizer ignore-call */ mapi_folder_gethierarchytable($searchFolderRoot);
Loading history...
2507
		$restriction = [
2508
			RES_CONTENT,
2509
			[
2510
				FUZZYLEVEL => FL_PREFIX,
2511
				ULPROPTAG => PR_DISPLAY_NAME,
2512
				VALUE => [PR_DISPLAY_NAME => $folderName],
2513
			],
2514
		];
2515
		// restrict the hierarchy to the grommunio-sync search folder only
2516
		mapi_table_restrict($searchFolders, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict 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

2516
		/** @scrutinizer ignore-call */ 
2517
  mapi_table_restrict($searchFolders, $restriction);
Loading history...
2517
		if (mapi_table_getrowcount($searchFolders)) {
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount 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

2517
		if (/** @scrutinizer ignore-call */ mapi_table_getrowcount($searchFolders)) {
Loading history...
2518
			$searchFolder = mapi_table_queryrows($searchFolders, [PR_ENTRYID], 0, 1);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows 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

2518
			$searchFolder = /** @scrutinizer ignore-call */ mapi_table_queryrows($searchFolders, [PR_ENTRYID], 0, 1);
Loading history...
2519
2520
			return mapi_msgstore_openentry($this->store, $searchFolder[0][PR_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

2520
			return /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->store, $searchFolder[0][PR_ENTRYID]);
Loading history...
2521
		}
2522
2523
		return mapi_folder_createfolder($searchFolderRoot, $folderName, null, 0, FOLDER_SEARCH);
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

2523
		return /** @scrutinizer ignore-call */ mapi_folder_createfolder($searchFolderRoot, $folderName, null, 0, FOLDER_SEARCH);
Loading history...
2524
	}
2525
2526
	/**
2527
	 * Creates a search restriction.
2528
	 *
2529
	 * @param ContentParameter $cpo
2530
	 *
2531
	 * @return array
2532
	 */
2533
	private function getSearchRestriction($cpo) {
2534
		$searchText = $cpo->GetSearchFreeText();
2535
2536
		$searchGreater = strtotime($cpo->GetSearchValueGreater());
2537
		$searchLess = strtotime($cpo->GetSearchValueLess());
2538
2539
		if (version_compare(phpversion(), '5.3.4') < 0) {
2540
			SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->getSearchRestriction(): Your system's PHP version (%s) might not correctly process unicode strings. Search containing such characters might not return correct results. It is recommended to update to at least PHP 5.3.4. See ZP-541 for more information.", phpversion()));
2541
		}
2542
		// split the search on whitespache and look for every word
2543
		$searchText = preg_split("/\\W+/u", $searchText);
2544
		$searchProps = [PR_BODY, PR_SUBJECT, PR_DISPLAY_TO, PR_DISPLAY_CC, PR_SENDER_NAME, PR_SENDER_EMAIL_ADDRESS, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS];
2545
		$resAnd = [];
2546
		foreach ($searchText as $term) {
2547
			$resOr = [];
2548
2549
			foreach ($searchProps as $property) {
2550
				array_push(
2551
					$resOr,
2552
					[RES_CONTENT,
2553
						[
2554
							FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE,
2555
							ULPROPTAG => $property,
2556
							VALUE => u2w($term),
2557
						],
2558
					]
2559
				);
2560
			}
2561
			array_push($resAnd, [RES_OR, $resOr]);
2562
		}
2563
2564
		// add time range restrictions
2565
		if ($searchGreater) {
2566
			array_push($resAnd, [RES_PROPERTY, [RELOP => RELOP_GE, ULPROPTAG => PR_MESSAGE_DELIVERY_TIME, VALUE => [PR_MESSAGE_DELIVERY_TIME => $searchGreater]]]); // RES_AND;
2567
		}
2568
		if ($searchLess) {
2569
			array_push($resAnd, [RES_PROPERTY, [RELOP => RELOP_LE, ULPROPTAG => PR_MESSAGE_DELIVERY_TIME, VALUE => [PR_MESSAGE_DELIVERY_TIME => $searchLess]]]);
2570
		}
2571
2572
		return [RES_AND, $resAnd];
2573
	}
2574
2575
	/**
2576
	 * Resolve recipient based on his email address.
2577
	 *
2578
	 * @param string $to
2579
	 * @param int    $maxAmbiguousRecipients
2580
	 * @param bool   $expandDistlist
2581
	 *
2582
	 * @return bool|SyncResolveRecipient
2583
	 */
2584
	private function resolveRecipient($to, $maxAmbiguousRecipients, $expandDistlist = true) {
2585
		$recipient = $this->resolveRecipientGAL($to, $maxAmbiguousRecipients, $expandDistlist);
2586
2587
		if ($recipient !== false) {
0 ignored issues
show
introduced by
The condition $recipient !== false is always true.
Loading history...
2588
			return $recipient;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $recipient returns the type array which is incompatible with the documented return type SyncResolveRecipient|boolean.
Loading history...
2589
		}
2590
2591
		$recipient = $this->resolveRecipientContact($to, $maxAmbiguousRecipients);
2592
2593
		if ($recipient !== false) {
2594
			return $recipient;
2595
		}
2596
2597
		return false;
2598
	}
2599
2600
	/**
2601
	 * Resolves recipient from the GAL and gets his certificates.
2602
	 *
2603
	 * @param string $to
2604
	 * @param int    $maxAmbiguousRecipients
2605
	 * @param bool   $expandDistlist
2606
	 *
2607
	 * @return array|bool
2608
	 */
2609
	private function resolveRecipientGAL($to, $maxAmbiguousRecipients, $expandDistlist = true) {
2610
		SLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientGAL(): Resolving recipient '%s' in GAL", $to));
2611
		$addrbook = $this->getAddressbook();
2612
		// FIXME: create a function to get the adressbook contentstable
2613
		$ab_entryid = mapi_ab_getdefaultdir($addrbook);
0 ignored issues
show
Bug introduced by
The function mapi_ab_getdefaultdir 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

2613
		$ab_entryid = /** @scrutinizer ignore-call */ mapi_ab_getdefaultdir($addrbook);
Loading history...
2614
		if ($ab_entryid) {
2615
			$ab_dir = mapi_ab_openentry($addrbook, $ab_entryid);
1 ignored issue
show
Bug introduced by
The function mapi_ab_openentry 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

2615
			$ab_dir = /** @scrutinizer ignore-call */ mapi_ab_openentry($addrbook, $ab_entryid);
Loading history...
2616
		}
2617
		if ($ab_dir) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ab_dir does not seem to be defined for all execution paths leading up to this point.
Loading history...
2618
			$table = mapi_folder_getcontentstable($ab_dir);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable 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

2618
			$table = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($ab_dir);
Loading history...
2619
		}
2620
2621
		if (!$table) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $table does not seem to be defined for all execution paths leading up to this point.
Loading history...
2622
			SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->resolveRecipientGAL(): Unable to open addressbook:0x%X", mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

2622
			SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->resolveRecipientGAL(): Unable to open addressbook:0x%X", /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
2623
2624
			return false;
2625
		}
2626
2627
		$restriction = MAPIUtils::GetSearchRestriction(u2w($to));
0 ignored issues
show
Bug introduced by
It seems like u2w($to) can also be of type false; however, parameter $query of MAPIUtils::GetSearchRestriction() 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

2627
		$restriction = MAPIUtils::GetSearchRestriction(/** @scrutinizer ignore-type */ u2w($to));
Loading history...
2628
		mapi_table_restrict($table, $restriction);
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict 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

2628
		/** @scrutinizer ignore-call */ 
2629
  mapi_table_restrict($table, $restriction);
Loading history...
2629
2630
		$querycnt = mapi_table_getrowcount($table);
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount 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

2630
		$querycnt = /** @scrutinizer ignore-call */ mapi_table_getrowcount($table);
Loading history...
2631
		if ($querycnt > 0) {
2632
			$recipientGal = [];
2633
			$rowsToQuery = $maxAmbiguousRecipients;
2634
			// some devices request 0 ambiguous recipients
2635
			if ($querycnt == 1 && $maxAmbiguousRecipients == 0) {
2636
				$rowsToQuery = 1;
2637
			}
2638
			elseif ($querycnt > 1 && $maxAmbiguousRecipients == 0) {
2639
				SLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->resolveRecipientGAL(): GAL search found %d recipients but the device hasn't requested ambiguous recipients", $querycnt));
2640
2641
				return $recipientGal;
2642
			}
2643
			elseif ($querycnt > 1 && $maxAmbiguousRecipients == 1) {
2644
				$rowsToQuery = $querycnt;
2645
			}
2646
			// get the certificate every time because caching the certificate is less expensive than opening addressbook entry again
2647
			$abentries = mapi_table_queryrows($table, [PR_ENTRYID, PR_DISPLAY_NAME, PR_EMS_AB_TAGGED_X509_CERT, PR_OBJECT_TYPE, PR_SMTP_ADDRESS], 0, $rowsToQuery);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryrows 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

2647
			$abentries = /** @scrutinizer ignore-call */ mapi_table_queryrows($table, [PR_ENTRYID, PR_DISPLAY_NAME, PR_EMS_AB_TAGGED_X509_CERT, PR_OBJECT_TYPE, PR_SMTP_ADDRESS], 0, $rowsToQuery);
Loading history...
2648
			for ($i = 0, $nrEntries = count($abentries); $i < $nrEntries; ++$i) {
2649
				if (strcasecmp($abentries[$i][PR_SMTP_ADDRESS], $to) !== 0 && $maxAmbiguousRecipients == 1) {
2650
					SLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->resolveRecipientGAL(): maxAmbiguousRecipients is 1 and found non-matching user (to '%s' found: '%s')", $to, $abentries[$i][PR_SMTP_ADDRESS]));
2651
2652
					continue;
2653
				}
2654
				if ($abentries[$i][PR_OBJECT_TYPE] == MAPI_DISTLIST) {
2655
					// check whether to expand dist list
2656
					if ($expandDistlist) {
2657
						SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->resolveRecipientGAL(): '%s' is a dist list. Expand it to members.", $to));
2658
						$distList = mapi_ab_openentry($addrbook, $abentries[$i][PR_ENTRYID]);
2659
						$distListContent = mapi_folder_getcontentstable($distList);
2660
						$distListMembers = mapi_table_queryallrows($distListContent, [PR_ENTRYID, PR_DISPLAY_NAME, PR_EMS_AB_TAGGED_X509_CERT]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

2660
						$distListMembers = /** @scrutinizer ignore-call */ mapi_table_queryallrows($distListContent, [PR_ENTRYID, PR_DISPLAY_NAME, PR_EMS_AB_TAGGED_X509_CERT]);
Loading history...
2661
						for ($j = 0, $nrDistListMembers = mapi_table_getrowcount($distListContent); $j < $nrDistListMembers; ++$j) {
2662
							SLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientGAL(): distlist's '%s' member: '%s'", $to, $distListMembers[$j][PR_DISPLAY_NAME]));
2663
							$recipientGal[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_GAL, $to, $distListMembers[$j], $nrDistListMembers);
2664
						}
2665
					}
2666
					else {
2667
						SLog::Write(LOGLEVEL_DEBUG, sprintf("Grommunio->resolveRecipientGAL(): '%s' is a dist list, but return it as is.", $to));
2668
						$recipientGal[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_GAL, $abentries[$i][PR_SMTP_ADDRESS], $abentries[$i]);
2669
					}
2670
				}
2671
				elseif ($abentries[$i][PR_OBJECT_TYPE] == MAPI_MAILUSER) {
2672
					$recipientGal[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_GAL, $abentries[$i][PR_SMTP_ADDRESS], $abentries[$i]);
2673
				}
2674
			}
2675
2676
			SLog::Write(LOGLEVEL_WBXML, "Grommunio->resolveRecipientGAL(): Found a recipient in GAL");
2677
2678
			return $recipientGal;
2679
		}
2680
2681
		SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->resolveRecipientGAL(): No recipient found for: '%s' in GAL", $to));
2682
2683
		return SYNC_RESOLVERECIPSSTATUS_RESPONSE_UNRESOLVEDRECIP;
0 ignored issues
show
Bug Best Practice introduced by
The expression return SYNC_RESOLVERECIP...ESPONSE_UNRESOLVEDRECIP returns the type integer which is incompatible with the documented return type array|boolean.
Loading history...
2684
2685
		return false;
0 ignored issues
show
Unused Code introduced by
return false is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
2686
	}
2687
2688
	/**
2689
	 * Resolves recipient from the contact list and gets his certificates.
2690
	 *
2691
	 * @param string $to
2692
	 * @param int    $maxAmbiguousRecipients
2693
	 *
2694
	 * @return array|bool
2695
	 */
2696
	private function resolveRecipientContact($to, $maxAmbiguousRecipients) {
2697
		SLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Resolving recipient '%s' in user's contacts", $to));
2698
		// go through all contact folders of the user and
2699
		// check if there's a contact with the given email address
2700
		$root = mapi_msgstore_openentry($this->defaultstore);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

2700
		$root = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($this->defaultstore);
Loading history...
2701
		if (!$root) {
2702
			SLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->resolveRecipientContact(): Unable to open default store: 0x%X", mapi_last_hresult()));
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

2702
			SLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->resolveRecipientContact(): Unable to open default store: 0x%X", /** @scrutinizer ignore-call */ mapi_last_hresult()));
Loading history...
2703
		}
2704
		$rootprops = mapi_getprops($root, [PR_IPM_CONTACT_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

2704
		$rootprops = /** @scrutinizer ignore-call */ mapi_getprops($root, [PR_IPM_CONTACT_ENTRYID]);
Loading history...
2705
		$contacts = $this->getContactsFromFolder($this->defaultstore, $rootprops[PR_IPM_CONTACT_ENTRYID], $to);
2706
		$recipients = [];
2707
2708
		if ($contacts !== false) {
2709
			SLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Found %d contacts in main contacts folder.", count($contacts)));
0 ignored issues
show
Bug introduced by
It seems like $contacts can also be of type true; however, parameter $value of count() does only seem to accept Countable|array, 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

2709
			SLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Found %d contacts in main contacts folder.", count(/** @scrutinizer ignore-type */ $contacts)));
Loading history...
2710
			// create resolve recipient object
2711
			foreach ($contacts as $contact) {
2712
				$recipients[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, $to, $contact);
2713
			}
2714
		}
2715
2716
		$contactfolder = mapi_msgstore_openentry($this->defaultstore, $rootprops[PR_IPM_CONTACT_ENTRYID]);
2717
		$subfolders = MAPIUtils::GetSubfoldersForType($contactfolder, "IPF.Contact");
2718
		if ($subfolders !== false) {
2719
			foreach ($subfolders as $folder) {
2720
				$contacts = $this->getContactsFromFolder($this->defaultstore, $folder[PR_ENTRYID], $to);
2721
				if ($contacts !== false) {
2722
					SLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Found %d contacts in contacts' subfolder.", count($contacts)));
2723
					foreach ($contacts as $contact) {
2724
						$recipients[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, $to, $contact);
2725
					}
2726
				}
2727
			}
2728
		}
2729
2730
		// search contacts in public folders
2731
		$storestables = mapi_getmsgstorestable($this->session);
1 ignored issue
show
Bug introduced by
The function mapi_getmsgstorestable 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

2731
		$storestables = /** @scrutinizer ignore-call */ mapi_getmsgstorestable($this->session);
Loading history...
2732
		$result = mapi_last_hresult();
2733
2734
		if ($result == NOERROR) {
2735
			$rows = mapi_table_queryallrows($storestables, [PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

2735
			$rows = /** @scrutinizer ignore-call */ mapi_table_queryallrows($storestables, [PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER]);
Loading history...
2736
			foreach ($rows as $row) {
2737
				if (isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
2738
					// TODO refactor public store
2739
					$publicstore = mapi_openmsgstore($this->session, $row[PR_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_openmsgstore 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

2739
					$publicstore = /** @scrutinizer ignore-call */ mapi_openmsgstore($this->session, $row[PR_ENTRYID]);
Loading history...
2740
					$publicfolder = mapi_msgstore_openentry($publicstore);
2741
2742
					$subfolders = MAPIUtils::GetSubfoldersForType($publicfolder, "IPF.Contact");
2743
					if ($subfolders !== false) {
2744
						foreach ($subfolders as $folder) {
2745
							$contacts = $this->getContactsFromFolder($publicstore, $folder[PR_ENTRYID], $to);
2746
							if ($contacts !== false) {
2747
								SLog::Write(LOGLEVEL_WBXML, sprintf("Grommunio->resolveRecipientContact(): Found %d contacts in public contacts folder.", count($contacts)));
2748
								foreach ($contacts as $contact) {
2749
									$recipients[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, $to, $contact);
2750
								}
2751
							}
2752
						}
2753
					}
2754
2755
					break;
2756
				}
2757
			}
2758
		}
2759
		else {
2760
			SLog::Write(LOGLEVEL_WARN, sprintf("Grommunio->resolveRecipientContact(): Unable to open public store: 0x%X", $result));
2761
		}
2762
2763
		if (empty($recipients)) {
2764
			$contactProperties = [];
2765
			$contactProperties[PR_DISPLAY_NAME] = $to;
2766
			$contactProperties[PR_USER_X509_CERTIFICATE] = false;
2767
2768
			$recipients[] = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, $to, $contactProperties);
2769
		}
2770
2771
		return $recipients;
2772
	}
2773
2774
	/**
2775
	 * Creates SyncResolveRecipientsCertificates object for ResolveRecipients.
2776
	 *
2777
	 * @param binary $certificates
0 ignored issues
show
Bug introduced by
The type binary was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2778
	 * @param int    $recipientCount
2779
	 *
2780
	 * @return SyncResolveRecipientsCertificates
2781
	 */
2782
	private function getCertificates($certificates, $recipientCount = 0) {
2783
		$cert = new SyncResolveRecipientsCertificates();
2784
		if ($certificates === false) {
0 ignored issues
show
introduced by
The condition $certificates === false is always false.
Loading history...
2785
			$cert->status = SYNC_RESOLVERECIPSSTATUS_CERTIFICATES_NOVALIDCERT;
2786
2787
			return $cert;
2788
		}
2789
		$cert->status = SYNC_RESOLVERECIPSSTATUS_SUCCESS;
2790
		$cert->certificatecount = count($certificates);
2791
		$cert->recipientcount = $recipientCount;
2792
		$cert->certificate = [];
2793
		foreach ($certificates as $certificate) {
2794
			$cert->certificate[] = base64_encode($certificate);
2795
		}
2796
2797
		return $cert;
2798
	}
2799
2800
	/**
2801
	 * Creates SyncResolveRecipient object for ResolveRecipientsResponse.
2802
	 *
2803
	 * @param int    $type
2804
	 * @param string $email
2805
	 * @param array  $recipientProperties
2806
	 * @param int    $recipientCount
2807
	 *
2808
	 * @return SyncResolveRecipient
2809
	 */
2810
	private function createResolveRecipient($type, $email, $recipientProperties, $recipientCount = 0) {
2811
		$recipient = new SyncResolveRecipient();
2812
		$recipient->type = $type;
2813
		$recipient->displayname = u2w($recipientProperties[PR_DISPLAY_NAME]);
2814
		$recipient->emailaddress = $email;
2815
2816
		if ($type == SYNC_RESOLVERECIPIENTS_TYPE_GAL) {
2817
			$certificateProp = PR_EMS_AB_TAGGED_X509_CERT;
2818
		}
2819
		elseif ($type == SYNC_RESOLVERECIPIENTS_TYPE_CONTACT) {
2820
			$certificateProp = PR_USER_X509_CERTIFICATE;
2821
		}
2822
		else {
2823
			$certificateProp = null;
2824
		}
2825
2826
		if (isset($recipientProperties[$certificateProp]) && is_array($recipientProperties[$certificateProp]) && !empty($recipientProperties[$certificateProp])) {
2827
			$certificates = $this->getCertificates($recipientProperties[$certificateProp], $recipientCount);
2828
		}
2829
		else {
2830
			$certificates = $this->getCertificates(false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type binary expected by parameter $certificates of Grommunio::getCertificates(). ( Ignorable by Annotation )

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

2830
			$certificates = $this->getCertificates(/** @scrutinizer ignore-type */ false);
Loading history...
2831
			SLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->createResolveRecipient(): No certificate found for '%s' (requested email address: '%s')", $recipientProperties[PR_DISPLAY_NAME], $email));
2832
		}
2833
		$recipient->certificates = $certificates;
2834
2835
		if (isset($recipientProperties[PR_ENTRYID])) {
2836
			$recipient->id = $recipientProperties[PR_ENTRYID];
2837
		}
2838
2839
		return $recipient;
2840
	}
2841
2842
	/**
2843
	 * Gets the availability of a user for the given time window.
2844
	 *
2845
	 * @param string                       $to
2846
	 * @param SyncResolveRecipient         $resolveRecipient
2847
	 * @param SyncResolveRecipientsOptions $resolveRecipientsOptions
2848
	 *
2849
	 * @return SyncResolveRecipientsAvailability
2850
	 */
2851
	private function getAvailability($to, $resolveRecipient, $resolveRecipientsOptions) {
2852
		$availability = new SyncResolveRecipientsAvailability();
2853
		$availability->status = SYNC_RESOLVERECIPSSTATUS_AVAILABILITY_SUCCESS;
2854
2855
		if (!isset($resolveRecipient->id)) {
2856
			// TODO this shouldn't happen but try to get the recipient in such a case
2857
		}
2858
2859
		$start = strtotime($resolveRecipientsOptions->availability->starttime);
2860
		$end = strtotime($resolveRecipientsOptions->availability->endtime);
2861
		// Each digit in the MergedFreeBusy indicates the free/busy status for the user for every 30 minute interval.
2862
		$timeslots = intval(ceil(($end - $start) / self::HALFHOURSECONDS));
2863
2864
		if ($timeslots > self::MAXFREEBUSYSLOTS) {
2865
			throw new StatusException("Grommunio->getAvailability(): the requested free busy range is too large.", SYNC_RESOLVERECIPSSTATUS_PROTOCOLERROR);
2866
		}
2867
2868
		$mergedFreeBusy = str_pad(fbNoData, $timeslots, fbNoData);
2869
2870
		$retval = mapi_getuseravailability($this->session, $resolveRecipient->id, $start, $end);
0 ignored issues
show
Bug introduced by
The function mapi_getuseravailability 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

2870
		$retval = /** @scrutinizer ignore-call */ mapi_getuseravailability($this->session, $resolveRecipient->id, $start, $end);
Loading history...
2871
		SLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->getAvailability(): free busy '%s'", print_r($retval, 1)));
0 ignored issues
show
Bug introduced by
It seems like print_r($retval, 1) can also be of type true; however, parameter $values of sprintf() does only seem to accept double|integer|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

2871
		SLog::Write(LOGLEVEL_INFO, sprintf("Grommunio->getAvailability(): free busy '%s'", /** @scrutinizer ignore-type */ print_r($retval, 1)));
Loading history...
2872
2873
		if (!empty($retval)) {
2874
			$freebusy = json_decode($retval, true);
2875
			// freebusy is available, assume that the user is free
2876
			$mergedFreeBusy = str_pad(fbFree, $timeslots, fbFree);
2877
			foreach ($freebusy['events'] as $event) {
2878
				// calculate which timeslot of mergedFreeBusy should be replaced.
2879
				$startSlot = intval(floor(($event['StartTime'] - $start) / self::HALFHOURSECONDS));
2880
				$endSlot = intval(floor(($event['EndTime'] - $start) / self::HALFHOURSECONDS));
2881
				// if event started at a multiple of half an hour from requested freebusy time and
2882
				// its duration is also a multiple of half an hour
2883
				// then it's necessary to reduce endSlot by one
2884
				if ((($event['StartTime'] - $start) % self::HALFHOURSECONDS == 0) && (($event['EndTime'] - $event['StartTime']) % self::HALFHOURSECONDS == 0)) {
2885
					--$endSlot;
2886
				}
2887
				$fbType = Utils::GetFbStatusFromType($event['BusyType']);
2888
				for ($i = $startSlot; $i <= $endSlot && $i < $timeslots; ++$i) {
2889
					// only set the new slot's free busy status if it's higher than the current one
2890
					if ($fbType > $mergedFreeBusy[$i]) {
2891
						$mergedFreeBusy[$i] = $fbType;
2892
					}
2893
				}
2894
			}
2895
		}
2896
		$availability->mergedfreebusy = $mergedFreeBusy;
2897
2898
		return $availability;
2899
	}
2900
2901
	/**
2902
	 * Returns contacts matching given email address from a folder.
2903
	 *
2904
	 * @param MAPIStore $store
0 ignored issues
show
Bug introduced by
The type MAPIStore was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2905
	 * @param binary    $folderEntryid
2906
	 * @param string    $email
2907
	 *
2908
	 * @return array|bool
2909
	 */
2910
	private function getContactsFromFolder($store, $folderEntryid, $email) {
2911
		$folder = mapi_msgstore_openentry($store, $folderEntryid);
1 ignored issue
show
Bug introduced by
The function mapi_msgstore_openentry 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

2911
		$folder = /** @scrutinizer ignore-call */ mapi_msgstore_openentry($store, $folderEntryid);
Loading history...
2912
		$folderContent = mapi_folder_getcontentstable($folder);
1 ignored issue
show
Bug introduced by
The function mapi_folder_getcontentstable 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

2912
		$folderContent = /** @scrutinizer ignore-call */ mapi_folder_getcontentstable($folder);
Loading history...
2913
		mapi_table_restrict($folderContent, MAPIUtils::GetEmailAddressRestriction($store, $email));
1 ignored issue
show
Bug introduced by
The function mapi_table_restrict 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

2913
		/** @scrutinizer ignore-call */ 
2914
  mapi_table_restrict($folderContent, MAPIUtils::GetEmailAddressRestriction($store, $email));
Loading history...
2914
		// TODO max limit
2915
		if (mapi_table_getrowcount($folderContent) > 0) {
1 ignored issue
show
Bug introduced by
The function mapi_table_getrowcount 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

2915
		if (/** @scrutinizer ignore-call */ mapi_table_getrowcount($folderContent) > 0) {
Loading history...
2916
			return mapi_table_queryallrows($folderContent, [PR_DISPLAY_NAME, PR_USER_X509_CERTIFICATE, PR_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_table_queryallrows 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

2916
			return /** @scrutinizer ignore-call */ mapi_table_queryallrows($folderContent, [PR_DISPLAY_NAME, PR_USER_X509_CERTIFICATE, PR_ENTRYID]);
Loading history...
2917
		}
2918
2919
		return false;
2920
	}
2921
2922
	/**
2923
	 * Get MAPI addressbook object.
2924
	 *
2925
	 * @return MAPIAddressbook object to be used with mapi_ab_* or false on failure
0 ignored issues
show
Bug introduced by
The type MAPIAddressbook was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2926
	 */
2927
	private function getAddressbook() {
2928
		if (isset($this->addressbook) && $this->addressbook) {
2929
			return $this->addressbook;
2930
		}
2931
		$this->addressbook = mapi_openaddressbook($this->session);
1 ignored issue
show
Bug introduced by
The function mapi_openaddressbook 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

2931
		$this->addressbook = /** @scrutinizer ignore-call */ mapi_openaddressbook($this->session);
Loading history...
2932
		$result = mapi_last_hresult();
1 ignored issue
show
Bug introduced by
The function mapi_last_hresult 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

2932
		$result = /** @scrutinizer ignore-call */ mapi_last_hresult();
Loading history...
2933
		if ($result && $this->addressbook === false) {
2934
			SLog::Write(LOGLEVEL_ERROR, sprintf("Grommunio->getAddressbook error opening addressbook 0x%X", $result));
2935
2936
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type MAPIAddressbook.
Loading history...
2937
		}
2938
2939
		return $this->addressbook;
2940
	}
2941
2942
	/**
2943
	 * Checks if the user is not disabled for grommunio-sync.
2944
	 *
2945
	 * @throws FatalException if user is disabled for grommunio-sync
2946
	 *
2947
	 * @return bool
2948
	 */
2949
	private function isGSyncEnabled() {
2950
		$addressbook = $this->getAddressbook();
2951
		// this check needs to be performed on the store of the main (authenticated) user
2952
		$store = $this->storeCache[$this->mainUser];
2953
		$userEntryid = mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_getprops 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

2953
		$userEntryid = /** @scrutinizer ignore-call */ mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID]);
Loading history...
2954
		$mailuser = mapi_ab_openentry($addressbook, $userEntryid[PR_MAILBOX_OWNER_ENTRYID]);
1 ignored issue
show
Bug introduced by
The function mapi_ab_openentry 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

2954
		$mailuser = /** @scrutinizer ignore-call */ mapi_ab_openentry($addressbook, $userEntryid[PR_MAILBOX_OWNER_ENTRYID]);
Loading history...
2955
		$enabledFeatures = mapi_getprops($mailuser, [PR_EC_DISABLED_FEATURES]);
2956
		if (isset($enabledFeatures[PR_EC_DISABLED_FEATURES]) && is_array($enabledFeatures[PR_EC_DISABLED_FEATURES])) {
2957
			$mobileDisabled = in_array(self::MOBILE_ENABLED, $enabledFeatures[PR_EC_DISABLED_FEATURES]);
2958
			$deviceId = Request::GetDeviceID();
2959
			// Checks for deviceId present in zarafaDisabledFeatures LDAP array attribute. Check is performed case insensitive.
2960
			$deviceIdDisabled = (($deviceId !== null) && in_array($deviceId, array_map('strtolower', $enabledFeatures[PR_EC_DISABLED_FEATURES]))) ? true : false;
2961
			if ($mobileDisabled) {
2962
				throw new FatalException("User is disabled for grommunio-sync.");
2963
			}
2964
			if ($deviceIdDisabled) {
2965
				throw new FatalException(sprintf("User has deviceId %s disabled for usage with grommunio-sync.", $deviceId));
2966
			}
2967
		}
2968
2969
		return true;
2970
	}
2971
}
2972