ItemOperations   F
last analyzed

Complexity

Total Complexity 84

Size/Duplication

Total Lines 424
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 238
dl 0
loc 424
rs 2
c 0
b 0
f 0
wmc 84

1 Method

Rating   Name   Duplication   Size   Complexity  
F Handle() 0 415 84

How to fix   Complexity   

Complex Class

Complex classes like ItemOperations 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 ItemOperations, 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-2022 grommunio GmbH
7
 *
8
 * Provides the ItemOperations command
9
 */
10
11
class ItemOperations extends RequestProcessor {
12
	/**
13
	 * Handles the ItemOperations command
14
	 * Provides batched online handling for Fetch, EmptyFolderContents and Move.
15
	 *
16
	 * @param int $commandCode
17
	 *
18
	 * @return bool
19
	 */
20
	public function Handle($commandCode) {
21
		// Parse input
22
		if (!self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_ITEMOPERATIONS)) {
23
			return false;
24
		}
25
26
		$itemoperations = [];
27
		// ItemOperations can either be Fetch, EmptyFolderContents or Move
28
		WBXMLDecoder::ResetInWhile("itemOperationsActions");
29
		while (WBXMLDecoder::InWhile("itemOperationsActions")) {
30
			// TODO check if multiple item operations are possible in one request
31
			$el = self::$decoder->getElement();
32
33
			if ($el[EN_TYPE] != EN_TYPE_STARTTAG) {
34
				return false;
35
			}
36
37
			$fetch = $efc = $move = false;
38
			$operation = [];
39
			if ($el[EN_TAG] == SYNC_ITEMOPERATIONS_FETCH) {
40
				$fetch = true;
41
				$operation['operation'] = SYNC_ITEMOPERATIONS_FETCH;
42
				self::$topCollector->AnnounceInformation("Fetch", true);
43
			}
44
			elseif ($el[EN_TAG] == SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS) {
45
				$efc = true;
46
				$operation['operation'] = SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS;
47
				self::$topCollector->AnnounceInformation("Empty Folder", true);
48
			}
49
			elseif ($el[EN_TAG] == SYNC_ITEMOPERATIONS_MOVE) {
50
				$move = true;
51
				$operation['operation'] = SYNC_ITEMOPERATIONS_MOVE;
52
				self::$topCollector->AnnounceInformation("Move", true);
53
			}
54
55
			if (!$fetch && !$efc && !$move) {
56
				SLog::Write(LOGLEVEL_DEBUG, "Unknown item operation:" . print_r($el, 1));
0 ignored issues
show
Bug introduced by
Are you sure print_r($el, 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

56
				SLog::Write(LOGLEVEL_DEBUG, "Unknown item operation:" . /** @scrutinizer ignore-type */ print_r($el, 1));
Loading history...
57
				self::$topCollector->AnnounceInformation("Unknown operation", true);
58
59
				return false;
60
			}
61
62
			// process operation
63
			WBXMLDecoder::ResetInWhile("itemOperationsOperation");
64
			while (WBXMLDecoder::InWhile("itemOperationsOperation")) {
65
				if ($fetch) {
66
					// Save all OPTIONS into a ContentParameters object
67
					$operation["cpo"] = new ContentParameters();
68
69
					if (self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_STORE)) {
70
						$operation['store'] = self::$decoder->getElementContent();
71
						if (!self::$decoder->getElementEndTag()) {
72
							return false;
73
						}// SYNC_ITEMOPERATIONS_STORE
74
					}
75
76
					if (self::$decoder->getElementStartTag(SYNC_SEARCH_LONGID)) {
77
						$operation['longid'] = self::$decoder->getElementContent();
78
						if (!self::$decoder->getElementEndTag()) {
79
							return false;
80
						}// SYNC_SEARCH_LONGID
81
					}
82
83
					if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
84
						$operation['folderid'] = self::$decoder->getElementContent();
85
						if (!self::$decoder->getElementEndTag()) {
86
							return false;
87
						}// SYNC_FOLDERID
88
					}
89
90
					if (self::$decoder->getElementStartTag(SYNC_SERVERENTRYID)) {
91
						$operation['serverid'] = self::$decoder->getElementContent();
92
						if (!self::$decoder->getElementEndTag()) {
93
							return false;
94
						}// SYNC_SERVERENTRYID
95
					}
96
97
					if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_FILEREFERENCE)) {
98
						$operation['filereference'] = self::$decoder->getElementContent();
99
						if (!self::$decoder->getElementEndTag()) {
100
							return false;
101
						}// SYNC_AIRSYNCBASE_FILEREFERENCE
102
					}
103
104
					if (($el = self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_OPTIONS)) && ($el[EN_FLAGS] & EN_FLAGS_CONTENT)) {
105
						// TODO other options
106
						// schema
107
						// range
108
						// username
109
						// password
110
						// bodypartpreference
111
						// rm:RightsManagementSupport
112
113
						WBXMLDecoder::ResetInWhile("itemOperationsOptions");
114
						while (WBXMLDecoder::InWhile("itemOperationsOptions")) {
115
							while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) {
116
								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
117
									$bptype = self::$decoder->getElementContent();
118
									$operation["cpo"]->BodyPreference($bptype);
119
									if (!self::$decoder->getElementEndTag()) {
120
										return false;
121
									}
122
								}
123
124
								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
125
									$operation["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...
126
									if (!self::$decoder->getElementEndTag()) {
127
										return false;
128
									}
129
								}
130
131
								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
132
									$operation["cpo"]->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent());
