Search   F
last analyzed

Complexity

Total Complexity 110

Size/Duplication

Total Lines 502
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 303
c 1
b 0
f 0
dl 0
loc 502
rs 2
wmc 110

1 Method

Rating   Name   Duplication   Size   Complexity  
F Handle() 0 494 110

How to fix   Complexity   

Complex Class

Complex classes like Search 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 Search, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * SPDX-License-Identifier: AGPL-3.0-only
5
 * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH
6
 * SPDX-FileCopyrightText: Copyright 2020-2024 grommunio GmbH
7
 *
8
 * Provides the SEARCH command
9
 */
10
11
class Search extends RequestProcessor {
12
	/**
13
	 * Handles the Search command.
14
	 *
15
	 * @param int $commandCode
16
	 *
17
	 * @return bool
18
	 */
19
	public function Handle($commandCode) {
20
		$searchrange = '0';
21
		$searchpicture = false;
22
		$cpo = new ContentParameters();
23
24
		if (!self::$decoder->getElementStartTag(SYNC_SEARCH_SEARCH)) {
25
			return false;
26
		}
27
28
		// TODO check: possible to search in other stores?
29
		if (!self::$decoder->getElementStartTag(SYNC_SEARCH_STORE)) {
30
			return false;
31
		}
32
33
		if (!self::$decoder->getElementStartTag(SYNC_SEARCH_NAME)) {
34
			return false;
35
		}
36
		$searchname = strtoupper((string) self::$decoder->getElementContent());
37
		if (!self::$decoder->getElementEndTag()) {
38
			return false;
39
		}
40
41
		if (!self::$decoder->getElementStartTag(SYNC_SEARCH_QUERY)) {
42
			return false;
43
		}
44
45
		// check if it is a content of an element (= GAL search)
46
		// or a starttag (= mailbox or documentlibrary search)
47
		$searchquery = self::$decoder->getElementContent();
48
		if ($searchquery && !self::$decoder->getElementEndTag()) {
49
			return false;
50
		}
51
52
		if ($searchquery === false) {
53
			$cpo->SetSearchName($searchname);
0 ignored issues
show
Bug introduced by
The method SetSearchName() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

53
			$cpo->/** @scrutinizer ignore-call */ 
54
         SetSearchName($searchname);
Loading history...
54
			if (self::$decoder->getElementStartTag(SYNC_SEARCH_AND)) {
55
				if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
56
					$searchfolderid = self::$decoder->getElementContent();
57
					$cpo->SetSearchFolderid($searchfolderid);
0 ignored issues
show
Bug introduced by
The method SetSearchFolderid() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

57
					$cpo->/** @scrutinizer ignore-call */ 
58
           SetSearchFolderid($searchfolderid);
Loading history...
58
					if (!self::$decoder->getElementEndTag()) { // SYNC_FOLDERID
59
						return false;
60
					}
61
				}
62
63
				if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
64
					$searchclass = self::$decoder->getElementContent();
65
					$cpo->SetSearchClass($searchclass);
0 ignored issues
show
Bug introduced by
The method SetSearchClass() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

65
					$cpo->/** @scrutinizer ignore-call */ 
66
           SetSearchClass($searchclass);
Loading history...
66
					if (!self::$decoder->getElementEndTag()) { // SYNC_FOLDERTYPE
67
						return false;
68
					}
69
				}
70
71
				if (self::$decoder->getElementStartTag(SYNC_SEARCH_FREETEXT)) {
72
					$searchfreetext = self::$decoder->getElementContent();
73
					$cpo->SetSearchFreeText($searchfreetext);
0 ignored issues
show
Bug introduced by
The method SetSearchFreeText() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

73
					$cpo->/** @scrutinizer ignore-call */ 
74
           SetSearchFreeText($searchfreetext);
Loading history...
74
					if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_FREETEXT
75
						return false;
76
					}
77
				}
78
79
				// TODO - review
80
				if (self::$decoder->getElementStartTag(SYNC_SEARCH_GREATERTHAN)) {
81
					if (self::$decoder->getElementStartTag(SYNC_POOMMAIL_DATERECEIVED)) {
82
						$datereceivedgreater = true;
83
						if (($dam = self::$decoder->getElementContent()) !== false) {
0 ignored issues
show
Unused Code introduced by
The assignment to $dam is dead and can be removed.
Loading history...
84
							$datereceivedgreater = true;
85
							if (!self::$decoder->getElementEndTag()) {
86
								return false;
87
							}
88
						}
89
						$cpo->SetSearchDateReceivedGreater($datereceivedgreater);
0 ignored issues
show
Bug introduced by
The method SetSearchDateReceivedGreater() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

89
						$cpo->/** @scrutinizer ignore-call */ 
90
            SetSearchDateReceivedGreater($datereceivedgreater);
Loading history...
90
					}
91
92
					if (self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) {
93
						$searchvalue = self::$decoder->getElementContent();
94
						$cpo->SetSearchValueGreater($searchvalue);
0 ignored issues
show
Bug introduced by
The method SetSearchValueGreater() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

94
						$cpo->/** @scrutinizer ignore-call */ 
95
            SetSearchValueGreater($searchvalue);
Loading history...
95
						if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_VALUE
96
							return false;
97
						}
98
					}
99
100
					if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_GREATERTHAN
101
						return false;
102
					}
