MAPISession::getSearchKey()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 2
c 1
b 1
f 0
nc 1
nop 0
dl 0
loc 4
rs 10
1
<?php
2
3
require_once BASE_PATH . 'server/includes/core/class.properties.php';
4
5
/**
6
 * MAPI session handling.
7
 *
8
 * This class handles MAPI authentication and stores
9
 */
10
class MAPISession {
11
	/**
12
	 * @var resource This holds the MAPI Session
13
	 */
14
	private $session;
15
16
	/**
17
	 * @var resource This can hold the addressbook resource
18
	 */
19
	private $ab;
20
21
	/**
22
	 * @var array List with all the currently opened stores
23
	 */
24
	private $stores;
25
26
	/**
27
	 * @var string The entryid (binary) of the default store
28
	 */
29
	private $defaultstore;
30
31
	/**
32
	 * @var string The entryid (binary) of the public store
33
	 */
34
	private $publicStore;
35
36
	/**
37
	 * @var array Information about the current session (username/email/password/etc)
38
	 */
39
	private $session_info;
40
41
	/**
42
	 * @var array Mapping username -> entryid for other stores
43
	 */
44
	private $userstores;
45
46
	/**
47
	 * @var int Makes sure retrieveUserData is called only once
48
	 */
49
	private $userDataRetrieved;
50
51
	public function __construct() {
52
		$this->stores = [];
53
		$this->defaultstore = 0;
54
		$this->publicStore = 0;
55
		$this->session = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type resource of property $session.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
56
		$this->ab = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type resource of property $ab.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
57
		$this->userstores = [];
58
		$this->userDataRetrieved = false;
0 ignored issues
show
Documentation Bug introduced by
The property $userDataRetrieved was declared of type integer, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
59
	}
60
61
	/**
62
	 * Logon to via php_mapi extension.
63
	 *
64
	 * Logs on to Gromox with the specified username and password. If the server is not specified,
65
	 * it will logon to the local server.
66
	 *
67
	 * @param string $username     the username of the user
68
	 * @param string $password     the password of the user
69
	 * @param string $server       the server address
70
	 * @param string $sslcert_file the optional ssl certificate file
71
	 * @param string $sslcert_pass the optional ssl certificate password
72
	 * @param string $flags        the optional logon flags
73
	 *
74
	 * @return int 0 on no error, otherwise a MAPI error code
75
	 */
76
	public function logon($username = '', $password = '', $server = DEFAULT_SERVER, $sslcert_file = '', $sslcert_pass = '', $flags = 0) {
77
		$result = NOERROR;
78
		$username = (string) $username;
79
		$password = (string) $password;
80
		$flags |= 1; // Always disable notifications
81
82
		try {
83
			$webapp_version = 'WebApp-' . trim(file_get_contents(BASE_PATH . 'version'));
84
			$browser_version = $_SERVER['HTTP_USER_AGENT'] ?? '';
85
			$this->session = mapi_logon_zarafa(
86
				$username,
87
				$password,
88
				$server,
89
				$sslcert_file,
90
				$sslcert_pass,
91
				$flags,
92
				$webapp_version,
93
				$browser_version
94
			);
95
			if ($this->session !== false) {
96
				$this->session_info["username"] = $username;
97
			}
98
		}
99
		catch (MAPIException $e) {
100
			$result = $e->getCode();
101
		}
102
103
		return $result;
104
	}
105
106
	/**
107
	 * Logons to gromox using the access token.
108
	 *
109
	 * @param mixed $email the username/email of the user
110
	 * @param mixed $token the access token
111
	 *
112
	 * @return int 0 on no error, otherwise a MAPI error code
113
	 */
114
	public function logon_token($email = null, $token = null) {
115
		$result = NOERROR;
116
		$email = (string) $email;
117
		$token = (string) $token;
118
119
		try {
120
			$this->session = mapi_logon_token($token);
121
			if ($this->session !== false) {
122
				$this->session_info["username"] = $email;
123
			}
124
		}
125
		catch (MAPIException $e) {
126
			$result = $e->getCode();
127
		}
128
129
		return $result;
130
	}
131
132
	/**
133
	 * Get the user MAPI Object.
134
	 *
135
	 * @param string $userEntryid The user entryid which is going to open. default is false.
136
	 *
137
	 * @return object an user MAPI object
138
	 */
139
	public function getUser($userEntryid = false) {
140
		if ($userEntryid === false) {
141
			// get user entryid
142
			$store_props = mapi_getprops($this->getDefaultMessageStore(), [PR_USER_ENTRYID]);
143
			$userEntryid = $store_props[PR_USER_ENTRYID];
144
		}
145
146
		// open the user entry
147
		return mapi_ab_openentry($this->getAddressbook(true), $userEntryid);
148
	}
149
150
	/**
151
	 * Get logged-in user information.
152
	 *
153
	 * This function populates the 'session_info' property of this class with the following information:
154
	 * - userentryid: the MAPI entryid of the current user
155
	 * - fullname: the fullname of the current user
156
	 * - emailaddress: the email address of the current user
157
	 *
158
	 * The function only populates the information once, subsequent calls will return without error and without
159
	 * doing anything.
160
	 *
161
	 * @return array Array of information about the currently logged-on user
162
	 */
163
	public function retrieveUserData() {
164
		if ($this->userDataRetrieved) {
165
			return;
166
		}
167
168
		$result = NOERROR;
169
170
		try {
171
			$store_props = mapi_getprops($this->getDefaultMessageStore(), [PR_USER_ENTRYID]);
172
			$user = $this->getUser($store_props[PR_USER_ENTRYID]);
173
174
			// receive userdata
175
			$user_props = [PR_ASSISTANT, PR_ASSISTANT_TELEPHONE_NUMBER, PR_BUSINESS2_TELEPHONE_NUMBER, PR_BUSINESS_TELEPHONE_NUMBER,
176
				PR_COMPANY_NAME, PR_COUNTRY, PR_DEPARTMENT_NAME, PR_DISPLAY_NAME,
177
				PR_EMAIL_ADDRESS, PR_EMS_AB_THUMBNAIL_PHOTO, PR_GIVEN_NAME, PR_HOME2_TELEPHONE_NUMBER,
178
				PR_STREET_ADDRESS, PR_HOME_TELEPHONE_NUMBER, PR_INITIALS, PR_LOCALITY,
179
				PR_MOBILE_TELEPHONE_NUMBER, PR_OFFICE_LOCATION, PR_PAGER_TELEPHONE_NUMBER, PR_POSTAL_CODE,
180
				PR_PRIMARY_FAX_NUMBER, PR_PRIMARY_TELEPHONE_NUMBER, PR_SEARCH_KEY, PR_SMTP_ADDRESS,
181
				PR_STATE_OR_PROVINCE, PR_SURNAME, PR_TITLE];
182
183
			$user_props = mapi_getprops($user, $user_props);
184
185
			if (is_array($user_props) && isset($user_props[PR_DISPLAY_NAME], $user_props[PR_SMTP_ADDRESS])) {
186
				$this->session_info["userentryid"] = $store_props[PR_USER_ENTRYID];
187
				$this->session_info["fullname"] = $user_props[PR_DISPLAY_NAME];
188
				$this->session_info["smtpaddress"] = $user_props[PR_SMTP_ADDRESS];
189
				$this->session_info["emailaddress"] = $user_props[PR_EMAIL_ADDRESS];
190
				$this->session_info["searchkey"] = $user_props[PR_SEARCH_KEY];
191
				$this->session_info["userimage"] = isset($user_props[PR_EMS_AB_THUMBNAIL_PHOTO]) ? "data:image/jpeg;base64," . base64_encode((string) $user_props[PR_EMS_AB_THUMBNAIL_PHOTO]) : "";
192
193
				$this->session_info["given_name"] = $user_props[PR_GIVEN_NAME] ?? '';
194
				$this->session_info["initials"] = $user_props[PR_INITIALS] ?? '';
195
				$this->session_info["surname"] = $user_props[PR_SURNAME] ?? '';
196
				$this->session_info["street_address"] = $user_props[PR_STREET_ADDRESS] ?? '';
197
				$this->session_info["locality"] = $user_props[PR_LOCALITY] ?? '';
198
				$this->session_info["state_or_province"] = $user_props[PR_STATE_OR_PROVINCE] ?? '';
199
				$this->session_info["postal_code"] = $user_props[PR_POSTAL_CODE] ?? '';
200
				$this->session_info["country"] = $user_props[PR_COUNTRY] ?? '';
201
				$this->session_info["title"] = $user_props[PR_TITLE] ?? '';
202
				$this->session_info["company_name"] = $user_props[PR_COMPANY_NAME] ?? '';
203
				$this->session_info["department_name"] = $user_props[PR_DEPARTMENT_NAME] ?? '';
204
				$this->session_info["office_location"] = $user_props[PR_OFFICE_LOCATION] ?? '';
205
				$this->session_info["assistant"] = $user_props[PR_ASSISTANT] ?? '';
206
				$this->session_info["assistant_telephone_number"] = $user_props[PR_ASSISTANT_TELEPHONE_NUMBER] ?? '';
207
				$this->session_info["office_telephone_number"] = $user_props[PR_PRIMARY_TELEPHONE_NUMBER] ?? '';
208
				$this->session_info["business_telephone_number"] = $user_props[PR_BUSINESS_TELEPHONE_NUMBER] ?? '';
209
				$this->session_info["business2_telephone_number"] = $user_props[PR_BUSINESS2_TELEPHONE_NUMBER] ?? '';
210
				$this->session_info["primary_fax_number"] = $user_props[PR_PRIMARY_FAX_NUMBER] ?? '';
211
				$this->session_info["home_telephone_number"] = $user_props[PR_HOME_TELEPHONE_NUMBER] ?? '';
212
				$this->session_info["home2_telephone_number"] = $user_props[PR_HOME2_TELEPHONE_NUMBER] ?? '';
213
				$this->session_info["mobile_telephone_number"] = $user_props[PR_MOBILE_TELEPHONE_NUMBER] ?? '';
214
				$this->session_info["pager_telephone_number"] = $user_props[PR_PAGER_TELEPHONE_NUMBER] ?? '';
215
			}
216
217
			$this->userDataRetrieved = true;
0 ignored issues
show
Documentation Bug introduced by
The property $userDataRetrieved was declared of type integer, but true is of type true. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
218
		}
219
		catch (MAPIException $e) {
220
			$result = $e->getCode();
221
		}
222
223
		return $result;
224
	}
225
226
	/**
227
	 * Get MAPI session object.
228
	 *
229
	 * @return mapisession Current MAPI session
230
	 */
231
	public function getSession() {
232
		return $this->session;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->session returns the type resource which is incompatible with the documented return type mapisession.
Loading history...
233
	}
234
235
	/**
236
	 * Set MAPI session object.
237
	 *
238
	 * @param mapisession The MAPI session
239
	 * @param mixed $session
240
	 */
241
	public function setSession($session) {
242
		$this->session = $session;
243
	}
244
245
	/**
246
	 * Get MAPI addressbook object.
247
	 *
248
	 * @param bool $providerless               When set to true it will return an addressbook resource
249
	 *                                         without any Contact Provider set on it, defaults to false
250
	 * @param bool $loadSharedContactsProvider when set to true it denotes that shared folders are
251
	 *                                         required to be configured to load the contacts from
252
	 *
253
	 * @return mixed An addressbook object to be used with mapi_ab_* or an error code
254
	 */
255
	public function getAddressbook($providerless = false, $loadSharedContactsProvider = false) {
256
		if ($providerless) {
257
			try {
258
				return mapi_openaddressbook($this->session);
259
			}
260
			catch (MAPIException $e) {
261
				return $e->getCode();
262
			}
263
		}
264
265
		$result = NOERROR;
266
267
		if ($this->ab === false) {
0 ignored issues
show
introduced by
The condition $this->ab === false is always false.
Loading history...
268
			$this->setupContactProviderAddressbook($loadSharedContactsProvider);
269
		}
270
271
		try {
272
			if ($this->ab === false) {
0 ignored issues
show
introduced by
The condition $this->ab === false is always false.
Loading history...
273
				$this->ab = mapi_openaddressbook($this->session);
274
			}
275
276
			if ($this->ab !== false) {
0 ignored issues
show
introduced by
The condition $this->ab !== false is always true.
Loading history...
277
				$result = $this->ab;
278
			}
279
		}
280
		catch (MAPIException $e) {
281
			$result = $e->getCode();
282
		}
283
284
		return $result;
285
	}
286
287
	/**
288
	 * Get logon status
289
	 * NOTE: This function only exists for backward compatibility with older plugins.
290
	 * 		 Currently the preferred way to check if a user is logged in, is using
291
	 * 		 the isAuthenticated() method of WebAppAuthentication.
292
	 *
293
	 * @return bool true on logged on, false on not logged on
294
	 */
295
	public function isLoggedOn() {
296
		trigger_error("isLoggedOn is deprecated, use WebAppAuthentication::isAuthenticated()", E_USER_NOTICE);
297
298
		return WebAppAuthentication::isAuthenticated();
299
	}
300
301
	/**
302
	 * Get current session id.
303
	 *
304
	 * @deprecated 2.2.0 This function only exists for backward compatibility with
305
	 * 		 older plugins that want to send the session id as a GET parameter with
306
	 * 		 requests that they make to grommunio.php. The script grommunio.php does not
307
	 * 		 expect this parameter anymore, but plugins that are not updated might
308
	 * 		 still call this function.
309
	 *
310
	 * @return string Always empty
311
	 */
312
	public function getSessionID() {
313
		return '';
314
	}
315
316
	/**
317
	 * Get current user entryid.
318
	 *
319
	 * @return string Current user's entryid
320
	 */
321
	public function getUserEntryID() {
322
		$this->retrieveUserData();
323
324
		return $this->session_info["userentryid"] ?? '';
325
	}
326
327
	/**
328
	 * Get current username.
329
	 *
330
	 * @return string Current user's username (equal to username passed in logon() )
331
	 */
332
	public function getUserName() {
333
		$encryptionStore = EncryptionStore::getInstance();
334
335
		return $encryptionStore->get('username') ?: '';
336
	}
337
338
	/**
339
	 * Get current user's full name.
340
	 *
341
	 * @return string User's full name
342
	 */
343
	public function getFullName() {
344
		$this->retrieveUserData();
345
346
		return array_key_exists("fullname", $this->session_info) ? $this->session_info["fullname"] : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_key_exists(...nfo['fullname'] : false could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
347
	}
348
349
	/**
350
	 * Get current user's smtp address.
351
	 *
352
	 * @return string User's smtp address
353
	 */
354
	public function getSMTPAddress() {
355
		$this->retrieveUserData();
356
357
		return array_key_exists("smtpaddress", $this->session_info) ? $this->session_info["smtpaddress"] : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_key_exists(...['smtpaddress'] : false could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
358
	}
359
360
	/**
361
	 * Get current user's email address.
362
	 *
363
	 * @return string User's email address
364
	 */
365
	public function getEmailAddress() {
366
		$this->retrieveUserData();
367
368
		return array_key_exists("emailaddress", $this->session_info) ? $this->session_info["emailaddress"] : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_key_exists(...'emailaddress'] : false could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
369
	}
370
371
	/**
372
	 * Get current user's image from the LDAP server.
373
	 *
374
	 * @return string A base64 encoded string (data url)
375
	 */
376
	public function getUserImage() {
377
		$this->retrieveUserData();
378
379
		return array_key_exists("userimage", $this->session_info) ? $this->session_info["userimage"] : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_key_exists(...fo['userimage'] : false could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
380
	}
381
382
	public function setUserImage($user_image) {
383
		if ($this->userDataRetrieved && is_array($this->session_info)) {
384
			$this->session_info["userimage"] = $user_image;
385
		}
386
	}
387
388
	public function getGivenName() {
389
		$this->retrieveUserData();
390
391
		return array_key_exists("given_name", $this->session_info) ? $this->session_info["given_name"] : false;
392
	}
393
394
	public function getInitials() {
395
		$this->retrieveUserData();
396
397
		return array_key_exists("initials", $this->session_info) ? $this->session_info["initials"] : false;
398
	}
399
400
	public function getSurname() {
401
		$this->retrieveUserData();
402
403
		return array_key_exists("surname", $this->session_info) ? $this->session_info["surname"] : false;
404
	}
405
406
	public function getStreetAddress() {
407
		$this->retrieveUserData();
408
409
		return array_key_exists("street_address", $this->session_info) ? $this->session_info["street_address"] : false;
410
	}
411
412
	public function getLocality() {
413
		$this->retrieveUserData();
414
415
		return array_key_exists("locality", $this->session_info) ? $this->session_info["locality"] : false;
416
	}
417
418
	public function getStateOrProvince() {
419
		$this->retrieveUserData();
420
421
		return array_key_exists("state_or_province", $this->session_info) ? $this->session_info["state_or_province"] : false;
422
	}
423
424
	public function getPostalCode() {
425
		$this->retrieveUserData();
426
427
		return array_key_exists("postal_code", $this->session_info) ? $this->session_info["postal_code"] : false;
428
	}
429
430
	public function getCountry() {
431
		$this->retrieveUserData();
432
433
		return array_key_exists("country", $this->session_info) ? $this->session_info["country"] : false;
434
	}
435
436
	public function getTitle() {
437
		$this->retrieveUserData();
438
439
		return array_key_exists("title", $this->session_info) ? $this->session_info["title"] : false;
440
	}
441
442
	public function getCompanyName() {
443
		$this->retrieveUserData();
444
445
		return array_key_exists("company_name", $this->session_info) ? $this->session_info["company_name"] : false;
446
	}
447
448
	public function getDepartmentName() {
449
		$this->retrieveUserData();
450
451
		return array_key_exists("department_name", $this->session_info) ? $this->session_info["department_name"] : false;
452
	}
453
454
	public function getOfficeLocation() {
455
		$this->retrieveUserData();
456
457
		return array_key_exists("office_location", $this->session_info) ? $this->session_info["office_location"] : false;
458
	}
459
460
	public function getAssistant() {
461
		$this->retrieveUserData();
462
463
		return array_key_exists("assistant", $this->session_info) ? $this->session_info["assistant"] : false;
464
	}
465
466
	public function getAssistantTelephoneNumber() {
467
		$this->retrieveUserData();
468
469
		return array_key_exists("assistant_telephone_number", $this->session_info) ? $this->session_info["assistant_telephone_number"] : false;
470
	}
471
472
	public function getOfficeTelephoneNumber() {
473
		$this->retrieveUserData();
474
475
		return array_key_exists("office_telephone_number", $this->session_info) ? $this->session_info["office_telephone_number"] : false;
476
	}
477
478
	public function getBusinessTelephoneNumber() {
479
		$this->retrieveUserData();
480
481
		return array_key_exists("business_telephone_number", $this->session_info) ? $this->session_info["business_telephone_number"] : false;
482
	}
483
484
	public function getBusiness2TelephoneNumber() {
485
		$this->retrieveUserData();
486
487
		return array_key_exists("business2_telephone_number", $this->session_info) ? $this->session_info["business2_telephone_number"] : false;
488
	}
489
490
	public function getPrimaryFaxNumber() {
491
		$this->retrieveUserData();
492
493
		return array_key_exists("primary_fax_number", $this->session_info) ? $this->session_info["primary_fax_number"] : false;
494
	}
495
496
	public function getHomeTelephoneNumber() {
497
		$this->retrieveUserData();
498
499
		return array_key_exists("home_telephone_number", $this->session_info) ? $this->session_info["home_telephone_number"] : false;
500
	}
501
502
	public function getHome2TelephoneNumber() {
503
		$this->retrieveUserData();
504
505
		return array_key_exists("home2_telephone_number", $this->session_info) ? $this->session_info["home2_telephone_number"] : false;
506
	}
507
508
	public function getMobileTelephoneNumber() {
509
		$this->retrieveUserData();
510
511
		return array_key_exists("mobile_telephone_number", $this->session_info) ? $this->session_info["mobile_telephone_number"] : false;
512
	}
513
514
	public function getPagerTelephoneNumber() {
515
		$this->retrieveUserData();
516
517
		return array_key_exists("pager_telephone_number", $this->session_info) ? $this->session_info["pager_telephone_number"] : false;
518
	}
519
520
	/**
521
	 * Checks whether the user is enabled for grommunio-web.
522
	 *
523
	 * @return bool
524
	 */
525
	public function isGwebEnabled() {
526
		$store_props = mapi_getprops($this->getDefaultMessageStore(), [PR_EC_ENABLED_FEATURES_L]);
527
528
		return $store_props[PR_EC_ENABLED_FEATURES_L] & UP_WEB;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $store_props[PR_E...ED_FEATURES_L] & UP_WEB returns the type integer which is incompatible with the documented return type boolean.
Loading history...
529
	}
530
531
	/**
532
	 * @return bool true if webapp is disabled feature else return false
533
	 */
534
	public function isWebappDisableAsFeature() {
535
		return !$this->isGwebEnabled();
536
	}
537
538
	/**
539
	 * Magic method to get properties from the session_info. When a method of this class if called
540
	 * and there is no method of this name defined this function will be called
541
	 * It creates getter methods for the properties stored in $session_info using the following syntax:
542
	 * getSomeUserProperty() will look return a property called some_user_property if it exists and
543
	 * throw an exception otherwise.
544
	 *
545
	 * @param string $methodName The name of the method that was called
546
	 * @param array  $arguments  The arguments that were passed in the call
547
	 *
548
	 * @return string The requested property if it exists
549
	 *
550
	 * @throws Exception
551
	 */
552
	public function __call($methodName, $arguments) {
553
		if (!preg_match('/^get(.+)$/', $methodName, $matches)) {
554
			// We don't know this function, so let's throw an error
555
			throw new Exception('Method ' . $methodName . ' does not exist');
556
		}
557
		$this->retrieveUserData();
558
		$propertyName = strtolower((string) preg_replace('/([^A-Z])([A-Z])/', '$1_$2', $matches[1]));
559
		if (!array_key_exists($propertyName, $this->session_info)) {
560
			// We don't know this function, so let's throw an error
561
			throw new Exception('Method ' . $methodName . ' does not exist ' . $propertyName);
562
		}
563
564
		return $this->session_info[$propertyName];
565
	}
566
567
	/**
568
	 * Returns a hash with information about the user that is logged in.
569
	 *
570
	 * @return array
571
	 */
572
	public function getUserInfo() {
573
		return [
574
			'username' => $this->getUserName(),
575
			'fullname' => $this->getFullName(),
576
			'entryid' => bin2hex($this->getUserEntryid()),
577
			'email_address' => $this->getEmailAddress(),
578
			'smtp_address' => $this->getSMTPAddress(),
579
			'search_key' => bin2hex($this->getSearchKey()),
580
			'user_image' => $this->getUserImage(),
581
			'given_name' => $this->getGivenName(),
582
			'initials' => $this->getInitials(),
583
			'surname' => $this->getSurname(),
584
			'street_address' => $this->getStreetAddress(),
585
			'locality' => $this->getLocality(),
586
			'state_or_province' => $this->getStateOrProvince(),
587
			'postal_code' => $this->getPostalCode(),
588
			'country' => $this->getCountry(),
589
			'title' => $this->getTitle(),
590
			'company_name' => $this->getCompanyName(),
591
			'department_name' => $this->getDepartmentName(),
592
			'office_location' => $this->getOfficeLocation(),
593
			'assistant' => $this->getAssistant(),
594
			'assistant_telephone_number' => $this->getAssistantTelephoneNumber(),
595
			'office_telephone_number' => $this->getOfficeTelephoneNumber(),
596
			'business_telephone_number' => $this->getBusinessTelephoneNumber(),
597
			'business2_telephone_number' => $this->getBusiness2TelephoneNumber(),
598
			'primary_fax_number' => $this->getPrimaryFaxNumber(),
599
			'home_telephone_number' => $this->getHomeTelephoneNumber(),
600
			'home2_telephone_number' => $this->getHome2TelephoneNumber(),
601
			'mobile_telephone_number' => $this->getMobileTelephoneNumber(),
602
			'pager_telephone_number' => $this->getPagerTelephoneNumber(),
603
		];
604
	}
605
606
	/**
607
	 * Get current user's search key.
608
	 *
609
	 * @return string Current user's searchkey
610
	 */
611
	public function getSearchKey() {
612
		$this->retrieveUserData();
613
614
		return $this->session_info["searchkey"] ?? '';
615
	}
616
617
	/**
618
	 * Get the message stores from the message store table from your session. Standard stores
619
	 * like the default store and the public store are made them easily accessible through the
620
	 * defaultstore and publicStore properties.
621
	 */
622
	public function loadMessageStoresFromSession() {
623
		$storestables = mapi_getmsgstorestable($this->session);
624
		$rows = mapi_table_queryallrows($storestables, [PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER]);
625
		foreach ($rows as $row) {
626
			if (!$row[PR_ENTRYID]) {
627
				continue;
628
			}
629
630
			if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE] == true) {
631
				$this->defaultstore = $row[PR_ENTRYID];
632
			}
633
			elseif ($row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
634
				$this->publicStore = $row[PR_ENTRYID];
635
			}
636
			elseif ($row[PR_MDB_PROVIDER] == ZARAFA_STORE_DELEGATE_GUID) {
637
				$eidObj = $GLOBALS["entryid"]->createMsgStoreEntryIdObj($row[PR_ENTRYID]);
638
				if (isset($eidObj['MailboxDN'])) {
639
					$this->openMessageStore($row[PR_ENTRYID], strtolower($eidObj['MailboxDN']));
640
				}
641
			}
642
		}
643
	}
644
645
	/**
646
	 * Get the current user's default message store.
647
	 *
648
	 * The store is opened only once, subsequent calls will return the previous store object
649
	 *
650
	 * @param bool reopen force re-open
0 ignored issues
show
Bug introduced by
The type reopen 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...
651
	 * @param mixed $reopen
652
	 *
653
	 * @return mapistore User's default message store object
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...
654
	 */
655
	public function getDefaultMessageStore($reopen = false) {
656
		// Return cached default store if we have one
657
		if (!$reopen && isset($this->defaultstore, $this->stores[$this->defaultstore])) {
658
			return $this->stores[$this->defaultstore];
659
		}
660
661
		$this->loadMessageStoresFromSession();
662
663
		return $this->openMessageStore($this->defaultstore, 'Default store');
664
	}
665
666
	/**
667
	 * The default messagestore entryid.
668
	 *
669
	 * @return string the entryid of the default messagestore
670
	 */
671
	public function getDefaultMessageStoreEntryId() {
672
		if (!isset($this->defaultstore)) {
673
			$this->loadMessageStoresFromSession();
674
		}
675
676
		return bin2hex($this->defaultstore);
677
	}
678
679
	/**
680
	 * Get single store if we are opening full store.
681
	 *
682
	 * @param object $store        the store of the user
683
	 * @param array  $storeOptions contains folder_type of which folder to open
684
	 *                             It is mapped to username, If folder_type is 'all' (i.e. Open Entire Inbox)
685
	 *                             then we will open full store.
686
	 * @param string $username     The username
687
	 *
688
	 * @return array storeArray The array of stores containing user's store
689
	 */
690
	public function getSingleMessageStores($store, $storeOptions, $username) {
0 ignored issues
show
Unused Code introduced by
The parameter $storeOptions is not used and could be removed. ( Ignorable by Annotation )

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

690
	public function getSingleMessageStores($store, /** @scrutinizer ignore-unused */ $storeOptions, $username) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $username is not used and could be removed. ( Ignorable by Annotation )

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

690
	public function getSingleMessageStores($store, $storeOptions, /** @scrutinizer ignore-unused */ $username) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
691
		return [$store];
692
	}
693
694
	/**
695
	 * Get the public message store.
696
	 *
697
	 * The store is opened only once, subsequent calls will return the previous store object
698
	 *
699
	 * @return mapistore Public message store object
700
	 */
701
	public function getPublicMessageStore() {
702
		// Return cached public store if we have one
703
		if (isset($this->publicStore, $this->stores[$this->publicStore])) {
704
			return $this->stores[$this->publicStore];
705
		}
706
707
		$this->loadMessageStoresFromSession();
708
709
		return $this->openMessageStore($this->publicStore, 'Public store');
710
	}
711
712
	/**
713
	 * Get all message stores currently open in the session.
714
	 *
715
	 * @return array Associative array with entryid -> mapistore of all open stores (private, public, delegate)
716
	 */
717
	public function getAllMessageStores() {
718
		$this->getDefaultMessageStore();
719
		$this->getPublicMessageStore();
720
		// The cache now contains all the stores in our profile. Next, add the stores
721
		// for other users.
722
		$this->getOtherUserStore();
723
724
		// Just return all the stores in our cache, even if we have some error in mapi
725
		return $this->stores;
726
	}
727
728
	/**
729
	 * Open the message store with entryid $entryid.
730
	 *
731
	 * @param string $entryid string representation of the binary entryid of the store
732
	 * @param string $name    The name of the store. Will be logged when opening fails.
733
	 *
734
	 * @return mapistore The opened store on success, false otherwise
735
	 */
736
	public function openMessageStore($entryid, $name = '') {
737
		// Check the cache before opening
738
		foreach ($this->stores as $storeEntryId => $storeObj) {
739
			if ($GLOBALS["entryid"]->compareEntryIds(bin2hex($entryid), bin2hex($storeEntryId))) {
740
				return $storeObj;
741
			}
742
		}
743
744
		try {
745
			$store = mapi_openmsgstore($this->session, $entryid);
746
			$store_props = mapi_getprops($store, [PR_ENTRYID]);
747
			$entryid = $store_props[PR_ENTRYID];
748
749
			// Cache the store for later use
750
			$this->stores[$entryid] = $store;
751
			$this->userstores[$name] = $entryid;
752
		}
753
		catch (MAPIException $e) {
754
			error_log('Failed to open store. ' . $this->session_info["username"] .
755
					  ' requested ' . bin2hex($entryid) . ($name ? " ({$name})" : ''));
756
757
			return $e->getCode();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $e->getCode() also could return the type integer which is incompatible with the documented return type mapistore.
Loading history...
758
		}
759
		catch (Exception $e) {
760
			// mapi_openmsgstore seems to throw another exception than MAPIException
761
			// sometimes, so we add a safety net.
762
			error_log('Failed to open store. ' . $this->session_info["username"] .
763
					  ' requested ' . bin2hex($entryid) . ($name ? " ({$name})" : ''));
764
765
			return $e->getCode();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $e->getCode() also could return the type integer which is incompatible with the documented return type mapistore.
Loading history...
766
		}
767
768
		return $store;
769
	}
770
771
	/**
772
	 * Get all the available shared stores.
773
	 *
774
	 * The store is opened only once, subsequent calls will return the previous store object
775
	 */
776
	public function getOtherUserStore() {
777
		$otherusers = $this->retrieveOtherUsersFromSettings();
778
		$otherUsersStores = [];
779
780
		foreach ($otherusers as $username => $folder) {
781
			if (isset($this->userstores[$username])) {
782
				continue;
783
			}
784
			$storeOk = true;
785
786
			if (is_array($folder) && !empty($folder)) {
787
				try {
788
					$user_entryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username);
789
790
					$sharedStore = $this->openMessageStore($user_entryid, $username);
791
					if ($sharedStore === false && $sharedStore === ecLoginPerm &&
792
						$sharedStore === MAPI_E_CALL_FAILED && $sharedStore === MAPI_E_NOT_FOUND) {
793
						$storeOk = false;
794
					}
795
				}
796
				catch (MAPIException $e) {
797
					if ($e->getCode() == MAPI_E_NOT_FOUND) {
798
						// The user or the corresponding store couldn't be found,
799
						// print an error to the log, and remove the user from the settings.
800
						dump('Failed to load store for user ' . $username . ', user was not found. Removing it from settings.');
801
						$GLOBALS["settings"]->delete("zarafa/v1/contexts/hierarchy/shared_stores/" . bin2hex($username), true);
802
					}
803
					else {
804
						// That is odd, something else went wrong. Lets not be hasty and preserve
805
						// the user in the settings, but do print something to the log to indicate
806
						// something happened...
807
						dump('Failed to load store for user ' . $username . '. ' . $e->getDisplayMessage());
808
					}
809
				}
810
				finally {
811
					if (!$storeOk && ($sharedStore == ecLoginPerm || $sharedStore == MAPI_E_NOT_FOUND)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sharedStore does not seem to be defined for all execution paths leading up to this point.
Loading history...
812
						// The user or the corresponding store couldn't be opened
813
						// (e.g. the user was deleted or permissions revoked),
814
						// print an error to the log, and remove the user from the settings.
815
						dump(sprintf("The user %s failed to load store of the user %s. Removing it from settings.", $this->session_info["username"], $username));
816
						$GLOBALS["settings"]->delete("zarafa/v1/contexts/hierarchy/shared_stores/" . bin2hex($username), true);
817
					}
818
				}
819
			}
820
		}
821
822
		foreach ($this->userstores as $entryid) {
823
			$otherUsersStores[$entryid] = $this->stores[$entryid];
824
		}
825
826
		return $otherUsersStores;
827
	}
828
829
	/**
830
	 * Resolve the username strictly by opening that user's store and returning the
831
	 * PR_MAILBOX_OWNER_ENTRYID. This can be used for resolving an username without the risk of
832
	 * ambiguity since mapi_ab_resolve() does not strictly resolve on the username.
833
	 *
834
	 * @param string $username The username
835
	 *
836
	 * @return Binary|int Entryid of the user on success otherwise the hresult error code
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...
837
	 */
838
	public function resolveStrictUserName($username) {
839
		$storeEntryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username);
840
		$store = $this->openMessageStore($storeEntryid, $username);
841
		$storeProps = mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID]);
