Test Failed
Push — master ( 19e7e7...e85ccb )
by
unknown
07:48
created

MAPISession::getOtherUserStore()   C

Complexity

Conditions 15
Paths 70

Size

Total Lines 51
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 26
c 1
b 0
f 0
nc 70
nop 0
dl 0
loc 51
rs 5.9166

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
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 = isset($_SERVER['HTTP_USER_AGENT']) ? $_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($user_props[PR_EMS_AB_THUMBNAIL_PHOTO]) : "";
192
193
				$this->session_info["given_name"] = isset($user_props[PR_GIVEN_NAME]) ? $user_props[PR_GIVEN_NAME] : '';
194
				$this->session_info["initials"] = isset($user_props[PR_INITIALS]) ? $user_props[PR_INITIALS] : '';
195
				$this->session_info["surname"] = isset($user_props[PR_SURNAME]) ? $user_props[PR_SURNAME] : '';
196
				$this->session_info["street_address"] = isset($user_props[PR_STREET_ADDRESS]) ? $user_props[PR_STREET_ADDRESS] : '';
197
				$this->session_info["locality"] = isset($user_props[PR_LOCALITY]) ? $user_props[PR_LOCALITY] : '';
198
				$this->session_info["state_or_province"] = isset($user_props[PR_STATE_OR_PROVINCE]) ? $user_props[PR_STATE_OR_PROVINCE] : '';
199
				$this->session_info["postal_code"] = isset($user_props[PR_POSTAL_CODE]) ? $user_props[PR_POSTAL_CODE] : '';
200
				$this->session_info["country"] = isset($user_props[PR_COUNTRY]) ? $user_props[PR_COUNTRY] : '';
201
				$this->session_info["title"] = isset($user_props[PR_TITLE]) ? $user_props[PR_TITLE] : '';
202
				$this->session_info["company_name"] = isset($user_props[PR_COMPANY_NAME]) ? $user_props[PR_COMPANY_NAME] : '';
203
				$this->session_info["department_name"] = isset($user_props[PR_DEPARTMENT_NAME]) ? $user_props[PR_DEPARTMENT_NAME] : '';
204
				$this->session_info["office_location"] = isset($user_props[PR_OFFICE_LOCATION]) ? $user_props[PR_OFFICE_LOCATION] : '';
205
				$this->session_info["assistant"] = isset($user_props[PR_ASSISTANT]) ? $user_props[PR_ASSISTANT] : '';
206
				$this->session_info["assistant_telephone_number"] = isset($user_props[PR_ASSISTANT_TELEPHONE_NUMBER]) ? $user_props[PR_ASSISTANT_TELEPHONE_NUMBER] : '';
207
				$this->session_info["office_telephone_number"] = isset($user_props[PR_PRIMARY_TELEPHONE_NUMBER]) ? $user_props[PR_PRIMARY_TELEPHONE_NUMBER] : '';
208
				$this->session_info["business_telephone_number"] = isset($user_props[PR_BUSINESS_TELEPHONE_NUMBER]) ? $user_props[PR_BUSINESS_TELEPHONE_NUMBER] : '';
209
				$this->session_info["business2_telephone_number"] = isset($user_props[PR_BUSINESS2_TELEPHONE_NUMBER]) ? $user_props[PR_BUSINESS2_TELEPHONE_NUMBER] : '';
210
				$this->session_info["primary_fax_number"] = isset($user_props[PR_PRIMARY_FAX_NUMBER]) ? $user_props[PR_PRIMARY_FAX_NUMBER] : '';
211
				$this->session_info["home_telephone_number"] = isset($user_props[PR_HOME_TELEPHONE_NUMBER]) ? $user_props[PR_HOME_TELEPHONE_NUMBER] : '';
212
				$this->session_info["home2_telephone_number"] = isset($user_props[PR_HOME2_TELEPHONE_NUMBER]) ? $user_props[PR_HOME2_TELEPHONE_NUMBER] : '';
213
				$this->session_info["mobile_telephone_number"] = isset($user_props[PR_MOBILE_TELEPHONE_NUMBER]) ? $user_props[PR_MOBILE_TELEPHONE_NUMBER] : '';
