Passed
Push — master ( 25f737...dd5bad )
by
unknown
08:43 queued 02:22
created

ResolveNamesModule::searchAddressBook()   F

Complexity

Conditions 23
Paths 160

Size

Total Lines 128
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 23
eloc 71
c 2
b 1
f 0
nc 160
nop 4
dl 0
loc 128
rs 3.6666

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * ResolveNames Module.
5
 */
6
class ResolveNamesModule extends Module {
7
	/**
8
	 * Constructor.
9
	 *
10
	 * @param mixed $id
11
	 * @param mixed $data
12
	 */
13
	public function __construct($id, $data) {
14
		parent::__construct($id, $data);
15
	}
16
17
	/**
18
	 * Executes all the actions in the $data variable.
19
	 */
20
	#[Override]
21
	public function execute() {
22
		foreach ($this->data as $actionType => $action) {
23
			if (isset($actionType)) {
24
				try {
25
					match ($actionType) {
26
						'checknames' => $this->checkNames($action),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->checkNames($action) targeting ResolveNamesModule::checkNames() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
27
						default => $this->handleUnknownActionType($actionType),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->handleUnknownActionType($actionType) targeting Module::handleUnknownActionType() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
28
					};
29
				}
30
				catch (MAPIException $e) {
31
					$this->processException($e, $actionType);
32
				}
33
			}
34
		}
35
	}
36
37
	/**
38
	 * Function which checks the names, sent by the client. This function is used
39
	 * when a user wants to sent an email and want to check the names filled in
40
	 * by the user in the to, cc and bcc field. This function uses the global
41
	 * user list to check if the names are correct.
42
	 *
43
	 * @param array $action the action data, sent by the client
44
	 */
45
	public function checkNames($action) {
46
		if (isset($action['resolverequests'])) {
47
			$data = [];
48
			$excludeLocalContacts = $action['exclude_local_contacts'] ?? false;
49
			$excludeGABGroups = $action['exclude_gab_groups'] ?? false;
50
			$resolveRequest = $action['resolverequests'];
51
			if (!is_array($resolveRequest)) {
52
				$resolveRequest = [$resolveRequest];
53
			}
54
55
			// open addressbook
56
			// When local contacts need to be excluded we have pass true as the first argument
57
			// so we will not have any Contact Providers set on the Addressbook resource.
58
			$ab = $GLOBALS['mapisession']->getAddressbook($excludeLocalContacts);
59
60
			$ab_dir = mapi_ab_openentry($ab);
61
			$resolveResponse = [];
62
63
			// check names
64
			foreach ($resolveRequest as $query) {
65
				if (is_array($query) && isset($query['id'])) {
66
					$responseEntry = [];
67
					$responseEntry['id'] = $query['id'];
68
69
					if (!empty($query['display_name']) || !empty($query['email_address'])) {
70
						$responseEntry['result'] = $this->searchAddressBook($ab, $ab_dir, $query, $excludeGABGroups);
71
						$resolveResponse[] = $responseEntry;
72
					}
73
				}
74
			}
75
76
			$data['resolveresponse'] = $resolveResponse;
77
78
			$this->addActionData('checknames', $data);
79
			$GLOBALS['bus']->addData($this->getResponseData());
80
		}
81
	}
82
83
	/**
84
	 * This function searches the addressbook specified for users and returns an array with data
85
	 * Please note that the returning array must be UTF8.
86
	 *
87
	 * @param resource $ab               The addressbook
88
	 * @param resource $ab_dir           The addressbook container
89
	 * @param string   $query            The search query, case is ignored
90
	 * @param bool     $excludeGABGroups flag to exclude groups from resolving
91
	 */
92
	public function searchAddressBook($ab, $ab_dir, $query, $excludeGABGroups) {
93
		// Prefer resolving the email_address. This allows the user
94
		// to resolve recipients with a display name that matches a EX
95
		// user with an alternative (external) email address.
96
		$searchstr = empty($query['email_address']) ? $query['display_name'] : $query['email_address'];
97
		// If the address_type is 'EX' then we are resolving something which must be found in
98
		// the GAB as an exact match. So add the flag EMS_AB_ADDRESS_LOOKUP to ensure we will not
99
		// get multiple results when multiple items have a partial match.
100
		$flags = $query['address_type'] === 'EX' ? EMS_AB_ADDRESS_LOOKUP : 0;
101
102
		try {
103
			// First, try an addressbook lookup
104
			$rows = mapi_ab_resolvename($ab, [[PR_DISPLAY_NAME => $searchstr]], $flags);
105
			$this->searchContactsFolders($ab, $ab_dir, $searchstr, $rows);
106
		}
107
		catch (MAPIException $e) {
108
			if ($e->getCode() == MAPI_E_AMBIGUOUS_RECIP) {
109
				$ab_entryid = mapi_ab_getdefaultdir($ab);
110
				$ab_dir = mapi_ab_openentry($ab, $ab_entryid);
111
				// Ambiguous, show possibilities:
112
				$table = mapi_folder_getcontentstable($ab_dir, MAPI_DEFERRED_ERRORS);
113
				$restriction = $this->getAmbigiousContactRestriction($searchstr, $excludeGABGroups, PR_ACCOUNT);
114
115
				mapi_table_restrict($table, $restriction, TBL_BATCH);
116
				mapi_table_sort($table, [PR_DISPLAY_NAME => TABLE_SORT_ASCEND], TBL_BATCH);
117
118
				$rows = mapi_table_queryallrows($table, [PR_ACCOUNT, PR_ADDRTYPE, PR_DISPLAY_NAME, PR_ENTRYID, PR_SEARCH_KEY, PR_OBJECT_TYPE, PR_SMTP_ADDRESS, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE]);
119
120
				$rows = array_merge($rows, $this->getAmbigiousContactResolveResults($ab, $searchstr, $excludeGABGroups));
121
			}
122
			elseif ($e->getCode() == MAPI_E_NOT_FOUND) {
123
				$rows = [];
124
125
				if (defined('ENABLE_RESOLVE_HIDDEN_USERS') && ENABLE_RESOLVE_HIDDEN_USERS) {
126
					$rows = $this->resolveHiddenExactMatches($ab, $query);
0 ignored issues
show
Bug introduced by
$query of type string is incompatible with the type array expected by parameter $query of ResolveNamesModule::resolveHiddenExactMatches(). ( Ignorable by Annotation )

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

126
					$rows = $this->resolveHiddenExactMatches($ab, /** @scrutinizer ignore-type */ $query);
Loading history...
127
				}
128
129
				if (empty($rows) && (($query['address_type'] ?? '') === 'SMTP')) {
130
					// If we still can't find anything, and we were searching for a SMTP user
131
					// we can generate a oneoff entry which contains the information of the user.
132
					if (!empty($query['email_address'])) {
133
						$rows[] = [
134
							PR_ACCOUNT => $query['email_address'], PR_ADDRTYPE => 'SMTP', PR_EMAIL_ADDRESS => $query['email_address'],
135
							PR_DISPLAY_NAME => $query['display_name'], PR_DISPLAY_TYPE_EX => DT_REMOTE_MAILUSER, PR_DISPLAY_TYPE => DT_MAILUSER,
136
							PR_SMTP_ADDRESS => $query['email_address'], PR_OBJECT_TYPE => MAPI_MAILUSER,
137
							PR_ENTRYID => mapi_createoneoff($query['display_name'], 'SMTP', $query['email_address']),
138
						];
139
					}
140
					// Check also the user's contacts folders
141
					else {
142
						$this->searchContactsFolders($ab, $ab_dir, $searchstr, $rows);
143
					}
144
				}
145
				elseif (empty($rows)) {
146
					$this->searchContactsFolders($ab, $ab_dir, $searchstr, $rows);
147
				}
148
			}
149
			else {
150
				// all other errors should be propagated to higher level exception handlers
151
				throw $e;
152
			}
153
		}
154
155
		$items = [];
156
		if ($rows) {
157
			foreach ($rows as $user_data) {
158
				$item = [];
159
160
				if (!isset($user_data[PR_ACCOUNT])) {
161
					$abitem = mapi_ab_openentry($ab, $user_data[PR_ENTRYID]);
162
					$user_data = mapi_getprops($abitem, [PR_ACCOUNT, PR_ADDRTYPE, PR_DISPLAY_NAME, PR_DISPLAY_TYPE_EX, PR_ENTRYID, PR_SEARCH_KEY, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE]);
163
				}
164
165
				if ($excludeGABGroups && $user_data[PR_OBJECT_TYPE] === MAPI_DISTLIST) {
166
					// exclude groups from result
167
					continue;
168
				}
169
170
				$item = [];
171
				$item['object_type'] = $user_data[PR_OBJECT_TYPE] ?? MAPI_MAILUSER;
172
				$item['entryid'] = isset($user_data[PR_ENTRYID]) ? bin2hex($user_data[PR_ENTRYID]) : '';
173
				$item['display_name'] = $user_data[PR_DISPLAY_NAME] ?? '';
174
				$item['display_type'] = $user_data[PR_DISPLAY_TYPE] ?? DT_MAILUSER;
175
176
				// Test whether the GUID in the entryid is from the Contact Provider
177
				if ($GLOBALS['entryid']->hasContactProviderGUID($item['entryid'])) {
178
					// The properties for a Distribution List differs from the other objects
179
					if ($item['object_type'] == MAPI_DISTLIST) {
180
						$item['address_type'] = 'MAPIPDL';
181
						// The email_address is empty for DistList, using display name for resolving
182
						$item['email_address'] = $item['display_name'];
183
						$item['smtp_address'] ??= '';
184
					}
185
					else {
186
						$item['address_type'] = $user_data[PR_ADDRTYPE] ?? 'SMTP';
187
						if (isset($user_data['address_type']) && $user_data['address_type'] === 'EX') {
188
							$item['email_address'] = $user_data[PR_EMAIL_ADDRESS] ?? '';
189
						}
190
						else {
191
							// Fake being an EX account, since it's actually an SMTP addrtype the email address is in a different property.
192
							$item['smtp_address'] = $user_data[PR_EMAIL_ADDRESS] ?? '';
193
							// Keep the old scenario happy.
194
							$item['email_address'] = $user_data[PR_EMAIL_ADDRESS] ?? '';
195
						}
196
					}
197
				// It can be considered a GAB entry
198
				}
199
				else {
200
					$item['user_name'] = $user_data[PR_ACCOUNT] ?? $item['display_name'];
201
					$item['display_type_ex'] = $user_data[PR_DISPLAY_TYPE_EX] ?? MAPI_MAILUSER;
202
					$item['email_address'] = $user_data[PR_EMAIL_ADDRESS] ?? '';
203
					$item['smtp_address'] = $user_data[PR_SMTP_ADDRESS] ?? $item['email_address'];
204
					$item['address_type'] = $user_data[PR_ADDRTYPE] ?? 'SMTP';
205
				}
206
207
				if (isset($user_data[PR_SEARCH_KEY])) {
208
					$item['search_key'] = bin2hex($user_data[PR_SEARCH_KEY]);
209
				}
210
				else {
211
					$emailAddress = $item['smtp_address'] ?? $item['email_address'];
212
					$item['search_key'] = bin2hex(strtoupper($item['address_type'] . ':' . $emailAddress)) . '00';
213
				}
214
215
				array_push($items, $item);
216
			}
217
		}
218
219
		return $items;
220
	}
221
222
	/**
223
	 * Try to resolve a hidden user by matching the provided query exactly against
224
	 * common address book properties. Hidden entries are not returned in regular
225
	 * searches, but the EMS_AB_ADDRESS_LOOKUP flag allows us to perform an exact
226
	 * lookup that also returns hidden entries.
227
	 *
228
	 * @param resource $ab    The addressbook
229
	 * @param array    $query Resolve query sent by the client
230
	 *
231
	 * @return array
232
	 */
233
	protected function resolveHiddenExactMatches($ab, $query) {
234
		if (!defined('ENABLE_RESOLVE_HIDDEN_USERS') || !ENABLE_RESOLVE_HIDDEN_USERS) {
235
			return [];
236
		}
237
238
		$candidates = [];
239
240
		if (!empty($query['display_name'])) {
241
			$candidates[] = trim((string) $query['display_name']);
242
		}
243
244
		if (!empty($query['email_address'])) {
245
			$candidates[] = trim((string) $query['email_address']);
246
		}
247
248
		$candidates = array_values(array_unique(array_filter($candidates, 'strlen')));
249
		$addressType = strtoupper((string) ($query['address_type'] ?? ''));
250
251
		foreach ($candidates as $candidate) {
252
			$lookups = [PR_DISPLAY_NAME];
253
254
			if ($addressType === 'EX') {
255
				$lookups[] = PR_ACCOUNT;
256
			}
257
258
			if (str_contains($candidate, '@')) {
259
				$lookups[] = PR_EMAIL_ADDRESS;
260
				$lookups[] = PR_SMTP_ADDRESS;
261
262
				if ($addressType !== 'EX') {
263
					$lookups[] = PR_ACCOUNT;
264
				}
265
			}
266
267
			foreach (array_unique($lookups) as $property) {
268
				$rows = $this->resolveNameByProperty($ab, $property, $candidate);
269
				if (!empty($rows)) {
270
					return $rows;
271
				}
272
			}
273
		}
274
275
		return [];
276
	}
277
278
	/**
279
	 * Execute an exact resolve against the GAB for the provided property.
280
	 *
281
	 * @param resource $ab       The addressbook
282
	 * @param int      $property Property tag used for the lookup
283
	 * @param string   $value    Search value
284
	 *
285
	 * @return array
286
	 */
287
	protected function resolveNameByProperty($ab, $property, $value) {
288
		if ($value === '') {
289
			return [];
290
		}
291
292
		try {
293
			return mapi_ab_resolvename($ab, [[$property => $value]], EMS_AB_ADDRESS_LOOKUP);
294
		}
295
		catch (MAPIException $e) {
296
			if ($e->getCode() == MAPI_E_NOT_FOUND || $e->getCode() == MAPI_E_AMBIGUOUS_RECIP) {
297
				return [];
298
			}
299
300
			throw $e;
301
		}
302
	}
303
304
	/**
305
	 * Used to find multiple entries from the contact folders in the Addressbook when resolving
306
	 * returned an ambiguous result. It will find the Contact folders in the Addressbook and
307
	 * apply a restriction to extract the entries.
308
	 *
309
	 * @param resource $ab               The addressbook
310
	 * @param string   $query            The search query, case is ignored
311
	 * @param bool     $excludeGABGroups flag to exclude groups from resolving
312
	 */
313
	public function getAmbigiousContactResolveResults($ab, $query, $excludeGABGroups) {
314
		/* We need to look for the Contact folders at the bottom of the following tree.
315
		*
316
		 * IAddrBook
317
		 *  - Root Container
318
		 *     - HIERARCHY TABLE
319
		 *        - Contacts Folders    (Contact Container)
320
		 *           - HIERARCHY TABLE         (Contact Container Hierarchy)
321
		 *              - Contact folder 1
322
		 *              - Contact folder 2
323
		 */
324
325
		$rows = [];
326
		$contactFolderRestriction = $this->getAmbigiousContactRestriction($query, $excludeGABGroups, PR_EMAIL_ADDRESS);
327
		// Open the AB Root Container by not supplying an entryid
328
		$abRootContainer = mapi_ab_openentry($ab);
329
330
		// Get the 'Contact Folders'
331
		$hierarchyTable = mapi_folder_gethierarchytable($abRootContainer, MAPI_DEFERRED_ERRORS);
332
		$abHierarchyRows = mapi_table_queryallrows($hierarchyTable, [PR_AB_PROVIDER_ID, PR_ENTRYID]);
333
334
		// Look for the 'Contacts Folders'
335
		for ($i = 0,$len = count($abHierarchyRows); $i < $len; ++$i) {
336
			// Check if the folder matches the Contact Provider GUID
337
			if ($abHierarchyRows[$i][PR_AB_PROVIDER_ID] == MUIDZCSAB) {
338
				$abContactContainerEntryid = $abHierarchyRows[$i][PR_ENTRYID];
339
				break;
340
			}
341
		}
342
343
		// Next go into the 'Contacts Folders' and look in the hierarchy table for the Contact folders.
344
		if ($abContactContainerEntryid) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $abContactContainerEntryid does not seem to be defined for all execution paths leading up to this point.
Loading history...
345
			// Get the rows from hierarchy table of the 'Contacts Folders'
346
			$abContactContainer = mapi_ab_openentry($ab, $abContactContainerEntryid);
347
			$abContactContainerHierarchyTable = mapi_folder_gethierarchytable($abContactContainer, MAPI_DEFERRED_ERRORS);
348
			$abContactContainerHierarchyRows = mapi_table_queryallrows($abContactContainerHierarchyTable, [PR_DISPLAY_NAME, PR_OBJECT_TYPE, PR_ENTRYID]);
349
350
			// Loop through all the contact folders found under the 'Contacts Folders' hierarchy
351
			for ($j = 0,$len = count($abContactContainerHierarchyRows); $j < $len; ++$j) {
352
				// Open, get contents table, restrict, sort and then merge the result in the list of $rows
353
				$abContactFolder = mapi_ab_openentry($ab, $abContactContainerHierarchyRows[$j][PR_ENTRYID]);
354
				$abContactFolderTable = mapi_folder_getcontentstable($abContactFolder, MAPI_DEFERRED_ERRORS);
355
356
				mapi_table_restrict($abContactFolderTable, $contactFolderRestriction, TBL_BATCH);
357
				mapi_table_sort($abContactFolderTable, [PR_DISPLAY_NAME => TABLE_SORT_ASCEND], TBL_BATCH);
358
359
				// Go go gadget, merge!
360
				$rows = array_merge($rows, mapi_table_queryallrows($abContactFolderTable, [PR_ACCOUNT, PR_DISPLAY_NAME, PR_ENTRYID, PR_OBJECT_TYPE, PR_SMTP_ADDRESS, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE]));
361
			}
362
		}
363
364
		return $rows;
365
	}
