AdvancedSearchListModule   F
last analyzed

Complexity

Total Complexity 99

Size/Duplication

Total Lines 495
Duplicated Lines 0 %

Importance

Changes 9
Bugs 4 Features 1
Metric Value
eloc 285
c 9
b 4
f 1
dl 0
loc 495
rs 2
wmc 99

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 20 1
C execute() 0 47 14
C messageList() 0 83 13
D parsePatterns() 0 92 38
F search() 0 205 33

How to fix   Complexity   

Complex Class

Complex classes like AdvancedSearchListModule often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AdvancedSearchListModule, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
require_once BASE_PATH . 'server/includes/core/class.indexsqlite.php';
4
5
class AdvancedSearchListModule extends ListModule {
6
	/**
7
	 * Constructor.
8
	 *
9
	 * @param int   $id   unique id
10
	 * @param array $data list of all actions
11
	 */
12
	public function __construct($id, $data) {
13
		parent::__construct($id, $data);
14
		// TODO: create a new method in Properties class that will return only the properties we
15
		// need for search list (and perhaps for preview???)
16
		$this->properties = $GLOBALS["properties"]->getMailListProperties();
17
		$this->properties = array_merge($this->properties, $GLOBALS["properties"]->getAppointmentListProperties());
18
		$this->properties = array_merge($this->properties, $GLOBALS["properties"]->getContactListProperties());
19
		$this->properties = array_merge($this->properties, $GLOBALS["properties"]->getStickyNoteListProperties());
20
		$this->properties = array_merge($this->properties, $GLOBALS["properties"]->getTaskListProperties());
21
		$this->properties = array_merge($this->properties, [
22
			'body' => PR_BODY,
23
			'html_body' => PR_HTML,
24
			'startdate' => "PT_SYSTIME:PSETID_Appointment:" . PidLidAppointmentStartWhole,
25
			'duedate' => "PT_SYSTIME:PSETID_Appointment:" . PidLidAppointmentEndWhole,
26
			'creation_time' => PR_CREATION_TIME,
27
			"task_duedate" => "PT_SYSTIME:PSETID_Task:" . PidLidTaskDueDate,
28
		]);
29
		$this->properties = getPropIdsFromStrings($GLOBALS["mapisession"]->getDefaultMessageStore(), $this->properties);
30
		$this->sort = [
31
			PR_MESSAGE_DELIVERY_TIME => TABLE_SORT_DESCEND,
32
		];
33
	}
34
35
	/**
36
	 * Executes all the actions in the $data variable.
37
	 */
38
	#[Override]
39
	public function execute() {
40
		foreach ($this->data as $actionType => $action) {
41
			if (isset($actionType)) {
42
				try {
43
					$store = $this->getActionStore($action);
44
					$parententryid = $this->getActionParentEntryID($action);
0 ignored issues
show
Unused Code introduced by
The assignment to $parententryid is dead and can be removed.
Loading history...
45
					$entryid = $this->getActionEntryID($action);
46
47
					switch ($actionType) {
48
						case "list":
49
						case "updatelist":
50
							$this->getDelegateFolderInfo($store);
0 ignored issues
show
Bug introduced by
$store of type object is incompatible with the type resource expected by parameter $store of ListModule::getDelegateFolderInfo(). ( Ignorable by Annotation )

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

50
							$this->getDelegateFolderInfo(/** @scrutinizer ignore-type */ $store);
Loading history...
51
							$this->messageList($store, $entryid, $action, $actionType);
52
							break;
53
54
						case "search":
55
							$this->search($store, $entryid, $action, $actionType);
56
							break;
57
58
						case "updatesearch":
59
							$this->updatesearch($store, $entryid, $action);
60
							break;
61
62
						case "stopsearch":
63
							$this->stopSearch($store, $entryid, $action);
64
							break;
65
66
						case "delete_searchfolder":
67
							$this->deleteSearchFolder($store, $entryid, $action);
68
							break;
69
					}
70
				}
71
				catch (MAPIException $e) {
72
					// This is a very nasty hack that makes sure that grommunio Web doesn't show an error message when
73
					// search wants to throw an error. This is only done because a proper fix for this bug has not
74
					// been found yet. When WA-9161 is really solved, this should be removed again.
75
					if ($actionType !== 'search' && $actionType !== 'updatesearch' && $actionType !== 'stopsearch') {
76
						$this->processException($e, $actionType);
77
					}
78
					else {
79
						if (DEBUG_LOADER === 0) {
80
							// Log all info we can get about this error to the error log of the web server
81
							error_log("Error in search: \n" . var_export($e, true) . "\n\n" . var_export(debug_backtrace(), true));
82
						}
83
						// Send success feedback without data, as if nothing strange happened...
84
						$this->sendFeedback(true);
85
					}
86
				}
87
			}
88
		}
89
	}
90
91
	/**
92
	 * Function which retrieves a list of messages in a folder.
93
	 *
94
	 * @param object $store      MAPI Message Store Object
95
	 * @param string $entryid    entryid of the folder
96
	 * @param array  $action     the action data, sent by the client
97
	 * @param string $actionType the action type, sent by the client
98
	 */
99
	#[Override]
100
	public function messageList($store, $entryid, $action, $actionType) {
101
		$this->searchFolderList = false; // Set to indicate this is not the search result, but a normal folder content
102
		$data = [];
103
104
		if ($store && $entryid) {
105
			// Restriction
106
			$this->parseRestriction($action);
0 ignored issues
show
Bug introduced by
$action of type array is incompatible with the type object expected by parameter $action of ListModule::parseRestriction(). ( Ignorable by Annotation )

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

106
			$this->parseRestriction(/** @scrutinizer ignore-type */ $action);
Loading history...
107
108
			// Sort
109
			$this->parseSortOrder($action, null, true);
110
111
			$limit = $action['restriction']['limit'] ?? 1000;
112
113
			$isSearchFolder = isset($action['search_folder_entryid']);
114
			$entryid = $isSearchFolder ? hex2bin((string) $action['search_folder_entryid']) : $entryid;
115
116
			if ($actionType == 'search') {
117
				$rows = [[PR_ENTRYID => $entryid]];
118
				if (isset($action['subfolders']) && $action['subfolders']) {
119
					$folder = mapi_msgstore_openentry($store, $entryid);
120
					$htable = mapi_folder_gethierarchytable($folder, CONVENIENT_DEPTH | MAPI_DEFERRED_ERRORS);
121
					$rows = mapi_table_queryallrows($htable, [PR_ENTRYID]);
122
				}
123
				$data['item'] = [];
124
				foreach ($rows as $row) {
125
					$items = $GLOBALS["operations"]->getTable($store, $row[PR_ENTRYID], $this->properties, $this->sort, $this->start, $limit, $this->restriction);
126
					$data['item'] = array_merge($data['item'], $items['item']);
127
					if (count($data['item']) >= $limit) {
128
						break;
129
					}
130
				}
131
				$data['page'] = [];
132
				$data['page']['start'] = 0;
133
				$data['page']['rowcount'] = 0;
134
				$data['page']['totalrowcount'] = count($data['item']);
135
				$data['search_meta'] = [];
136
				$data['search_meta']['searchfolder_entryid'] = null;
137
				$data['search_meta']['search_store_entryid'] = $action['store_entryid'];
138
				$data['search_meta']['searchstate'] = null;
139
				$data['search_meta']['results'] = count($data['item']);
140
				$data['folder'] = [];
141
				$data['folder']['content_count'] = count($data['item']);
142
				$data['folder']['content_unread'] = 0;
143
			}
144
			else {
145
				// Get the table and merge the arrays
146
				$data = $GLOBALS["operations"]->getTable($store, $entryid, $this->properties, $this->sort, $this->start, $limit, $this->restriction);
147
			}
148
149
			// If the request come from search folder then no need to send folder information
150
			if (!$isSearchFolder && !isset($data['folder'])) {
151
				// Open the folder.
152
				$folder = mapi_msgstore_openentry($store, $entryid);
153
				$data["folder"] = [];
154
155
				// Obtain some statistics from the folder contents
156
				$contentcount = mapi_getprops($folder, [PR_CONTENT_COUNT, PR_CONTENT_UNREAD]);
157
				if (isset($contentcount[PR_CONTENT_COUNT])) {
158
					$data["folder"]["content_count"] = $contentcount[PR_CONTENT_COUNT];
159
				}
160
161
				if (isset($contentcount[PR_CONTENT_UNREAD])) {
162
					$data["folder"]["content_unread"] = $contentcount[PR_CONTENT_UNREAD];
163
				}
164
			}
165
166
			$data = $this->filterPrivateItems($data);
167
168
			// Allowing to hook in just before the data sent away to be sent to the client
169
			$GLOBALS['PluginManager']->triggerHook('server.module.listmodule.list.after', [
170
				'moduleObject' => &$this,
171
				'store' => $store,
172
				'entryid' => $entryid,
173
				'action' => $action,
174
				'data' => &$data,
175
			]);
176
177
			// unset will remove the value but will not regenerate array keys, so we need to
178
			// do it here
179
			$data["item"] = array_values($data["item"]);
180
			$this->addActionData($actionType, $data);
181
			$GLOBALS["bus"]->addData($this->getResponseData());
182
		}
183
	}
184
185
	private function parsePatterns($restriction, &$patterns) {
186
		if (empty($restriction)) {
187
			return;
188
		}
189
		$type = $restriction[0];
190
		if ($type == RES_CONTENT) {
191
			$subres = $restriction[1];
192
193
			switch ($subres[ULPROPTAG]) {
194
				case PR_SUBJECT:
195
					$patterns['subject'] = $subres[VALUE][$subres[ULPROPTAG]];
196
					break;
197
198
				case PR_BODY:
199
					$patterns['content'] = $subres[VALUE][$subres[ULPROPTAG]];
200
					$patterns['attachments'] = $subres[VALUE][$subres[ULPROPTAG]];
201
					break;
202
203
				case PR_SENDER_NAME:
204
					$patterns['sender'] = $subres[VALUE][$subres[ULPROPTAG]];
205
					break;
206
207
				case PR_SENT_REPRESENTING_NAME:
208
					$patterns['sending'] = $subres[VALUE][$subres[ULPROPTAG]];
209
					break;
210
211
				case PR_DISPLAY_TO:
212
				case PR_DISPLAY_CC:
213
				case PR_DISPLAY_BCC:
214
					$patterns['recipients'] = $subres[VALUE][$subres[ULPROPTAG]];
215
					break;
216
217
				case PR_MESSAGE_CLASS:
218
					if (empty($patterns['message_classes'])) {
219
						$patterns['message_classes'] = [];
220
					}
221
					$patterns['message_classes'][] = $subres[VALUE][$subres[ULPROPTAG]];
222
					break;
223
224
				case PR_DISPLAY_NAME:
225
					$patterns['others'] = $subres[VALUE][$subres[ULPROPTAG]];
226
					break;
227
228
				case $this->properties['categories']:
229
					if (!isset($patterns['categories'])) {
230
						$patterns['categories'] = [];
231
					}
232
					if (isset($subres[VALUE][$subres[ULPROPTAG]][0])) {
233
						$patterns['categories'][] = $subres[VALUE][$subres[ULPROPTAG]][0];
234
					}
235
					break;
236
			}
237
		}
238
		elseif ($type == RES_AND || $type == RES_OR) {
239
			foreach ($restriction[1] as $subres) {
240
				$this->parsePatterns($subres, $patterns);
241
			}
242
		}
243
		elseif ($type == RES_BITMASK) {
244
			$subres = $restriction[1];
245
			if ($subres[ULPROPTAG] == PR_MESSAGE_FLAGS && $subres[ULTYPE] == BMR_EQZ) {
246
				$patterns['unread'] = MSGFLAG_READ & $subres[ULMASK];
247
			}
248
		}
249
		elseif ($type == RES_PROPERTY) {
250
			$subres = $restriction[1];
251
			if ($subres[ULPROPTAG] == PR_MESSAGE_DELIVERY_TIME ||
252
				$subres[ULPROPTAG] == PR_LAST_MODIFICATION_TIME) {
253
				if ($subres[RELOP] == RELOP_LT ||
254
					$subres[RELOP] == RELOP_LE) {
255
					$patterns['date_end'] = $subres[VALUE][$subres[ULPROPTAG]];
256
				}
257
				elseif ($subres[RELOP] == RELOP_GT ||
258
					$subres[RELOP] == RELOP_GE) {
259
					$patterns['date_start'] = $subres[VALUE][$subres[ULPROPTAG]];
260
				}
261
			}
262
		}
263
		elseif ($type == RES_SUBRESTRICTION) {
264
			$subres = $restriction[1];
265
			if ($subres[ULPROPTAG] == PR_MESSAGE_ATTACHMENTS) {
266
				$patterns['has_attachments'] = true;
267
			}
268
			elseif ($subres[ULPROPTAG] == PR_MESSAGE_RECIPIENTS) {
269
				$inner = $subres[RESTRICTION] ?? null;
270
				if (is_array($inner) && $inner[0] == RES_AND) {
271
					foreach ($inner[1] as $res) {
272
						if ($res[0] == RES_OR) {
273
							foreach ($res[1] as $orRes) {
274
								if ($orRes[0] == RES_CONTENT) {
275
									$patterns['recipients'] = $orRes[1][VALUE][$orRes[1][ULPROPTAG]];
276
									break 2;
277
								}
278
							}
279
						}
280
					}
281
				}
282
			}
283
		}
284
	}
285
286
	/**
287
	 *	Function will set search restrictions on search folder and start search process
288
	 *	and it will also parse visible columns and sorting data when sending results to client.
289
	 *
290
	 * @param object $store      MAPI Message Store Object
291
	 * @param string $entryid    entryid of the folder
292
	 * @param object $action     the action data, sent by the client
293
	 * @param string $actionType the action type, sent by the client
294
	 */
295
	#[Override]
296
	public function search($store, $entryid, $action, $actionType) {
297
		$useSearchFolder = $action["use_searchfolder"] ?? false;
298
		if (!$useSearchFolder) {
299
			/*
300
			 * store doesn't support search folders so we can't use this
301
			 * method instead we will pass restriction to messageList and
302
			 * it will give us the restricted results
303
			 */
304
			return parent::messageList($store, $entryid, $action, "list");
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::messageList($sto...tryid, $action, 'list') targeting ListModule::messageList() 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...
Bug introduced by
$action of type object is incompatible with the type array expected by parameter $action of ListModule::messageList(). ( Ignorable by Annotation )

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

304
			return parent::messageList($store, $entryid, /** @scrutinizer ignore-type */ $action, "list");
Loading history...
305
		}
306
		$store_props = mapi_getprops($store, [PR_MDB_PROVIDER, PR_DEFAULT_STORE, PR_IPM_SUBTREE_ENTRYID]);
307
		if ($store_props[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) {
308
			// public store does not support search folders
309
			return parent::messageList($store, $entryid, $action, "search");
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::messageList($sto...yid, $action, 'search') targeting ListModule::messageList() 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...
310
		}
311
		if ($GLOBALS['entryid']->compareEntryIds(bin2hex($entryid), bin2hex(TodoList::getEntryId()))) {
312
			// todo list do not need to perform full text index search
313
			return parent::messageList($store, $entryid, $action, "list");
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::messageList($sto...tryid, $action, 'list') targeting ListModule::messageList() 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...
314
		}
315
316
		$this->searchFolderList = true; // Set to indicate this is not the normal folder, but a search folder
317
		$this->restriction = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type array of property $restriction.

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...
318
319
		// Parse Restriction
320
		$this->parseRestriction($action);
321
		if ($this->restriction == false) {
322
			// if error in creating restriction then send error to client
323
			$errorInfo = [];
324
			$errorInfo["error_message"] = _("Error in search, please try again") . ".";
325
			$errorInfo["original_error_message"] = "Error in parsing restrictions.";
326
327
			return $this->sendSearchErrorToClient($store, $entryid, $action, $errorInfo);
0 ignored issues
show
Bug introduced by
$entryid of type string is incompatible with the type hexString expected by parameter $entryid of ListModule::sendSearchErrorToClient(). ( Ignorable by Annotation )

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

327
			return $this->sendSearchErrorToClient($store, /** @scrutinizer ignore-type */ $entryid, $action, $errorInfo);
Loading history...
Bug introduced by
$errorInfo of type array is incompatible with the type object expected by parameter $errorInfo of ListModule::sendSearchErrorToClient(). ( Ignorable by Annotation )

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

327
			return $this->sendSearchErrorToClient($store, $entryid, $action, /** @scrutinizer ignore-type */ $errorInfo);
Loading history...
328
		}
329
330
		$isSetSearchFolderEntryId = isset($action['search_folder_entryid']);
331
		if ($isSetSearchFolderEntryId) {
332
			$this->sessionData['searchFolderEntryId'] = $action['search_folder_entryid'];
333
		}
334
335
		if (isset($action['forceCreateSearchFolder']) && $action['forceCreateSearchFolder']) {
336
			$isSetSearchFolderEntryId = false;
337
		}
338
339
		// create or open search folder
340
		$searchFolder = $this->createSearchFolder($store, $isSetSearchFolderEntryId);
341
		if ($searchFolder === false) {
342
			if ($store_props[PR_MDB_PROVIDER] == ZARAFA_STORE_DELEGATE_GUID) {
343
				$this->messageList($store, $entryid, $action, "search");
344
345
				return true;
346
			}
347
			// if error in creating search folder then send error to client
348
			$errorInfo = [];
349
350
			$errorInfo["error_message"] = match (mapi_last_hresult()) {
351
				MAPI_E_NO_ACCESS => _("Unable to perform search query, no permissions to create search folder."),
352
				MAPI_E_NOT_FOUND => _("Unable to perform search query, search folder not found."),
353
				default => _("Unable to perform search query, store might not support searching."),
354
			};
355
356
			$errorInfo["original_error_message"] = _("Error in creating search folder.");
357
358
			return $this->sendSearchErrorToClient($store, $entryid, $action, $errorInfo);
0 ignored issues
show
Bug introduced by
$errorInfo of type array is incompatible with the type object expected by parameter $errorInfo of ListModule::sendSearchErrorToClient(). ( Ignorable by Annotation )

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

358
			return $this->sendSearchErrorToClient($store, $entryid, $action, /** @scrutinizer ignore-type */ $errorInfo);
Loading history...
359
		}
360
361
		$subfolder_flag = 0;
362
		$recursive = false;
363
		if (isset($action["subfolders"]) && $action["subfolders"] == "true") {
364
			$recursive = true;
365
			$subfolder_flag = RECURSIVE_SEARCH;
366
		}
367
368
		if (!is_array($entryid)) {
0 ignored issues
show
introduced by
The condition is_array($entryid) is always false.
Loading history...
369
			$entryids = [$entryid];
370
		}
371
		else {
372
			$entryids = $entryid;
373
		}
374
375
		$searchFolderEntryId = $this->sessionData['searchFolderEntryId'];
376
377
		// check if searchcriteria has changed
378
		$restrictionCheck = md5(serialize($this->restriction) . $searchFolderEntryId . $subfolder_flag);
379
380
		// check if there is need to set searchcriteria again
381
		if (!isset($this->sessionData['searchCriteriaCheck']) || $restrictionCheck != $this->sessionData['searchCriteriaCheck']) {
382
			if (!empty($this->sessionData['searchOriginalEntryids']) &&
383
				isset($action['entryid']) &&
384
				in_array($action['entryid'], $this->sessionData['searchOriginalEntryids'])
385
			) {
386
				// get entryids of original folders, and use it to set new search criteria
387
				$entryids = [];
388
				$entryIdsCount = count($this->sessionData['searchOriginalEntryids']);
389
				for ($index = 0; $index < $entryIdsCount; ++$index) {
390
					$entryids[] = hex2bin((string) $this->sessionData['searchOriginalEntryids'][$index]);
391
				}
392
			}
393
			else {
394
				// store entryids of original folders, so that can be used for re-setting the search criteria if needed
395
				$this->sessionData['searchOriginalEntryids'] = [];
396
				for ($index = 0, $len = count($entryids); $index < $len; ++$index) {
397
					$this->sessionData['searchOriginalEntryids'][] = bin2hex($entryids[$index]);
398
				}
399
			}
400
			// we never start the search folder because we will populate the search folder by ourselves
401
			mapi_folder_setsearchcriteria($searchFolder, $this->restriction, $entryids, $subfolder_flag | STOP_SEARCH);
402
			$this->sessionData['searchCriteriaCheck'] = $restrictionCheck;
403
		}
404
405
		if (isset($this->sessionData['searchCriteriaCheck']) || $restrictionCheck == $this->sessionData['searchCriteriaCheck']) {
406
			$folderEntryid = bin2hex($entryid);
407
			if ($this->sessionData['searchOriginalEntryids'][0] !== $folderEntryid) {
408
				$this->sessionData['searchOriginalEntryids'][0] = $folderEntryid;
409
				// we never start the search folder because we will populate the search folder by ourselves
410
				mapi_folder_setsearchcriteria($searchFolder, $this->restriction, [$entryid], $subfolder_flag | STOP_SEARCH);
411
			}
412
		}
413
414
		// Sort
415
		$this->parseSortOrder($action);
416
		// Initialize search patterns with default values
417
		$search_patterns = array_fill_keys(
418
			['sender', 'sending', 'recipients',
419
				'subject', 'content', 'attachments', 'others', 'message_classes',
420
				'date_start', 'date_end', 'unread', 'has_attachments', 'categories'],
421
			null
422
		);
423
		$this->parsePatterns($this->restriction, $search_patterns);
424
		if (isset($search_patterns['message_classes']) &&
425
			count($search_patterns['message_classes']) >= 7) {
426
			$search_patterns['message_classes'] = null;
427
		}
428
429
		if ($store_props[PR_MDB_PROVIDER] == ZARAFA_STORE_DELEGATE_GUID) {
430
			$eidObj = $GLOBALS["entryid"]->createMsgStoreEntryIdObj(hex2bin((string) $action['store_entryid']));
431
			$username = $eidObj['ServerShortname'];
432
			$session = $GLOBALS["mapisession"]->getSession();
433
434
			if ($username) {
435
				$indexDB = new IndexSqlite($username, $session, $store);
436
			}
437
		}
438
		else {
439
			$indexDB = new IndexSqlite();
440
		}
441
442
		$search_result = $indexDB->search(hex2bin((string) $searchFolderEntryId), $search_patterns, $entryid, $recursive);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $indexDB does not seem to be defined for all execution paths leading up to this point.
Loading history...
443
		// Use the query search if search in index fails or is not available.
444
		if ($search_result == false) {
445
			// Search in the inbox instead of Top of Information Store
446
			if (isset($store_props[PR_IPM_SUBTREE_ENTRYID]) &&
447
				$GLOBALS['entryid']->compareEntryIds(bin2hex($entryid), bin2hex((string) $store_props[PR_IPM_SUBTREE_ENTRYID]))) {
448
				$inbox = mapi_msgstore_getreceivefolder($store);
449
				$inboxProps = mapi_getprops($inbox, [PR_ENTRYID]);
450
				$entryid = $inboxProps[PR_ENTRYID];
451
			}
452
453
			return parent::messageList($store, $entryid, $action, "search");
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::messageList($sto...yid, $action, 'search') targeting ListModule::messageList() 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...
454
		}
455
456
		unset($action["restriction"]);
457
458
		// Get the table and merge the arrays
459
		$table = $GLOBALS["operations"]->getTable($store, hex2bin((string) $searchFolderEntryId), $this->properties, $this->sort, $this->start);
460
		// Create the data array, which will be send back to the client
461
		$data = [];
462
		$data = array_merge($data, $table);
463
464
		$this->getDelegateFolderInfo($store);
0 ignored issues
show
Bug introduced by
$store of type object is incompatible with the type resource expected by parameter $store of ListModule::getDelegateFolderInfo(). ( Ignorable by Annotation )

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

464
		$this->getDelegateFolderInfo(/** @scrutinizer ignore-type */ $store);
Loading history...
465
		$data = $this->filterPrivateItems($data);
466
467
		// remember which entryid's are send to the client
468
		$searchResults = [];
469
		foreach ($table["item"] as $item) {
470
			// store entryid => last_modification_time mapping
471
			$searchResults[$item["entryid"]] = $item["props"]["last_modification_time"];
472
		}
473
474
		// store search results into session data
475
		if (!isset($this->sessionData['searchResults'])) {
476
			$this->sessionData['searchResults'] = [];
477
		}
478
		$this->sessionData['searchResults'][$searchFolderEntryId] = $searchResults;
479
480
		$result = mapi_folder_getsearchcriteria($searchFolder);
481
482
		$data["search_meta"] = [];
483
		$data["search_meta"]["searchfolder_entryid"] = $searchFolderEntryId;
484
		$data["search_meta"]["search_store_entryid"] = $action["store_entryid"];
485
		$data["search_meta"]["searchstate"] = $result["searchstate"];
486
		$data["search_meta"]["results"] = count($searchResults);
487
488
		// Reopen the search folder, because otherwise the suggestion property will
489
		// not have been updated
490
		$searchFolder = $this->createSearchFolder($store, true);
491
		$storeProps = mapi_getprops($searchFolder, [PR_EC_SUGGESTION]);
492
		if (isset($storeProps[PR_EC_SUGGESTION])) {
493
			$data["search_meta"]["suggestion"] = $storeProps[PR_EC_SUGGESTION];
494
		}
495
496
		$this->addActionData("search", $data);
497
		$GLOBALS["bus"]->addData($this->getResponseData());
498
499
		return true;
500
	}
501
}
502