103
				}
104
105
				if (self::$decoder->getElementStartTag(SYNC_SEARCH_LESSTHAN)) {
106
					if (self::$decoder->getElementStartTag(SYNC_POOMMAIL_DATERECEIVED)) {
107
						$datereceivedless = true;
108
						if (($dam = self::$decoder->getElementContent()) !== false) {
109
							$datereceivedless = true;
110
							if (!self::$decoder->getElementEndTag()) {
111
								return false;
112
							}
113
						}
114
						$cpo->SetSearchDateReceivedLess($datereceivedless);
0 ignored issues
show
Bug introduced by
The method SetSearchDateReceivedLess() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

114
						$cpo->/** @scrutinizer ignore-call */ 
115
            SetSearchDateReceivedLess($datereceivedless);
Loading history...
115
					}
116
117
					if (self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) {
118
						$searchvalue = self::$decoder->getElementContent();
119
						$cpo->SetSearchValueLess($searchvalue);
0 ignored issues
show
Bug introduced by
The method SetSearchValueLess() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

119
						$cpo->/** @scrutinizer ignore-call */ 
120
            SetSearchValueLess($searchvalue);
Loading history...
120
						if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_VALUE
121
							return false;
122
						}
123
					}
124
125
					if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_LESSTHAN
126
						return false;
127
					}
128
				}
129
130
				if (self::$decoder->getElementStartTag(SYNC_SEARCH_FREETEXT)) {
131
					$searchfreetext = self::$decoder->getElementContent();
132
					$cpo->SetSearchFreeText($searchfreetext);
133
					if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_FREETEXT
134
						return false;
135
					}
136
				}
137
138
				if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_AND
139
					return false;
140
				}
141
			}
142
			elseif (self::$decoder->getElementStartTag(SYNC_SEARCH_EQUALTO)) {
143
				// linkid can be an empty tag as well as have value
144
				if (self::$decoder->getElementStartTag(SYNC_DOCUMENTLIBRARY_LINKID)) {
145
					if (($linkId = self::$decoder->getElementContent()) !== false) {
146
						$cpo->SetLinkId($linkId);
0 ignored issues
show
Bug introduced by
The method SetLinkId() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

146
						$cpo->/** @scrutinizer ignore-call */ 
147
            SetLinkId($linkId);
Loading history...
147
						if (!self::$decoder->getElementEndTag()) { // SYNC_DOCUMENTLIBRARY_LINKID
148
							return false;
149
						}
150
					}
151
				}
152
153
				if (self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) {
154
					$searchvalue = self::$decoder->getElementContent();
155
					$cpo->SetSearchValueEqualTo($searchvalue);
0 ignored issues
show
Bug introduced by
The method SetSearchValueEqualTo() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

155
					$cpo->/** @scrutinizer ignore-call */ 
156
           SetSearchValueEqualTo($searchvalue);
Loading history...
156
					if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_VALUE
157
						return false;
158
					}
159
				}