366
367
	/**
368
	 * Setup the restriction used for resolving in the Contact folders or GAB.
369
	 *
370
	 * @param string $query            The search query, case is ignored
371
	 * @param bool   $excludeGABGroups flag to exclude groups from resolving
372
	 * @param int    $content          the PROPTAG to search in
373
	 */
374
	public function getAmbigiousContactRestriction($query, $excludeGABGroups, $content) {
375
		// only return users from who the displayName or the username starts with $name
376
		// TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and $content.
377
		$resAnd = [
378
			[RES_OR,
379
				[
380
					[RES_CONTENT,
381
						[
382
							FUZZYLEVEL => FL_PREFIX | FL_IGNORECASE,
383
							ULPROPTAG => PR_DISPLAY_NAME,
384
							VALUE => $query,
385
						],
386
					],
387
					[RES_CONTENT,
388
						[
389
							FUZZYLEVEL => FL_PREFIX | FL_IGNORECASE,
390
							ULPROPTAG => $content,
391
							VALUE => $query,
392
						],
393
					],
394
				], // RES_OR
395
			],
396
		];
397
398
		// create restrictions based on excludeGABGroups flag
399
		if ($excludeGABGroups) {
400
			array_push($resAnd, [
401
				RES_PROPERTY,
402
				[
403
					RELOP => RELOP_EQ,
404
					ULPROPTAG => PR_OBJECT_TYPE,
405
					VALUE => MAPI_MAILUSER,
406
				],
407
			]);
408
		}
409
		else {
410
			array_push($resAnd, [RES_OR,
411
				[
412
					[RES_PROPERTY,
413
						[
414
							RELOP => RELOP_EQ,
415
							ULPROPTAG => PR_OBJECT_TYPE,
416
							VALUE => MAPI_MAILUSER,
417
						],
418
					],
419
					[RES_PROPERTY,
420
						[
421
							RELOP => RELOP_EQ,
422
							ULPROPTAG => PR_OBJECT_TYPE,
423
							VALUE => MAPI_DISTLIST,
424
						],
425
					],
426
				],
427
			]);
428
		}
429
430
		return [RES_AND, $resAnd];
431
	}