133
									if (!self::$decoder->getElementEndTag()) {
134
										return false;
135
									}
136
								}
137
138
								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
139
									$operation["cpo"]->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent());
140
									if (!self::$decoder->getElementEndTag()) {
141
										return false;
142
									}
143
								}
144
145
								if (!self::$decoder->getElementEndTag()) {
146
									return false;
147
								}// SYNC_AIRSYNCBASE_BODYPREFERENCE
148
							}
149
150
							if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPARTPREFERENCE)) {
151
								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
152
									$bpptype = self::$decoder->getElementContent();
153
									$operation["cpo"]->BodyPartPreference($bpptype);
154
									if (!self::$decoder->getElementEndTag()) {
155
										return false;
156
									}
157
								}
158
159
								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
160
									$operation["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...
161
									if (!self::$decoder->getElementEndTag()) {
162
										return false;
163
									}
164
								}
165
166
								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
167
									$operation["cpo"]->BodyPartPreference($bpptype)->SetAllOrNone(self::$decoder->getElementContent());
168
									if (!self::$decoder->getElementEndTag()) {
169
										return false;
170
									}
171
								}
172
173
								if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
174
									$operation["cpo"]->BodyPartPreference($bpptype)->SetPreview(self::$decoder->getElementContent());
175
									if (!self::$decoder->getElementEndTag()) {
176
										return false;
177
									}
178
								}
179
180
								if (!self::$decoder->getElementEndTag()) {
181
									return false;
182
								}
183
							}
184
185
							if (self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) {
186
								$operation["cpo"]->SetMimeSupport(self::$decoder->getElementContent());
187
								if (!self::$decoder->getElementEndTag()) {
188
									return false;
189
								}
190
							}
191
192
							if (self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_RANGE)) {
193
								$operation["range"] = self::$decoder->getElementContent();
194
								if (!self::$decoder->getElementEndTag()) {
195
									return false;
196
								}
197
							}
198
199
							if (self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_SCHEMA)) {
200
								// read schema tags
201
								WBXMLDecoder::ResetInWhile("itemOperationsSchema");
202
								while (WBXMLDecoder::InWhile("itemOperationsSchema")) {
203
									// TODO save elements
204
									$el = self::$decoder->getElement();
0 ignored issues
show
Unused Code introduced by
The assignment to $el is dead and can be removed.
Loading history...
205
									$e = self::$decoder->peek();
206
									if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
207
										self::$decoder->getElementEndTag();
208
209
										break;
210
									}
211
								}
212
							}
213
214
							if (self::$decoder->getElementStartTag(SYNC_RIGHTSMANAGEMENT_SUPPORT)) {
215
								$operation["cpo"]->SetRmSupport(self::$decoder->getElementContent());
216
								if (!self::$decoder->getElementEndTag()) {
217
									return false;
218
								}
219
							}
220
221
							// break if it reached the endtag
222
							$e = self::$decoder->peek();
223
							if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
224
								self::$decoder->getElementEndTag();
225
226
								break;
227
							}
228
						}
229
					}