214
				$this->session_info["pager_telephone_number"] = isset($user_props[PR_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
		return $this->session_info["userentryid"] ?? '';
324
	}
325
326
	/**
327
	 * Get current username.
328
	 *
329
	 * @return string Current user's username (equal to username passed in logon() )
330
	 */
331
	public function getUserName() {
332
		$encryptionStore = EncryptionStore::getInstance();
333
		return $encryptionStore->get('username') ? $encryptionStore->get('username') : '';
334
	}
335
336
	/**
337
	 * Get current user's full name.
338
	 *
339
	 * @return string User's full name
340
	 */
341
	public function getFullName() {
342
		$this->retrieveUserData();
343
		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...
344
	}
345
346
	/**
347
	 * Get current user's smtp address.
348
	 *
349
	 * @return string User's smtp address
350
	 */
351
	public function getSMTPAddress() {
352
		$this->retrieveUserData();
353
		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...
354
	}
355
356
	/**
357
	 * Get current user's email address.
358
	 *
359
	 * @return string User's email address
360
	 */
361
	public function getEmailAddress() {
362
		$this->retrieveUserData();
363
		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...
364
	}
365
366
	/**
367
	 * Get current user's image from the LDAP server.
368
	 *
369
	 * @return string A base64 encoded string (data url)
370
	 */
371
	public function getUserImage() {
372
		$this->retrieveUserData();
373
		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...
374
	}
375
376
	public function setUserImage($user_image) {
377
		if ($this->userDataRetrieved && is_array($this->session_info)) {
378
			$this->session_info["userimage"] = $user_image;
379
		}
380
	}
381
382
	public function getGivenName() {
383
		$this->retrieveUserData();
384
		return array_key_exists("given_name", $this->session_info) ? $this->session_info["given_name"] : false;
385
	}
386
387
	public function getInitials() {
388
		$this->retrieveUserData();
389
		return array_key_exists("initials", $this->session_info) ? $this->session_info["initials"] : false;
390
	}
391
392
	public function getSurname() {
393
		$this->retrieveUserData();
394
		return array_key_exists("surname", $this->session_info) ? $this->session_info["surname"] : false;
395
	}
396
397
	public function getStreetAddress() {
398
		$this->retrieveUserData();
399
		return array_key_exists("street_address", $this->session_info) ? $this->session_info["street_address"] : false;
400
	}
401
402
	public function getLocality() {
403
		$this->retrieveUserData();
404
		return array_key_exists("locality", $this->session_info) ? $this->session_info["locality"] : false;
405
	}
406
407
	public function getStateOrProvince() {
408
		$this->retrieveUserData();
409
		return array_key_exists("state_or_province", $this->session_info) ? $this->session_info["state_or_province"] : false;
410
	}
411
412
	public function getPostalCode() {
413
		$this->retrieveUserData();
414
		return array_key_exists("postal_code", $this->session_info) ? $this->session_info["postal_code"] : false;
415
	}
416
417
	public function getCountry() {
418
		$this->retrieveUserData();
419
		return array_key_exists("country", $this->session_info) ? $this->session_info["country"] : false;
420
	}
421
422
	public function getTitle() {
423
		$this->retrieveUserData();
424
		return array_key_exists("title", $this->session_info) ? $this->session_info["title"] : false;
425
	}
426
427
	public function getCompanyName() {
428
		$this->retrieveUserData();
429
		return array_key_exists("company_name", $this->session_info) ? $this->session_info["company_name"] : false;
430
	}
431
432
	public function getDepartmentName() {
433
		$this->retrieveUserData();
434
		return array_key_exists("department_name", $this->session_info) ? $this->session_info["department_name"] : false;
435
	}
436
437
	public function getOfficeLocation() {
438
		$this->retrieveUserData();
439
		return array_key_exists("office_location", $this->session_info) ? $this->session_info["office_location"] : false;
440
	}
441
442
	public function getAssistant() {
443
		$this->retrieveUserData();
444
		return array_key_exists("assistant", $this->session_info) ? $this->session_info["assistant"] : false;
445
	}
446
447
	public function getAssistantTelephoneNumber() {
448
		$this->retrieveUserData();
449
		return array_key_exists("assistant_telephone_number", $this->session_info) ? $this->session_info["assistant_telephone_number"] : false;
450
	}
451
452
	public function getOfficeTelephoneNumber() {
453
		$this->retrieveUserData();
454
		return array_key_exists("office_telephone_number", $this->session_info) ? $this->session_info["office_telephone_number"] : false;
455
	}
456
457
	public function getBusinessTelephoneNumber() {
458
		$this->retrieveUserData();
459
		return array_key_exists("business_telephone_number", $this->session_info) ? $this->session_info["business_telephone_number"] : false;
460
	}
461
462
	public function getBusiness2TelephoneNumber() {
463
		$this->retrieveUserData();
464
		return array_key_exists("business2_telephone_number", $this->session_info) ? $this->session_info["business2_telephone_number"] : false;
465
	}
466
467
	public function getPrimaryFaxNumber() {
468
		$this->retrieveUserData();
469
		return array_key_exists("primary_fax_number", $this->session_info) ? $this->session_info["primary_fax_number"] : false;
470
	}
471
472
	public function getHomeTelephoneNumber() {
473
		$this->retrieveUserData();
474
		return array_key_exists("home_telephone_number", $this->session_info) ? $this->session_info["home_telephone_number"] : false;
475
	}
476
477
	public function getHome2TelephoneNumber() {
478
		$this->retrieveUserData();
479
		return array_key_exists("home2_telephone_number", $this->session_info) ? $this->session_info["home2_telephone_number"] : false;
480
	}
481
482
	public function getMobileTelephoneNumber() {
483
		$this->retrieveUserData();
484
		return array_key_exists("mobile_telephone_number", $this->session_info) ? $this->session_info["mobile_telephone_number"] : false;
485
	}
486
487
	public function getPagerTelephoneNumber() {
488
		$this->retrieveUserData();
489
		return array_key_exists("pager_telephone_number", $this->session_info) ? $this->session_info["pager_telephone_number"] : false;
490
	}
491
492
	/**
493
	 * Checks whether the user is enabled for grommunio-web.
494
	 *
495
	 * @return bool
496
	 */
497
	public function isGwebEnabled() {
498
		$store_props = mapi_getprops($this->getDefaultMessageStore(), [PR_EC_ENABLED_FEATURES_L]);
499
		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...
500
	}
501
502
	/**
503
	 * @return bool true if webapp is disabled feature else return false
504
	 */
505
	public function isWebappDisableAsFeature() {
506
		return !$this->isGwebEnabled();
507
	}
508
509
	/**
510
	 * Magic method to get properties from the session_info. When a method of this class if called
511
	 * and there is no method of this name defined this function will be called
512
	 * It creates getter methods for the properties stored in $session_info using the following syntax:
513
	 * getSomeUserProperty() will look return a property called some_user_property if it exists and
514
	 * throw an exception otherwise.
515
	 *
516
	 * @param string $methodName The name of the method that was called
517
	 * @param array  $arguments  The arguments that were passed in the call
518
	 *
519
	 * @return string The requested property if it exists
520
	 *
521
	 * @throws Exception
522
	 */
523
	public function __call($methodName, $arguments) {
524
		if (!preg_match('/^get(.+)$/', $methodName, $matches)) {
525
			// We don't know this function, so let's throw an error
526
			throw new Exception('Method ' . $methodName . ' does not exist');
527
		}
528
		$this->retrieveUserData();
529
		$propertyName = strtolower(preg_replace('/([^A-Z])([A-Z])/', '$1_$2', $matches[1]));
530
		if (!array_key_exists($propertyName, $this->session_info)) {
531
			// We don't know this function, so let's throw an error
532
			throw new Exception('Method ' . $methodName . ' does not exist ' . $propertyName);
533
		}
534
535
		return $this->session_info[$propertyName];
536
	}
537
538
	/**
539
	 * Returns a hash with information about the user that is logged in.
540
	 *
541
	 * @return array
542
	 */
543
	public function getUserInfo() {
544
		return [
545
			'username' => $this->getUserName(),
546
			'fullname' => $this->getFullName(),
547
			'entryid' => bin2hex($this->getUserEntryid()),
548
			'email_address' => $this->getEmailAddress(),
549
			'smtp_address' => $this->getSMTPAddress(),
550
			'search_key' => bin2hex($this->getSearchKey()),
551
			'user_image' => $this->getUserImage(),
552
			'given_name' => $this->getGivenName(),
553
			'initials' => $this->getInitials(),
554
			'surname' => $this->getSurname(),
555
			'street_address' => $this->getStreetAddress(),
556
			'locality' => $this->getLocality(),
557
			'state_or_province' => $this->getStateOrProvince(),
558
			'postal_code' => $this->getPostalCode(),
559
			'country' => $this->getCountry(),
560
			'title' => $this->getTitle(),
561
			'company_name' => $this->getCompanyName(),
562
			'department_name' => $this->getDepartmentName(),
563
			'office_location' => $this->getOfficeLocation(),
564
			'assistant' => $this->getAssistant(),
565
			'assistant_telephone_number' => $this->getAssistantTelephoneNumber(),
566
			'office_telephone_number' => $this->getOfficeTelephoneNumber(),
567
			'business_telephone_number' => $this->getBusinessTelephoneNumber(),
568
			'business2_telephone_number' => $this->getBusiness2TelephoneNumber(),
569
			'primary_fax_number' => $this->getPrimaryFaxNumber(),
570
			'home_telephone_number' => $this->getHomeTelephoneNumber(),
571
			'home2_telephone_number' => $this->getHome2TelephoneNumber(),
572
			'mobile_telephone_number' => $this->getMobileTelephoneNumber(),
573
			'pager_telephone_number' => $this->getPagerTelephoneNumber(),
574
		];
575
	}
576
577
	/**
578
	 * Get current user's search key.
579
	 *
580
	 * @return string Current user's searchkey
581
	 */
582
	public function getSearchKey() {
583
		$this->retrieveUserData();
584
		return $this->session_info["searchkey"] ?? '';
585
	}
586
587
	/**
588
	 * Get the message stores from the message store table from your session. Standard stores
589
	 * like the default store and the public store are made them easily accessible through the
590
	 * defaultstore and publicStore properties.
591
	 */
592
	public function loadMessageStoresFromSession() {
593
		$storestables = mapi_getmsgstorestable($this->session);
594
		$rows = mapi_table_queryallrows($storestables, [PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER]);
595
		foreach ($rows as $row) {
596
			if (!$row[PR_ENTRYID]) {
597
				continue;
598
			}
599
600
			if (isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE] == true) {
601
				$this->defaultstore = $row[PR_ENTRYID];
602
			}
603
			elseif ($row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
604
				$this->publicStore = $row[PR_ENTRYID];
605
			}
606
			elseif ($row[PR_MDB_PROVIDER] == ZARAFA_STORE_DELEGATE_GUID) {
607
				$eidObj = $GLOBALS["entryid"]->createMsgStoreEntryIdObj($row[PR_ENTRYID]);
608
				if (isset($eidObj['MailboxDN'])) {
609
					$this->openMessageStore($row[PR_ENTRYID], strtolower($eidObj['MailboxDN']));
610
				}
611
			}
612
		}
613
	}