842
843
		return $storeProps[PR_MAILBOX_OWNER_ENTRYID];
844
	}
845
846
	/**
847
	 * Get other users from settings.
848
	 *
849
	 * @return array Array of usernames of delegate stores
850
	 */
851
	public function retrieveOtherUsersFromSettings() {
852
		$other_users = $GLOBALS["settings"]->get("zarafa/v1/contexts/hierarchy/shared_stores", []);
853
		$result = [];
854
		foreach ($other_users as $username => $folders) {
855
			// No folders are being shared, the store has probably been closed by the user,
856
			// but the username is still lingering in the settings...
857
			if (!isset($folders) || empty($folders)) {
858
				continue;
859
			}
860
861
			$username = strtolower(hex2bin((string) $username));
862
			if (!isset($result[$username])) {
863
				$result[$username] = [];
864
			}
865
866
			foreach ($folders as $folder) {
867
				if (is_array($folder)) {
868
					$result[$username][$folder["folder_type"]] = [];
869
					$result[$username][$folder["folder_type"]]["folder_type"] = $folder["folder_type"];
870
					$result[$username][$folder["folder_type"]]["show_subfolders"] = $folder["show_subfolders"];
871
				}
872
			}
873
		}
874
875
		return $result;
876
	}
877
878
	/**
879
	 * Add the store of another user to the list of other user stores.
880
	 *
881
	 * @param string $username The username whose store should be added to the list of other users' stores
882
	 *
883
	 * @return mapistore The store of the user or false on error;
884
	 */