160
161
				if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_EQUALTO
162
					return false;
163
				}
164
			}
165
166
			if (!self::$decoder->getElementEndTag()) { // SYNC_SEARCH_QUERY
167
				return false;
168
			}
169
		}
170
171
		if (self::$decoder->getElementStartTag(SYNC_SEARCH_OPTIONS)) {
172
			WBXMLDecoder::ResetInWhile("searchOptions");
173
			while (WBXMLDecoder::InWhile("searchOptions")) {
174
				if (self::$decoder->getElementStartTag(SYNC_SEARCH_RANGE)) {
175
					$searchrange = self::$decoder->getElementContent();
176
					$cpo->SetSearchRange($searchrange);
0 ignored issues
show
Bug introduced by
The method SetSearchRange() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

176
					$cpo->/** @scrutinizer ignore-call */ 
177
           SetSearchRange($searchrange);
Loading history...
177
					if (!self::$decoder->getElementEndTag()) {
178
						return false;
179
					}
180
				}
181
182
				if (self::$decoder->getElementStartTag(SYNC_SEARCH_REBUILDRESULTS)) {
183
					$rebuildresults = true;
184
					if (($dam = self::$decoder->getElementContent()) !== false) {
185
						$rebuildresults = true;
186
						if (!self::$decoder->getElementEndTag()) {
187
							return false;
188
						}
189
					}
190
					$cpo->SetSearchRebuildResults($rebuildresults);
0 ignored issues
show
Bug introduced by
The method SetSearchRebuildResults() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

190
					$cpo->/** @scrutinizer ignore-call */ 
191
           SetSearchRebuildResults($rebuildresults);
Loading history...
191
				}
192
193
				if (self::$decoder->getElementStartTag(SYNC_SEARCH_DEEPTRAVERSAL)) {
194
					$deeptraversal = true;
195
					if (($dam = self::$decoder->getElementContent()) !== false) {
196
						$deeptraversal = true;
197
						if (!self::$decoder->getElementEndTag()) {
198
							return false;
199
						}
200
					}
201
					$cpo->SetSearchDeepTraversal($deeptraversal);
0 ignored issues
show
Bug introduced by
The method SetSearchDeepTraversal() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

201
					$cpo->/** @scrutinizer ignore-call */ 
202
           SetSearchDeepTraversal($deeptraversal);
Loading history...
202
				}
203
204
				if (self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) {
205
					$cpo->SetMimeSupport(self::$decoder->getElementContent());
0 ignored issues
show
Bug introduced by
The method SetMimeSupport() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

205
					$cpo->/** @scrutinizer ignore-call */ 
206
           SetMimeSupport(self::$decoder->getElementContent());
Loading history...
206
					if (!self::$decoder->getElementEndTag()) {
207
						return false;
208
					}
209
				}
210
211
				// TODO body preferences
212
				while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) {
213
					if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
214
						$bptype = self::$decoder->getElementContent();
215
						$cpo->BodyPreference($bptype);
216
						if (!self::$decoder->getElementEndTag()) {
217
							return false;
218
						}
219
					}
220
221
					if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
222
						$cpo->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent());
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $bptype does not seem to be defined for all execution paths leading up to this point.
Loading history...
223
						if (!self::$decoder->getElementEndTag()) {
224
							return false;
225
						}
226
					}
227
228
					if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
229
						$cpo->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent());
230
						if (!self::$decoder->getElementEndTag()) {
231
							return false;
232
						}
233
					}
234
235
					if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
236
						$cpo->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent());
237
						if (!self::$decoder->getElementEndTag()) {
238
							return false;
239
						}
240
					}
241
242
					if (!self::$decoder->getElementEndTag()) {
243
						return false;
244
					}
245
				}
246
247
				if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPARTPREFERENCE)) {
248
					if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
249
						$bpptype = self::$decoder->getElementContent();
250
						$cpo->BodyPartPreference($bpptype);
251
						if (!self::$decoder->getElementEndTag()) {
252
							return false;
253
						}
254
					}
