Passed
Push — master ( 367bbb...b6e19f )
by
unknown
07:38
created

getContactFoldersForABContactProvider()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 22
rs 9.6111
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 (Exception $e) {
754
			// mapi_openmsgstore seems to not only throw MAPIException
755
			error_log(sprintf("openmsgstore %s failed (actor:%s, name:%s): %s",
756
				bin2hex($entryid), $this->session_info["username"], $name,
757
				$e->getDisplayMessage()));
0 ignored issues
show
Bug introduced by
The method getDisplayMessage() does not exist on Exception. It seems like you code against a sub-type of Exception such as BaseException. ( Ignorable by Annotation )

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

757
				$e->/** @scrutinizer ignore-call */ 
758
        getDisplayMessage()));
Loading history...
758
			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...
759
		}
760
761
		return $store;
762
	}
763
764
	/**
765
	 * Get all the available shared stores.
766
	 *
767
	 * The store is opened only once, subsequent calls will return the previous store object
768
	 */
769
	public function getOtherUserStore() {
770
		$otherusers = $this->retrieveOtherUsersFromSettings();
771
		$otherUsersStores = [];
772
773
		foreach ($otherusers as $username => $folder) {
774
			if (isset($this->userstores[$username])) {
775
				continue;
776
			}
777
			$storeOk = true;
778
779
			if (is_array($folder) && !empty($folder)) {
780
				try {
781
					$user_entryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username);
782
783
					$sharedStore = $this->openMessageStore($user_entryid, $username);
784
					if ($sharedStore === false && $sharedStore === ecLoginPerm &&
785
						$sharedStore === MAPI_E_CALL_FAILED && $sharedStore === MAPI_E_NOT_FOUND) {
786
						$storeOk = false;
787
					}
788
				}
789
				catch (MAPIException $e) {
790
					if ($e->getCode() == MAPI_E_NOT_FOUND) {
791
						// The user or the corresponding store couldn't be found,
792
						// print an error to the log, and remove the user from the settings.
793
						dump('Failed to load store for user ' . $username . ', user was not found. Removing it from settings.');
794
						$GLOBALS["settings"]->delete("zarafa/v1/contexts/hierarchy/shared_stores/" . bin2hex($username), true);
795
					}
796
					else {
797
						// That is odd, something else went wrong. Lets not be hasty and preserve
798
						// the user in the settings, but do print something to the log to indicate
799
						// something happened...
800
						dump('Failed to load store for user ' . $username . '. ' . $e->getDisplayMessage());
801
					}
802
				}
803
				finally {
804
					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...
805
						// The user or the corresponding store couldn't be opened
806
						// (e.g. the user was deleted or permissions revoked),
807
						// print an error to the log, and remove the user from the settings.
808
						dump(sprintf("The user %s failed to load store of the user %s. Removing it from settings.", $this->session_info["username"], $username));
809
						$GLOBALS["settings"]->delete("zarafa/v1/contexts/hierarchy/shared_stores/" . bin2hex($username), true);
810
					}
811
				}
812
			}
813
		}
814
815
		foreach ($this->userstores as $entryid) {
816
			$otherUsersStores[$entryid] = $this->stores[$entryid];
817
		}
818
819
		return $otherUsersStores;
820
	}
821
822
	/**
823
	 * Resolve the username strictly by opening that user's store and returning the
824
	 * PR_MAILBOX_OWNER_ENTRYID. This can be used for resolving an username without the risk of
825
	 * ambiguity since mapi_ab_resolve() does not strictly resolve on the username.
826
	 *
827
	 * @param string $username The username
828
	 *
829
	 * @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...
830
	 */
831
	public function resolveStrictUserName($username) {
832
		$storeEntryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username);
833
		$store = $this->openMessageStore($storeEntryid, $username);
834
		$storeProps = mapi_getprops($store, [PR_MAILBOX_OWNER_ENTRYID]);
835
836
		return $storeProps[PR_MAILBOX_OWNER_ENTRYID];
837
	}
838
839
	/**
840
	 * Get other users from settings.
841
	 *
842
	 * @return array Array of usernames of delegate stores
843
	 */