230
231
					if (self::$decoder->getElementStartTag(SYNC_RIGHTSMANAGEMENT_REMOVERIGHTSMGNTPROTECTION)) {
232
						$operation["cpo"]->SetRemoveRmProtection(true);
233
						if (($rrmp = self::$decoder->getElementContent()) !== false) {
234
							$operation["cpo"]->SetRemoveRmProtection($rrmp);
235
							if (!self::$decoder->getElementEndTag()) {
236
								return false;
237
							}
238
						}
239
					}
240
				} // end if fetch
241
242
				if ($efc) {
243
					if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
244
						$operation['folderid'] = self::$decoder->getElementContent();
245
						if (!self::$decoder->getElementEndTag()) {
246
							return false;
247
						}// SYNC_FOLDERID
248
					}
249
					if (self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_OPTIONS)) {
250
						if (self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_DELETESUBFOLDERS)) {
251
							$operation['deletesubfolders'] = true;
252
							if (($dsf = self::$decoder->getElementContent()) !== false) {
253
								$operation['deletesubfolders'] = (bool) $dsf;
254
								if (!self::$decoder->getElementEndTag()) {
255
									return false;
256
								}
257
							}
258
						}
259
						self::$decoder->getElementEndTag();
260
					}
261
				}
262
263
				// TODO move
264
265
				// break if it reached the endtag SYNC_ITEMOPERATIONS_FETCH or SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS or SYNC_ITEMOPERATIONS_MOVE
266
				$e = self::$decoder->peek();
267
				if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
268
					self::$decoder->getElementEndTag();
269
270
					break;
271
				}
272
			} // end while operation
273
274
			// rewrite folderid into backendfolderid to be used on backend operations below
275
			if (isset($operation['folderid'])) {
276
				$operation['backendfolderid'] = self::$deviceManager->GetBackendIdForFolderId($operation['folderid']);
277
			}
278
279
			$itemoperations[] = $operation;
280
			// break if it reached the endtag
281
			$e = self::$decoder->peek();
282
			if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
283
				self::$decoder->getElementEndTag(); // SYNC_ITEMOPERATIONS_ITEMOPERATIONS
284
285
				break;
286
			}
287
		} // end operations loop
288
289
		$status = SYNC_ITEMOPERATIONSSTATUS_SUCCESS;
290
291
		self::$encoder->startWBXML();
292
293
		self::$encoder->startTag(SYNC_ITEMOPERATIONS_ITEMOPERATIONS);
294
295
		self::$encoder->startTag(SYNC_ITEMOPERATIONS_STATUS);
296
		self::$encoder->content($status);
297
		self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_STATUS
298
299
		// Stop here if something went wrong
300
		if ($status != SYNC_ITEMOPERATIONSSTATUS_SUCCESS) {
0 ignored issues
show
introduced by
The condition $status != SYNC_ITEMOPERATIONSSTATUS_SUCCESS is always false.
Loading history...
301
			self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_ITEMOPERATIONS
302
303
			return true;
304
		}
305
306
		self::$encoder->startTag(SYNC_ITEMOPERATIONS_RESPONSE);