614
615
	/**
616
	 * Get the current user's default message store.
617
	 *
618
	 * The store is opened only once, subsequent calls will return the previous store object
619
	 *
620
	 * @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...
621
	 * @param mixed $reopen
622
	 *
623
	 * @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...
624
	 */
625
	public function getDefaultMessageStore($reopen = false) {
626
		// Return cached default store if we have one
627
		if (!$reopen && isset($this->defaultstore, $this->stores[$this->defaultstore])) {
628
			return $this->stores[$this->defaultstore];
629
		}
630
631
		$this->loadMessageStoresFromSession();
632
633
		return $this->openMessageStore($this->defaultstore, 'Default store');
634
	}
635
636
	/**
637
	 * The default messagestore entryid.
638
	 *
639
	 * @return string the entryid of the default messagestore
640
	 */
641
	public function getDefaultMessageStoreEntryId() {
642
		if (!isset($this->defaultstore)) {
643
			$this->loadMessageStoresFromSession();
644
		}
645
646
		return bin2hex($this->defaultstore);
647
	}
648
649
	/**
650
	 * Get single store if we are opening full store.
651
	 *
652
	 * @param object $store        the store of the user
653
	 * @param array  $storeOptions contains folder_type of which folder to open
654
	 *                             It is mapped to username, If folder_type is 'all' (i.e. Open Entire Inbox)
655
	 *                             then we will open full store.
656
	 * @param string $username     The username
657
	 *
658
	 * @return array storeArray The array of stores containing user's store
659
	 */
660
	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

660
	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

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

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

1060
			$contactFolders = $this->getContactFolders($store, $storeProps[PR_IPM_SUBTREE_ENTRYID], /** @scrutinizer ignore-type */ $storeProps[PR_MDB_PROVIDER] === ZARAFA_STORE_PUBLIC_GUID ? true : false);
Loading history...
1061
		}
1062
		catch (Exception $e) {
1063
			return $contactFolders;
1064
		}
1065
1066
		// Need to search all the contact-subfolders within first level contact folders.
1067
		if ($storeProps[PR_MDB_PROVIDER] != ZARAFA_STORE_PUBLIC_GUID) {
1068
			$firstLevelHierarchyNodes = $contactFolders;
1069
			foreach ($firstLevelHierarchyNodes as $firstLevelNode) {
1070
				// To search for multiple levels CONVENIENT_DEPTH needs to be passed as well.
1071
				$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

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