432
433
	/**
434
	 * Function does customization of exception based on module data.
435
	 * like, here it will generate display message based on actionType
436
	 * for particular exception.
437
	 *
438
	 * @param object     $e             Exception object
439
	 * @param string     $actionType    the action type, sent by the client
440
	 * @param MAPIobject $store         store object of message
0 ignored issues
show
Bug introduced by
The type MAPIobject 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...
441
	 * @param string     $parententryid parent entryid of the message
442
	 * @param string     $entryid       entryid of the message
443
	 * @param array      $action        the action data, sent by the client
444
	 */
445
	#[Override]
446
	public function handleException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null) {
447
		if (is_null($e->displayMessage)) {
448
			switch ($actionType) {
449
				case 'checknames':
450
					if ($e->getCode() == MAPI_E_NO_ACCESS) {
451
						$e->setDisplayMessage(_('You have insufficient privileges to perform this action.'));
452
					}
453
					else {
454
						$e->setDisplayMessage(_('Could not resolve user.'));
455
					}
456
					break;
457
			}
458
		}
459
460
		parent::handleException($e, $actionType, $store, $parententryid, $entryid, $action);
461
	}
462
463
	/**
464
	 * This function searches the private contact folders for users and returns an array with data.
465
	 * Please note that the returning array must be UTF8.
466
	 *
467
	 * @param resource $ab        The addressbook
468
	 * @param resource $ab_dir    The addressbook container
469
	 * @param string   $searchstr The search query, case is ignored
470
	 * @param array    $rows      Array of the found contacts
471
	 */
