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
 * SPDX-License-Identifier: AGPL-3.0-only
4
 * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH
5
 * SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH
6
 *
7
 * Provides the ItemOperations command
8
 */
9
10
class ItemOperations extends RequestProcessor {
11
	/**
12
	 * Handles the ItemOperations command
13
	 * Provides batched online handling for Fetch, EmptyFolderContents and Move.
14
	 *
15
	 * @param int $commandCode
16
	 *
17
	 * @return bool
18
	 */
19
	public function Handle($commandCode) {
20
		// Parse input
21
		if (!self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_ITEMOPERATIONS)) {
22
			return false;
23
		}
24
25
		$itemoperations = [];
26
		// ItemOperations can either be Fetch, EmptyFolderContents or Move
27
		WBXMLDecoder::ResetInWhile("itemOperationsActions");
28
		while (WBXMLDecoder::InWhile("itemOperationsActions")) {
29
			// TODO check if multiple item operations are possible in one request
30
			$el = self::$decoder->getElement();
31
32
			if ($el[EN_TYPE] != EN_TYPE_STARTTAG) {
33
				return false;
34
			}
35
36
			$fetch = $efc = $move = false;
37
			$operation = [];
38
			if ($el[EN_TAG] == SYNC_ITEMOPERATIONS_FETCH) {
39
				$fetch = true;
40
				$operation['operation'] = SYNC_ITEMOPERATIONS_FETCH;
41
				self::$topCollector->AnnounceInformation("Fetch", true);
42
			}
43
			elseif ($el[EN_TAG] == SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS) {
44
				$efc = true;
45
				$operation['operation'] = SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS;
46
				self::$topCollector->AnnounceInformation("Empty Folder", true);
47
			}
48
			elseif ($el[EN_TAG] == SYNC_ITEMOPERATIONS_MOVE) {
49
				$move = true;
50
				$operation['operation'] = SYNC_ITEMOPERATIONS_MOVE;
51
				self::$topCollector->AnnounceInformation("Move", true);
52
			}
53
54
			if (!$fetch && !$efc && !$move) {
55
				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

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