885
	public function addUserStore($username) {
886
		$user_entryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username);
887
888
		if ($user_entryid) {
889
			// mapi_msgstore_createentryid and mapi_getprops(PR_ENTRYID) have different
890
			// values for shared stores, so save the one from mapi_getprops(PR_ENTRYID)
891
			// $this->userstores[$username] = $user_entryid;
892
893
			return $this->openMessageStore($user_entryid, $username);
894
		}
895
	}
896
897
	/**
898
	 * Remove the store of another user from the list of other user stores.
899
	 *
900
	 * @param string $username The username whose store should be deleted from the list of other users' stores
901
	 *
902
	 * @return string The entryid of the store which was removed
903
	 */
904
	public function removeUserStore($username) {
905
		// Remove the reference to the store if we had one
906
		if (isset($this->userstores[$username])) {
907
			$entryid = $this->userstores[$username];
908
			unset($this->userstores[$username], $this->stores[$entryid]);
909
910
			return $entryid;
911
		}
912
	}
913
914
	/**
915
	 * Get the store entryid of the specified user.
916
	 *
917
	 * The store must have been previously added via addUserStores.
918
	 *
919
	 * @param string $username The username whose store is being looked up
920
	 *
921
	 * @return string The entryid of the store of the user
922
	 */
923
	public function getStoreEntryIdOfUser($username) {
924
		return $this->userstores[$username];
925
	}