255
256
					if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
257
						$cpo->BodyPartPreference($bpptype)->SetTruncationSize(self::$decoder->getElementContent());
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $bpptype does not seem to be defined for all execution paths leading up to this point.
Loading history...
258
						if (!self::$decoder->getElementEndTag()) {
259
							return false;
260
						}
261
					}
262
263
					if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
264
						$cpo->BodyPartPreference($bpptype)->SetAllOrNone(self::$decoder->getElementContent());
265
						if (!self::$decoder->getElementEndTag()) {
266
							return false;
267
						}
268
					}
269
270
					if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
271
						$cpo->BodyPartPreference($bpptype)->SetPreview(self::$decoder->getElementContent());
272
						if (!self::$decoder->getElementEndTag()) {
273
							return false;
274
						}
275
					}
276
277
					if (!self::$decoder->getElementEndTag()) {
278
						return false;
279
					}
280
				}
281
282
				if (self::$decoder->getElementStartTag(SYNC_RIGHTSMANAGEMENT_SUPPORT)) {
283
					$cpo->SetRmSupport(self::$decoder->getElementContent());
0 ignored issues
show
Bug introduced by
The method SetRmSupport() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

283
					$cpo->/** @scrutinizer ignore-call */ 
284
           SetRmSupport(self::$decoder->getElementContent());
Loading history...
284
					if (!self::$decoder->getElementEndTag()) {
285
						return false;
286
					}
287
				}
288
289
				if (self::$decoder->getElementStartTag(SYNC_SEARCH_PICTURE)) { // TODO - do something with maxsize and maxpictures in the backend
290
					$searchpicture = new SyncResolveRecipientsPicture();
291
					if (self::$decoder->getElementStartTag(SYNC_SEARCH_MAXSIZE)) {
292
						$searchpicture->maxsize = self::$decoder->getElementContent();
293
						if (!self::$decoder->getElementEndTag()) {
294
							return false;
295
						}
296
					}
297
298
					if (self::$decoder->getElementStartTag(SYNC_SEARCH_MAXPICTURES)) {
299
						$searchpicture->maxpictures = self::$decoder->getElementContent();
300
						if (!self::$decoder->getElementEndTag()) {
301
							return false;
302
						}
303
					}
304
305
					// iOs devices send empty picture tag: <Search:Picture/>
306
					if (($sp = self::$decoder->getElementContent()) !== false) {
0 ignored issues
show
Unused Code introduced by
The assignment to $sp is dead and can be removed.
Loading history...
307
						if (!self::$decoder->getElementEndTag()) {
308
							return false;
309
						}
310
					}
311
				}
312
313
				$e = self::$decoder->peek();
314
				if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
315
					self::$decoder->getElementEndTag();
316
317
					break;
318
				}
319
			}
320
		}
321
		if (!self::$decoder->getElementEndTag()) { // store
322
			return false;
323
		}
324
325
		if (!self::$decoder->getElementEndTag()) { // search
326
			return false;
327
		}
328
329
		// get SearchProvider
330
		$searchprovider = GSync::GetBackend()->GetSearchProvider();
331
		$status = SYNC_SEARCHSTATUS_SUCCESS;
332
		$rows = [];
333
334
		// TODO support other searches
335
		if ($searchprovider->SupportsType($searchname)) {
336
			$storestatus = SYNC_SEARCHSTATUS_STORE_SUCCESS;
337
338
			try {
339
				if ($searchname == ISearchProvider::SEARCH_GAL) {
340
					// get search results from the searchprovider
341
					$rows = $searchprovider->GetGALSearchResults($searchquery, $searchrange, $searchpicture);
342
				}
343
				elseif ($searchname == ISearchProvider::SEARCH_MAILBOX) {
344
					$backendFolderId = self::$deviceManager->GetBackendIdForFolderId($cpo->GetSearchFolderid());
0 ignored issues
show
Bug introduced by
The method GetSearchFolderid() does not exist on ContentParameters. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

344
					$backendFolderId = self::$deviceManager->GetBackendIdForFolderId($cpo->/** @scrutinizer ignore-call */ GetSearchFolderid());
Loading history...
345
					$cpo->SetSearchFolderid($backendFolderId);
346
					$rows = $searchprovider->GetMailboxSearchResults($cpo);
347
				}
348
			}
349
			catch (StatusException $stex) {
350
				$storestatus = $stex->getCode();
351
			}
352
		}