844
	public function retrieveOtherUsersFromSettings() {
845
		$other_users = $GLOBALS["settings"]->get("zarafa/v1/contexts/hierarchy/shared_stores", []);
846
		$result = [];
847
		foreach ($other_users as $username => $folders) {
848
			// No folders are being shared, the store has probably been closed by the user,
849
			// but the username is still lingering in the settings...
850
			if (!isset($folders) || empty($folders)) {
851
				continue;
852
			}
853
854
			$username = strtolower(hex2bin((string) $username));
855
			if (!isset($result[$username])) {
856
				$result[$username] = [];
857
			}
858
859
			foreach ($folders as $folder) {
860
				if (is_array($folder)) {
861
					$result[$username][$folder["folder_type"]] = [];
862
					$result[$username][$folder["folder_type"]]["folder_type"] = $folder["folder_type"];
863
					$result[$username][$folder["folder_type"]]["show_subfolders"] = $folder["show_subfolders"];
864
				}
865
			}
866
		}
867
868
		return $result;
869
	}
870
871
	/**
872
	 * Add the store of another user to the list of other user stores.
873
	 *
874
	 * @param string $username The username whose store should be added to the list of other users' stores
875
	 *
876
	 * @return mapistore The store of the user or false on error;
877
	 */
878
	public function addUserStore($username) {
879
		$user_entryid = mapi_msgstore_createentryid($this->getDefaultMessageStore(), $username);
880
881
		if ($user_entryid) {
882
			// mapi_msgstore_createentryid and mapi_getprops(PR_ENTRYID) have different
883
			// values for shared stores, so save the one from mapi_getprops(PR_ENTRYID)
884
			// $this->userstores[$username] = $user_entryid;
885
886
			return $this->openMessageStore($user_entryid, $username);
887
		}
888
	}
889
890
	/**
891
	 * Remove the store of another user from the list of other user stores.
892
	 *
893
	 * @param string $username The username whose store should be deleted from the list of other users' stores
894
	 *
895
	 * @return string The entryid of the store which was removed
896
	 */
897
	public function removeUserStore($username) {
898
		// Remove the reference to the store if we had one
899
		if (isset($this->userstores[$username])) {
900
			$entryid = $this->userstores[$username];
901
			unset($this->userstores[$username], $this->stores[$entryid]);
902
903
			return $entryid;
904
		}
905
	}
906
907
	/**
908
	 * Get the store entryid of the specified user.
909
	 *
910
	 * The store must have been previously added via addUserStores.
911
	 *
912
	 * @param string $username The username whose store is being looked up
913
	 *
914
	 * @return string The entryid of the store of the user
915
	 */
916
	public function getStoreEntryIdOfUser($username) {
917
		return $this->userstores[$username];
918
	}
919
920
	/**
921
	 * Get the username of the user store.
922
	 *
923
	 * @param string $username the loginname of whom we want to full name
924
	 *
925
	 * @return string the display name of the user
926
	 */
927
	public function getDisplayNameofUser($username) {
928
		$user_entryid = $this->getStoreEntryIdOfUser($username);
929
		$store = $this->openMessageStore($user_entryid, $username);
930
		$props = mapi_getprops($store, [PR_DISPLAY_NAME]);
931
932
		return str_replace('Inbox - ', '', $props[PR_DISPLAY_NAME]);
933
	}
934
935
	/**
936
	 * Get the username of the owner of the specified store.
937
	 *
938
	 * The store must have been previously added via addUserStores.
939
	 *
940
	 * @param string $entryid EntryID of the store
941
	 *
942
	 * @return string Username of the specified store or false if it is not found
943
	 */
944
	public function getUserNameOfStore($entryid) {
945
		foreach ($this->userstores as $username => $storeentryid) {
946
			if ($GLOBALS["entryid"]->compareEntryIds(bin2hex((string) $storeentryid), bin2hex($entryid))) {
947
				return $username;
948
			}
949
		}
950
951
		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...
952
	}
