FolderChange   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 235
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 123
dl 0
loc 235
rs 9.2
c 0
b 0
f 0
wmc 40

1 Method

Rating   Name   Duplication   Size   Complexity  
F Handle() 0 226 40

How to fix   Complexity   

Complex Class

Complex classes like FolderChange 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 FolderChange, 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 FOLDERCREATE, FOLDERDELETE, FOLDERUPDATE command
9
 */
10
11
class FolderChange extends RequestProcessor {
12
	/**
13
	 * Handles creates, updates or deletes of a folder
14
	 * issued by the commands FolderCreate, FolderUpdate and FolderDelete.
15
	 *
16
	 * @param int $commandCode
17
	 *
18
	 * @return bool
19
	 */
20
	public function Handle($commandCode) {
21
		$el = self::$decoder->getElement();
22
23
		if ($el[EN_TYPE] != EN_TYPE_STARTTAG) {
24
			return false;
25
		}
26
27
		$create = $update = $delete = false;
28
		if ($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERCREATE) {
29
			$create = true;
30
		}
31
		elseif ($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERUPDATE) {
32
			$update = true;
33
		}
34
		elseif ($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERDELETE) {
35
			$delete = true;
36
		}
37
38
		if (!$create && !$update && !$delete) {
39
			return false;
40
		}
41
42
		// SyncKey
43
		if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) {
44
			return false;
45
		}
46
		$synckey = self::$decoder->getElementContent();
47
		if (!self::$decoder->getElementEndTag()) {
48
			return false;
49
		}
50
51
		// ServerID
52
		$serverid = false;
53
		$backendid = false;
54
		if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID)) {
55
			$serverid = self::$decoder->getElementContent();
56
			$backendid = self::$deviceManager->GetBackendIdForFolderId($serverid);
57
			if (!self::$decoder->getElementEndTag()) {
58
				return false;
59
			}
60
		}
61
62
		// Parent
63
		$parentid = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $parentid is dead and can be removed.
Loading history...
64
		$parentBackendId = false;
65
66
		// when creating or updating more information is necessary
67
		if (!$delete) {
68
			if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_PARENTID)) {
69
				$parentid = self::$decoder->getElementContent();
70
				$parentBackendId = self::$deviceManager->GetBackendIdForFolderId($parentid);
71
				if (!self::$decoder->getElementEndTag()) {
72
					return false;
73
				}
74
			}
75
76
			// Displayname
77
			if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_DISPLAYNAME)) {
78
				return false;
79
			}
80
			$displayname = self::$decoder->getElementContent();
81
			if (!self::$decoder->getElementEndTag()) {
82
				return false;
83
			}
84
85
			// Type
86
			$type = false;
87
			if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_TYPE)) {
88
				$type = self::$decoder->getElementContent();
89
				if (!self::$decoder->getElementEndTag()) {
90
					return false;
91
				}
92
			}
93
		}
94
95
		// endtag foldercreate, folderupdate, folderdelete
96
		if (!self::$decoder->getElementEndTag()) {
97
			return false;
98
		}
99
100
		$status = SYNC_FSSTATUS_SUCCESS;
101
102
		// Get state of hierarchy
103
		try {
104
			$syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey);
105
			$newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey);
106
107
			// there are no SyncParameters for the hierarchy, but we use it to save the latest synckeys
108
			$spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState(false);
109
110
			// Over the ChangesWrapper the HierarchyCache is notified about all changes
111
			$changesMem = self::$deviceManager->GetHierarchyChangesWrapper();
112
113
			// the hierarchyCache should now fully be initialized - check for changes in the additional folders
114
			$changesMem->Config(GSync::GetAdditionalSyncFolders(false));
115
116
			// reset to default store in backend
117
			self::$backend->Setup(false);
118
119
			// there are unprocessed changes in the hierarchy, trigger resync
120
			if ($changesMem->GetChangeCount() > 0) {
121
				throw new StatusException("HandleFolderChange() can not proceed as there are unprocessed hierarchy changes", SYNC_FSSTATUS_SERVERERROR);
122
			}
123
124
			// any additional folders can not be modified - with exception if they are of type SYNC_FOLDER_TYPE_UNKNOWN