307
308
		foreach ($itemoperations as $operation) {
309
			// fetch response
310
			if ($operation['operation'] == SYNC_ITEMOPERATIONS_FETCH) {
311
				$status = SYNC_ITEMOPERATIONSSTATUS_SUCCESS;
312
313
				// retrieve the data
314
				// Fetch throws Sync status codes, - GetAttachmentData ItemOperations codes
315
				if (isset($operation['filereference'])) {
316
					try {
317
						self::$topCollector->AnnounceInformation("Get attachment data from backend with file reference");
318
						$data = self::$backend->GetAttachmentData($operation['filereference']);
319
					}
320
					catch (StatusException $stex) {
321
						$status = $stex->getCode();
322
					}
323
				}
324
				else {
325
					try {
326
						if (isset($operation['folderid'], $operation['serverid'])) {
327
							self::$topCollector->AnnounceInformation("Fetching data from backend with item and folder id");
328
							$data = self::$backend->Fetch($operation['backendfolderid'], $operation['serverid'], $operation["cpo"]);
329
						}
330
						elseif (isset($operation['longid'])) {
331
							self::$topCollector->AnnounceInformation("Fetching data from backend with long id");
332
							$tmp = explode(":", $operation['longid']);
333
							$data = self::$backend->Fetch(self::$deviceManager->GetBackendIdForFolderId($tmp[0]), $tmp[1], $operation["cpo"]);
334
						}
335
					}
336
					catch (StatusException) {
337
						// the only option to return is that we could not retrieve it
338
						$status = SYNC_ITEMOPERATIONSSTATUS_CONVERSIONFAILED;
339
					}
340
				}
341
342
				self::$encoder->startTag(SYNC_ITEMOPERATIONS_FETCH);
343
344
				self::$encoder->startTag(SYNC_ITEMOPERATIONS_STATUS);
345
				self::$encoder->content($status);
346
				self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_STATUS
347
348
				if (isset($operation['folderid'], $operation['serverid'])) {
349
					self::$encoder->startTag(SYNC_FOLDERID);
350
					self::$encoder->content($operation['folderid']);
351
					self::$encoder->endTag(); // end SYNC_FOLDERID
352
353
					self::$encoder->startTag(SYNC_SERVERENTRYID);
354
					self::$encoder->content($operation['serverid']);
355
					self::$encoder->endTag(); // end SYNC_SERVERENTRYID
356
357
					self::$encoder->startTag(SYNC_FOLDERTYPE);
358
					self::$encoder->content("Email");
359
					self::$encoder->endTag();
360
				}
361
362
				if (isset($operation['longid'])) {
363
					self::$encoder->startTag(SYNC_SEARCH_LONGID);
364
					self::$encoder->content($operation['longid']);
365
					self::$encoder->endTag(); // end SYNC_FOLDERID
366
367
					self::$encoder->startTag(SYNC_FOLDERTYPE);
368
					self::$encoder->content("Email");
369
					self::$encoder->endTag();
370
				}
371
372
				if (isset($operation['filereference'])) {
373
					self::$encoder->startTag(SYNC_AIRSYNCBASE_FILEREFERENCE);
374
					self::$encoder->content($operation['filereference']);
375
					self::$encoder->endTag(); // end SYNC_AIRSYNCBASE_FILEREFERENCE
376
				}
377
378
				if (isset($data)) {
379
					self::$topCollector->AnnounceInformation("Streaming data");
380
381
					self::$encoder->startTag(SYNC_ITEMOPERATIONS_PROPERTIES);
382
					if (isset($operation['range'])) {
383
						self::$encoder->startTag(SYNC_ITEMOPERATIONS_RANGE);
384
						self::$encoder->content($operation['range']);
385
						self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_RANGE
386
					}
387
					$data->Encode(self::$encoder);
388
					self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_PROPERTIES
389
				}
390
391
				self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_FETCH
392
			}
393
			// empty folder contents operation
394
			elseif ($operation['operation'] == SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS) {
395
				try {
396
					self::$topCollector->AnnounceInformation("Emptying folder");
397
398
					// send request to backend
399
					self::$backend->EmptyFolder($operation['backendfolderid'], $operation['deletesubfolders']);
400
				}
401
				catch (StatusException $stex) {
402
					$status = $stex->getCode();
403
				}
404
405
				self::$encoder->startTag(SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS);
406
407
				self::$encoder->startTag(SYNC_ITEMOPERATIONS_STATUS);
408
				self::$encoder->content($status);
409
				self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_STATUS
410
411
				if (isset($operation['folderid'])) {
412
					self::$encoder->startTag(SYNC_FOLDERID);
413
					self::$encoder->content($operation['folderid']);
414
					self::$encoder->endTag(); // end SYNC_FOLDERID
415
				}
416
				self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS
417
			}
418
			// TODO implement ItemOperations Move
419
			// move operation
420
			else {
421
				self::$topCollector->AnnounceInformation("not implemented", true);
422
423
				// reply with "can't do"
424
				self::$encoder->startTag(SYNC_ITEMOPERATIONS_MOVE);
425
				self::$encoder->startTag(SYNC_ITEMOPERATIONS_STATUS);
426
				self::$encoder->content(SYNC_ITEMOPERATIONSSTATUS_SERVERERROR);
427
				self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_STATUS
428
				self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_MOVE
429
			}
430
		}
431
		self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_RESPONSE
432
		self::$encoder->endTag(); // SYNC_ITEMOPERATIONS_ITEMOPERATIONS
433
434
		return true;
435
	}
436
}
437