926
927
	/**
928
	 * Get the username of the user store.
929
	 *
930
	 * @param string $username the loginname of whom we want to full name
931
	 *
932
	 * @return string the display name of the user
933
	 */
934
	public function getDisplayNameofUser($username) {
935
		$user_entryid = $this->getStoreEntryIdOfUser($username);
936
		$store = $this->openMessageStore($user_entryid, $username);
937
		$props = mapi_getprops($store, [PR_DISPLAY_NAME]);
938
939
		return str_replace('Inbox - ', '', $props[PR_DISPLAY_NAME]);
940
	}
941
942
	/**
943
	 * Get the username of the owner of the specified store.
944
	 *
945
	 * The store must have been previously added via addUserStores.
946
	 *
947
	 * @param string $entryid EntryID of the store
948
	 *
949
	 * @return string Username of the specified store or false if it is not found
950
	 */
951
	public function getUserNameOfStore($entryid) {
952
		foreach ($this->userstores as $username => $storeentryid) {
953
			if ($GLOBALS["entryid"]->compareEntryIds(bin2hex((string) $storeentryid), bin2hex($entryid))) {
954
				return $username;
955
			}
956
		}
957
958
		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...
959
	}
960
961
	/**
962
	 * Open a MAPI message using session object.
963
	 * The function is used to open message when we don't know
964
	 * the specific store and we want to open message using entryid.
965
	 *
966
	 * @param string $entryid entryid of the message
967
	 *
968
	 * @return object MAPI Message
969
	 */