953
954
	/**
955
	 * Open a MAPI message using session object.
956
	 * The function is used to open message when we don't know
957
	 * the specific store and we want to open message using entryid.
958
	 *
959
	 * @param string $entryid entryid of the message
960
	 *
961
	 * @return object MAPI Message
962
	 */
963
	public function openMessage($entryid) {
964
		return mapi_openentry($this->session, $entryid);
965
	}
966
967
	/**
968
	 * Setup the contact provider for the addressbook. It asks getContactFoldersForABContactProvider
969
	 * for the entryids and display names for the contact folders in the user's store.
970
	 *
971
	 * @param bool $loadSharedContactsProvider when set to true it denotes that shared folders are
972
	 *                                         required to be configured to load the contacts from
973
	 */
974
	public function setupContactProviderAddressbook($loadSharedContactsProvider) {
975
		$profsect = mapi_openprofilesection($GLOBALS['mapisession']->getSession(), pbGlobalProfileSectionGuid);
976
		if ($profsect) {
977
			// Get information about all contact folders from own store, shared stores and public store
978
			$defaultStore = $this->getDefaultMessageStore();
979
			$contactFolders = $this->getContactFoldersForABContactProvider($defaultStore);
980
981
			// include shared contact folders in addressbook if shared contact folders are enabled
982
			if (ENABLE_SHARED_CONTACT_FOLDERS && $loadSharedContactsProvider) {
983
				if (empty($this->userstores)) {
984
					$this->getOtherUserStore();
985
				}
986
987
				$sharedSetting = $GLOBALS["settings"]->get("zarafa/v1/contexts/hierarchy/shared_stores", []);
988
				// Find available contact folders from all user stores, one by one.
989
				foreach ($this->userstores as $username => $storeEntryID) {
990
					$userContactFolders = [];
991
					$sharedUserSetting = [];
992
					$openedUserStore = $this->openMessageStore($storeEntryID, $username);
993
994
					// Get settings of respective shared folder of given user
995
					if (array_key_exists(strtolower(bin2hex($username)), $sharedSetting)) {
996
						$sharedUserSetting = $sharedSetting[strtolower(bin2hex($username))];
997
					}
998
999
					// Only add opened shared folders into addressbook contacts provider.
1000
					// If entire inbox is opened then add each and every contact folders of that particular user.
1001
					if (isset($sharedUserSetting['all'])) {
1002
						$userContactFolders = $this->getContactFoldersForABContactProvider($openedUserStore);
1003
					}
1004
					elseif (isset($sharedUserSetting['contact'])) {
1005
						// Add respective default contact folder which is opened.
1006
						// Get entryid of default contact folder from root.
1007
						$root = mapi_msgstore_openentry($openedUserStore);
1008
						$rootProps = mapi_getprops($root, [PR_IPM_CONTACT_ENTRYID]);
1009
1010
						// Just add the default contact folder only.
1011
						$defaultContactFolder = [
1012
							PR_STORE_ENTRYID => $storeEntryID,
1013
							PR_ENTRYID => $rootProps[PR_IPM_CONTACT_ENTRYID],
1014
							PR_DISPLAY_NAME => _("Contacts"),
1015
						];
1016
						array_push($userContactFolders, $defaultContactFolder);
1017
1018
						// Go for sub folders only if configured in settings
1019
						if ($sharedUserSetting['contact']['show_subfolders'] == true) {
1020
							$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

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

1083
			$contactFolders = $this->getContactFolders($store, $storeProps[PR_IPM_SUBTREE_ENTRYID], /** @scrutinizer ignore-type */ $storeProps[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID ? true : false);
Loading history...
1084
		}
1085
		catch (Exception) {
1086
			return $contactFolders;
1087
		}
1088
1089
		// Need to search all the contact-subfolders within first level contact folders.
1090
		if ($storeProps[PR_MDB_PROVIDER] != ZARAFA_STORE_PUBLIC_GUID) {
1091
			$firstLevelHierarchyNodes = $contactFolders;
1092
			foreach ($firstLevelHierarchyNodes as $firstLevelNode) {
1093
				// To search for multiple levels CONVENIENT_DEPTH needs to be passed as well.
1094
				$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

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