353
		else {
354
			$rows = ['searchtotal' => 0];
355
			$status = SYNC_SEARCHSTATUS_SERVERERROR;
356
			SLog::Write(LOGLEVEL_WARN, sprintf("Searchtype '%s' is not supported.", $searchname));
357
			self::$topCollector->AnnounceInformation(sprintf("Unsupported type '%s''", $searchname), true);
358
		}
359
		$searchprovider->Disconnect();
360
361
		self::$topCollector->AnnounceInformation(sprintf("'%s' search found %d results", $searchname, $rows['searchtotal'] ?? 0), true);
362
363
		self::$encoder->startWBXML();
364
		self::$encoder->startTag(SYNC_SEARCH_SEARCH);
365
366
		self::$encoder->startTag(SYNC_SEARCH_STATUS);
367
		self::$encoder->content($status);
368
		self::$encoder->endTag();
369
370
		if ($status == SYNC_SEARCHSTATUS_SUCCESS) {
371
			self::$encoder->startTag(SYNC_SEARCH_RESPONSE);
372
			self::$encoder->startTag(SYNC_SEARCH_STORE);
373
374
			self::$encoder->startTag(SYNC_SEARCH_STATUS);
375
			self::$encoder->content($storestatus);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $storestatus does not seem to be defined for all execution paths leading up to this point.
Loading history...
376
			self::$encoder->endTag();
377
378
			if (isset($rows['range'])) {
379
				$searchrange = $rows['range'];
380
				unset($rows['range']);
381
			}
382
			if (isset($rows['searchtotal'])) {
383
				$searchtotal = $rows['searchtotal'];
384
				unset($rows['searchtotal']);
385
			}
386
			if ($searchname == ISearchProvider::SEARCH_GAL) {
387
				if (is_array($rows) && !empty($rows)) {
388
					foreach ($rows as $u) {
389
						self::$encoder->startTag(SYNC_SEARCH_RESULT);
390
						self::$encoder->startTag(SYNC_SEARCH_PROPERTIES);
391
392
						self::$encoder->startTag(SYNC_GAL_DISPLAYNAME);
393
						self::$encoder->content($u[SYNC_GAL_DISPLAYNAME] ?? "No name");
394
						self::$encoder->endTag();
395
396
						if (isset($u[SYNC_GAL_PHONE])) {
397
							self::$encoder->startTag(SYNC_GAL_PHONE);
398
							self::$encoder->content($u[SYNC_GAL_PHONE]);
399
							self::$encoder->endTag();
400
						}
401
402
						if (isset($u[SYNC_GAL_OFFICE])) {
403
							self::$encoder->startTag(SYNC_GAL_OFFICE);
404
							self::$encoder->content($u[SYNC_GAL_OFFICE]);
405
							self::$encoder->endTag();
406
						}
407
408
						if (isset($u[SYNC_GAL_TITLE])) {
409
							self::$encoder->startTag(SYNC_GAL_TITLE);
410
							self::$encoder->content($u[SYNC_GAL_TITLE]);
411
							self::$encoder->endTag();
412
						}
413
414
						if (isset($u[SYNC_GAL_COMPANY])) {
415
							self::$encoder->startTag(SYNC_GAL_COMPANY);
416
							self::$encoder->content($u[SYNC_GAL_COMPANY]);
417
							self::$encoder->endTag();
418
						}
419
420
						if (isset($u[SYNC_GAL_ALIAS])) {
421
							self::$encoder->startTag(SYNC_GAL_ALIAS);
422
							self::$encoder->content($u[SYNC_GAL_ALIAS]);
423
							self::$encoder->endTag();
424
						}
425
426
						// Always send the firstname, even empty. Nokia needs this to display the entry
427
						self::$encoder->startTag(SYNC_GAL_FIRSTNAME);
428
						self::$encoder->content($u[SYNC_GAL_FIRSTNAME] ?? "");
429
						self::$encoder->endTag();
430
431
						self::$encoder->startTag(SYNC_GAL_LASTNAME);
432
						self::$encoder->content($u[SYNC_GAL_LASTNAME] ?? "No name");
433
						self::$encoder->endTag();
434
435
						if (isset($u[SYNC_GAL_HOMEPHONE])) {
436
							self::$encoder->startTag(SYNC_GAL_HOMEPHONE);
437
							self::$encoder->content($u[SYNC_GAL_HOMEPHONE]);
438
							self::$encoder->endTag();
439
						}
440
441
						if (isset($u[SYNC_GAL_MOBILEPHONE])) {
442
							self::$encoder->startTag(SYNC_GAL_MOBILEPHONE);
443
							self::$encoder->content($u[SYNC_GAL_MOBILEPHONE]);
444
							self::$encoder->endTag();
445
						}
446
447
						self::$encoder->startTag(SYNC_GAL_EMAILADDRESS);
448
						self::$encoder->content($u[SYNC_GAL_EMAILADDRESS] ?? "");
449
						self::$encoder->endTag();
450
451
						if (isset($u[SYNC_GAL_PICTURE])) {
452
							self::$encoder->startTag(SYNC_GAL_PICTURE);
453
							self::$encoder->startTag(SYNC_GAL_STATUS);
454
							self::$encoder->content(SYNC_SEARCHSTATUS_PICTURE_SUCCESS); // FIXME: status code
455
							self::$encoder->endTag(); // SYNC_SEARCH_STATUS
456
457
							self::$encoder->startTag(SYNC_GAL_DATA);
458
							self::$encoder->contentStream($u[SYNC_GAL_PICTURE], false, true);
459
							self::$encoder->endTag(); // SYNC_GAL_DATA
460
							self::$encoder->endTag(); // SYNC_GAL_PICTURE
461
						}
462
463
						self::$encoder->endTag(); // result
464
						self::$encoder->endTag(); // properties
465
					}
466
				}
467
			}
468
			elseif ($searchname == ISearchProvider::SEARCH_MAILBOX) {
469
				foreach ($rows as $u) {
470
					// TODO: unclear if any clients *require* the folder id where the message is located (it's not available anymore)
471
					// $folderid = self::$deviceManager->GetFolderIdForBackendId($u['folderid']);
472
473
					self::$encoder->startTag(SYNC_SEARCH_RESULT);
474
					self::$encoder->startTag(SYNC_FOLDERTYPE);
475
					self::$encoder->content($u['class']);
476
					self::$encoder->endTag();
477
					self::$encoder->startTag(SYNC_SEARCH_LONGID);
478
					self::$encoder->content($u['longid']);
479
					self::$encoder->endTag();
480
					if (isset($searchfolderid)) {
481
						self::$encoder->startTag(SYNC_FOLDERID);
482
						self::$encoder->content($searchfolderid);
483
						self::$encoder->endTag();
484
					}
485
486
					self::$encoder->startTag(SYNC_SEARCH_PROPERTIES);
487
					$message = self::$backend->Fetch(false, $u['longid'], $cpo);
488
					$message->Encode(self::$encoder);
489
490
					self::$encoder->endTag(); // result
491
					self::$encoder->endTag(); // properties
492
				}
493
			}
494
			// it seems that android 4 requires range and searchtotal
495
			// or it won't display the search results
496
			if (isset($searchrange)) {
497
				self::$encoder->startTag(SYNC_SEARCH_RANGE);
498
				self::$encoder->content($searchrange);
499
				self::$encoder->endTag();
500
			}
501
			if (isset($searchtotal) && $searchtotal > 0) {
502
				self::$encoder->startTag(SYNC_SEARCH_TOTAL);
503
				self::$encoder->content($searchtotal);
504
				self::$encoder->endTag();
505
			}
506
507
			self::$encoder->endTag(); // store
508
			self::$encoder->endTag(); // response
509
		}
510
		self::$encoder->endTag(); // search
511
512
		return true;
513
	}
514
}
515