970
	public function openMessage($entryid) {
971
		return mapi_openentry($this->session, $entryid);
972
	}
973
974
	/**
975
	 * Setup the contact provider for the addressbook. It asks getContactFoldersForABContactProvider
976
	 * for the entryids and display names for the contact folders in the user's store.
977
	 *
978
	 * @param bool $loadSharedContactsProvider when set to true it denotes that shared folders are
979
	 *                                         required to be configured to load the contacts from
980
	 */
981
	public function setupContactProviderAddressbook($loadSharedContactsProvider) {
982
		$profsect = mapi_openprofilesection($GLOBALS['mapisession']->getSession(), pbGlobalProfileSectionGuid);
983
		if ($profsect) {
984
			// Get information about all contact folders from own store, shared stores and public store
985
			$defaultStore = $this->getDefaultMessageStore();
986
			$contactFolders = $this->getContactFoldersForABContactProvider($defaultStore);
987
988
			// include shared contact folders in addressbook if shared contact folders are enabled
989
			if (ENABLE_SHARED_CONTACT_FOLDERS && $loadSharedContactsProvider) {
990
				if (empty($this->userstores)) {
991
					$this->getOtherUserStore();
992
				}
993
994
				$sharedSetting = $GLOBALS["settings"]->get("zarafa/v1/contexts/hierarchy/shared_stores", []);
995
				// Find available contact folders from all user stores, one by one.
996
				foreach ($this->userstores as $username => $storeEntryID) {
997
					$userContactFolders = [];
998
					$sharedUserSetting = [];
999
					$openedUserStore = $this->openMessageStore($storeEntryID, $username);
1000
1001
					// Get settings of respective shared folder of given user
1002
					if (array_key_exists(strtolower(bin2hex($username)), $sharedSetting)) {
1003
						$sharedUserSetting = $sharedSetting[strtolower(bin2hex($username))];
1004
					}
1005
1006
					// Only add opened shared folders into addressbook contacts provider.
1007
					// If entire inbox is opened then add each and every contact folders of that particular user.
1008
					if (isset($sharedUserSetting['all'])) {
1009
						$userContactFolders = $this->getContactFoldersForABContactProvider($openedUserStore);
1010
					}
1011
					elseif (isset($sharedUserSetting['contact'])) {
1012
						// Add respective default contact folder which is opened.
1013
						// Get entryid of default contact folder from root.
1014
						$root = mapi_msgstore_openentry($openedUserStore);
1015
						$rootProps = mapi_getprops($root, [PR_IPM_CONTACT_ENTRYID]);
1016
1017
						// Just add the default contact folder only.
1018
						$defaultContactFolder = [
1019
							PR_STORE_ENTRYID => $storeEntryID,
1020
							PR_ENTRYID => $rootProps[PR_IPM_CONTACT_ENTRYID],
1021
							PR_DISPLAY_NAME => _("Contacts"),
1022
						];
1023
						array_push($userContactFolders, $defaultContactFolder);
1024
1025
						// Go for sub folders only if configured in settings
1026
						if ($sharedUserSetting['contact']['show_subfolders'] == true) {
1027
							$subContactFolders = $this->getContactFolders($openedUserStore, $rootProps[PR_IPM_CONTACT_ENTRYID], true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type integer expected by parameter $depthSearch of MAPISession::getContactFolders(). ( Ignorable by Annotation )

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

1027
							$subContactFolders = $this->getContactFolders($openedUserStore, $rootProps[PR_IPM_CONTACT_ENTRYID], /** @scrutinizer ignore-type */ true);
Loading history...
1028
							if (is_array($subContactFolders)) {
1029
								$userContactFolders = array_merge($userContactFolders, $subContactFolders);
1030
							}
1031
						}
1032
					}
1033
1034
					// Postfix display name of every contact folder with respective owner name
1035
					// it is mandatory to keep display-name different
1036
					$userStoreProps = mapi_getprops($openedUserStore, [PR_MAILBOX_OWNER_NAME]);
1037
					for ($i = 0,$len = count($userContactFolders); $i < $len; ++$i) {
1038
						$userContactFolders[$i][PR_DISPLAY_NAME] = $userContactFolders[$i][PR_DISPLAY_NAME] . " - " . $userStoreProps[PR_MAILBOX_OWNER_NAME];
1039
					}
1040
1041
					$contactFolders = array_merge($contactFolders, $userContactFolders);
1042
				}
1043
			}
1044
1045
			// Include public contact folders in addressbook if public folders and public contacts folders are enabled
1046
			if (ENABLE_PUBLIC_CONTACT_FOLDERS && ENABLE_PUBLIC_FOLDERS) {
1047
				$publicStore = $this->getPublicMessageStore();
1048
				if ($publicStore !== false) {
0 ignored issues
show
introduced by
The condition $publicStore !== false is always true.
Loading history...
1049
					$contactFolders = array_merge($contactFolders, $this->getContactFoldersForABContactProvider($publicStore));
1050
				}
1051
			}
1052
			// TODO: The shared stores are not opened as there still is a bug that does not allow resolving from shared contact folders
1053
1054
			// These lists will be used to put set in the profile section
1055
			$contact_store_entryids = [];
1056
			$contact_folder_entryids = [];
1057
			$contact_folder_names = [];
1058
1059
			// Create the lists of store entryids, folder entryids and folder names to be added
1060
			// to the profile section
1061
			for ($i = 0, $len = count($contactFolders); $i < $len; ++$i) {
1062
				$contact_store_entryids[] = $contactFolders[$i][PR_STORE_ENTRYID];
1063
				$contact_folder_entryids[] = $contactFolders[$i][PR_ENTRYID];
1064
				$contact_folder_names[] = $contactFolders[$i][PR_DISPLAY_NAME];
1065
			}
1066
1067
			if (!empty($contact_store_entryids)) {
1068
				// add the defaults contacts folder in the addressbook hierarchy under 'Contacts Folders'
1069
				mapi_setprops($profsect, [PR_ZC_CONTACT_STORE_ENTRYIDS => $contact_store_entryids,
1070
					PR_ZC_CONTACT_FOLDER_ENTRYIDS => $contact_folder_entryids,
1071
					PR_ZC_CONTACT_FOLDER_NAMES => $contact_folder_names, ]);
1072
			}
1073
		}
1074
	}
1075
1076
	/**
1077
	 * Get the store entryid, folder entryid and display name of the contact folders in the
1078
	 * user's store. It returns an array prepared by getContactFolders.
1079
	 *
1080
	 * @param mapiStore $store The mapi store to look for folders in
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...
1081
	 *
1082
	 * @return array Contact folder information
1083
	 */
1084
	public function getContactFoldersForABContactProvider($store) {
1085
		$storeProps = mapi_getprops($store, [PR_ENTRYID, PR_MDB_PROVIDER, PR_IPM_SUBTREE_ENTRYID, PR_IPM_PUBLIC_FOLDERS_ENTRYID]);
1086
		$contactFolders = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $contactFolders is dead and can be removed.
Loading history...
1087
1088
		try {
1089
			// Only searches one level deep, otherwise deleted contact folders will also be included.
1090
			$contactFolders = $this->getContactFolders($store, $storeProps[PR_IPM_SUBTREE_ENTRYID], $storeProps[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID ? true : false);
0 ignored issues
show
Bug introduced by
$storeProps[PR_MDB_PROVI...LIC_GUID ? true : false of type boolean is incompatible with the type integer expected by parameter $depthSearch of MAPISession::getContactFolders(). ( Ignorable by Annotation )

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

1090
			$contactFolders = $this->getContactFolders($store, $storeProps[PR_IPM_SUBTREE_ENTRYID], /** @scrutinizer ignore-type */ $storeProps[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID ? true : false);
Loading history...
1091
		}
1092
		catch (Exception) {
1093
			return $contactFolders;
1094
		}
1095
1096
		// Need to search all the contact-subfolders within first level contact folders.
1097
		if ($storeProps[PR_MDB_PROVIDER] != ZARAFA_STORE_PUBLIC_GUID) {
1098
			$firstLevelHierarchyNodes = $contactFolders;
1099
			foreach ($firstLevelHierarchyNodes as $firstLevelNode) {
1100
				// To search for multiple levels CONVENIENT_DEPTH needs to be passed as well.
1101
				$contactFolders = array_merge($contactFolders, $this->getContactFolders($store, $firstLevelNode[PR_ENTRYID], true));
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type integer expected by parameter $depthSearch of MAPISession::getContactFolders(). ( Ignorable by Annotation )

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

1101
				$contactFolders = array_merge($contactFolders, $this->getContactFolders($store, $firstLevelNode[PR_ENTRYID], /** @scrutinizer ignore-type */ true));
Loading history...
1102
			}
1103
		}
1104
1105
		return $contactFolders;
1106
	}
1107
1108
	/**
1109
	 * Get the store entryid, folder entryid and display name of the contact folders from within given folder, in the
1110
	 * user's store. It provides an array where each item contains the information of a folder
1111
	 * formatted like this:
1112
	 * Array(
1113
	 *     PR_STORE_ENTRYID => '1234567890ABCDEF',
1114
	 *     PR_ENTRYID       => '1234567890ABCDEF',
1115
	 *     PR_DISPLAY_NAME  => 'Contact folder'
1116
	 * ).
1117
	 *
1118
	 * @param mapiStore $store         The mapi store of the user
1119
	 * @param string    $folderEntryid EntryID of the folder to look for contact folders in
1120
	 * @param int       $depthSearch   flag to search into all the folder levels
1121
	 *
1122
	 * @return array an array in which founded contact-folders will be pushed
1123
	 */
1124
	public function getContactFolders($store, $folderEntryid, $depthSearch) {
1125
		$restriction = [RES_CONTENT,
1126
			[
1127
				// Fuzzylevel PF_PREFIX also allows IPF.Contact.Custom folders to be included.
1128
				// Otherwise FL_FULLSTRING would only allow IPF.Contact folders.
1129
				FUZZYLEVEL => FL_PREFIX,
1130
				ULPROPTAG => PR_CONTAINER_CLASS,
1131
				VALUE => [
1132
					PR_CONTAINER_CLASS => "IPF.Contact",
1133
				],
1134
			],
1135
		];
1136
1137
		// Set necessary flag(s) to search considering all the sub folders or not
1138
		$depthFlag = MAPI_DEFERRED_ERRORS;
1139
		if ($depthSearch) {
1140
			$depthFlag |= CONVENIENT_DEPTH;
1141
		}
1142
1143
		$hierarchyFolder = mapi_msgstore_openentry($store, $folderEntryid);
1144
1145
		// Filter-out contact folders only
1146
		$contactFolderTable = mapi_folder_gethierarchytable($hierarchyFolder, $depthFlag);
1147
		mapi_table_restrict($contactFolderTable, $restriction, TBL_BATCH);
1148
1149
		return mapi_table_queryallrows($contactFolderTable, [PR_STORE_ENTRYID, PR_ENTRYID, PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_DEPTH]);
1150
	}
1151
1152
	/**
1153
	 * Obtains server version from the PR_EC_SERVER_VERSION property.
1154
	 */
1155
	public function getServerVersion() {
1156
		$props = mapi_getprops($this->getDefaultMessageStore(), [PR_EC_SERVER_VERSION]);
1157
		if (propIsError(PR_EC_SERVER_VERSION, $props) === MAPI_E_NOT_FOUND) {
1158
			return '';
1159
		}
1160
1161
		return $props[PR_EC_SERVER_VERSION];
1162
	}
1163
1164
	/**
1165
	 * Checks if the entryid is of the public store.
1166
	 *
1167
	 * @param string $entryid
1168
	 *
1169
	 * @return bool true if public store entryid false otherwise
1170
	 */
1171
	public function isPublicStore($entryid) {
1172
		return $GLOBALS["entryid"]->compareEntryIds(bin2hex($this->publicStore), $entryid);
1173
	}
1174
}
1175