125
			if (self::$deviceManager->GetFolderTypeFromCacheById($serverid) != SYNC_FOLDER_TYPE_UNKNOWN && $serverid !== false && GSync::GetAdditionalSyncFolderStore($backendid)) {
126
				throw new StatusException("HandleFolderChange() can not change additional folders which are configured", SYNC_FSSTATUS_SYSTEMFOLDER);
127
			}
128
129
			// switch user store if this this happens inside an additional folder
130
			// if this is an additional folder the backend has to be setup correctly
131
			// backend should also not be switched when type is SYNC_FOLDER_TYPE_UNKNOWN
132
			if (self::$deviceManager->GetFolderTypeFromCacheById($serverid) != SYNC_FOLDER_TYPE_UNKNOWN && !self::$backend->Setup(GSync::GetAdditionalSyncFolderStore(($parentBackendId != false) ? $parentBackendId : $backendid))) {
133
				throw new StatusException(sprintf("HandleFolderChange() could not Setup() the backend for folder id '%s'", ($parentBackendId != false) ? $parentBackendId : $backendid), SYNC_FSSTATUS_SERVERERROR);
134
			}
135
		}
136
		catch (StateNotFoundException) {
137
			$status = SYNC_FSSTATUS_SYNCKEYERROR;
138
		}
139
		catch (StatusException $stex) {
140
			$status = $stex->getCode();
141
		}
142
143
		// set $newsynckey in case of an error
144
		if (!isset($newsynckey)) {
145
			$newsynckey = $synckey;
146
		}
147
148
		if ($status == SYNC_FSSTATUS_SUCCESS) {
149
			try {
150
				// Configure importer with last state
151
				$importer = self::$backend->GetImporter();
152
				$importer->Config($syncstate);
153
154
				// the messages from the PIM will be forwarded to the real importer
155
				$changesMem->SetDestinationImporter($importer);
156
157
				// Create SyncFolder object
158
				$folder = new SyncFolder();
159
				$folder->serverid = $serverid;
160
				$folder->parentid = $parentBackendId;
161
				if (isset($displayname)) {
162
					$folder->displayname = $displayname;
163
				}
164
				if (isset($type)) {
165
					$folder->type = $type;
166
				}
167
				// add the backendId to the SyncFolder object
168
				$folder->BackendId = $backendid;
169
170
				// process incoming change
171
				if (!$delete) {
172
					// when creating, $folder->serverid is false, and the returned id is already mapped by the backend
173
					$folder = $changesMem->ImportFolderChange($folder);
174
				}
175
				else {
176
					// delete folder
177
					$changesMem->ImportFolderDeletion($folder);
178
				}
179
			}
180
			catch (StatusException $stex) {
181
				$status = $stex->getCode();
182
			}
183
		}
184
185
		self::$encoder->startWBXML();
186
		if ($create) {
187
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERCREATE);
188
189
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
190
			self::$encoder->content($status);
191
			self::$encoder->endTag();
192
193
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
194
			self::$encoder->content($newsynckey);
195
			self::$encoder->endTag();
196
197
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID);
198
			self::$encoder->content($folder->serverid);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $folder does not seem to be defined for all execution paths leading up to this point.
Loading history...
199
			self::$encoder->endTag();
200
201
			self::$encoder->endTag();
202
		}
203
		elseif ($update) {
204
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERUPDATE);
205
206
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
207
			self::$encoder->content($status);
208
			self::$encoder->endTag();
209
210
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
211
			self::$encoder->content($newsynckey);
212
			self::$encoder->endTag();
213
214
			self::$encoder->endTag();
215
		}
216
		elseif ($delete) {
217
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERDELETE);
218
219
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
220
			self::$encoder->content($status);
221
			self::$encoder->endTag();
222
223
			self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
224
			self::$encoder->content($newsynckey);
225
			self::$encoder->endTag();
226
227
			self::$encoder->endTag();
228
		}
229
230
		self::$topCollector->AnnounceInformation(sprintf("Operation status %d", $status), true);
231
232
		// Save the sync state for the next time
233
		if (isset($importer)) {
234
			self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $importer->GetState());
235
236
			// update SPA & save it
237
			$spa->SetSyncKey($newsynckey);
238
			$spa->SetFolderId(false);
239
			self::$deviceManager->GetStateManager()->SetSynchedFolderState($spa);
240
241
			// invalidate all pingable flags
242
			SyncCollections::InvalidatePingableFlags();
243
		}
244
245
		return true;
246
	}
247
}
248