472
	public function searchContactsFolders($ab, $ab_dir, $searchstr, &$rows) {
473
		$abhtable = mapi_folder_gethierarchytable($ab_dir, MAPI_DEFERRED_ERRORS | CONVENIENT_DEPTH);
474
		$abcntfolders = mapi_table_queryallrows($abhtable, [PR_ENTRYID, PR_AB_PROVIDER_ID]);
475
		$restriction = [
476
			RES_CONTENT,
477
			[
478
				FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE,
479
				ULPROPTAG => PR_DISPLAY_NAME,
480
				VALUE => [PR_DISPLAY_NAME => $searchstr],
481
			],
482
		];
483
		// restriction on hierarchy table for PR_AB_PROVIDER_ID
484
		// seems not to work, just loop through
485
		foreach ($abcntfolders as $abcntfolder) {
486
			if ($abcntfolder[PR_AB_PROVIDER_ID] == ZARAFA_CONTACTS_GUID) {
487
				$abfldentry = mapi_ab_openentry($ab, $abcntfolder[PR_ENTRYID]);
488
				$abfldcontents = mapi_folder_getcontentstable($abfldentry);
489
				mapi_table_restrict($abfldcontents, $restriction);
490
				$r = mapi_table_queryallrows($abfldcontents, [PR_ACCOUNT, PR_ADDRTYPE, PR_DISPLAY_NAME, PR_ENTRYID,
491
					PR_SEARCH_KEY, PR_OBJECT_TYPE, PR_SMTP_ADDRESS, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS,
492
					PR_OBJECT_TYPE, PR_DISPLAY_TYPE]);
493
				if (is_array($r) && !empty($r)) {
494
					$rows = array_merge($rows, $r);
495
				}
496
			}
497
		}
498
	}
499
}
500