Provisioning   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 252
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 145
c 2
b 0
f 0
dl 0
loc 252
rs 8.48
wmc 49

1 Method

Rating   Name   Duplication   Size   Complexity  
F Handle() 0 244 49

How to fix   Complexity   

Complex Class

Complex classes like Provisioning 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 Provisioning, 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 PROVISIONING command
8
 */
9
10
class Provisioning extends RequestProcessor {
11
	/**
12
	 * Handles the Provisioning command.
13
	 *
14
	 * @param int $commandCode
15
	 *
16
	 * @return bool
17
	 */
18
	public function Handle($commandCode) {
19
		$status = SYNC_PROVISION_STATUS_SUCCESS;
20
		$policystatus = SYNC_PROVISION_POLICYSTATUS_SUCCESS;
21
22
		$rwstatus = GSync::GetProvisioningManager()->GetProvisioningWipeStatus();
23
		$rwstatusWiped = false;
24
		$deviceInfoSet = false;
25
		$wipeRequest = !($rwstatus < SYNC_PROVISION_RWSTATUS_PENDING);
26
27
		// if this is a regular provisioning require that an authenticated remote user
28
		if (!$wipeRequest) {
29
			SLog::Write(LOGLEVEL_DEBUG, "RequestProcessor::HandleProvision(): Forcing delayed Authentication");
30
			self::Authenticate();
31
		}
32
33
		$phase2 = true;
34
35
		if (!self::$decoder->getElementStartTag(SYNC_PROVISION_PROVISION)) {
36
			return false;
37
		}
38
39
		// Loop through Provision request tags. Possible are:
40
		// - Remote Wipe
41
		// - DeviceInformation
42
		// - Policies
43
		// Each of them should only be once per request.
44
		WBXMLDecoder::ResetInWhile("provisioningMain");
45
		while (WBXMLDecoder::InWhile("provisioningMain")) {
46
			$requestName = "";
47
			if (self::$decoder->getElementStartTag(SYNC_PROVISION_REMOTEWIPE)) {
48
				$requestName = SYNC_PROVISION_REMOTEWIPE;
49
			}
50
			if (self::$decoder->getElementStartTag(SYNC_PROVISION_ACCOUNTONLYREMOTEWIPE)) {
51
				$requestName = SYNC_PROVISION_ACCOUNTONLYREMOTEWIPE;
52
			}
53
			if (self::$decoder->getElementStartTag(SYNC_PROVISION_POLICIES)) {
54
				$requestName = SYNC_PROVISION_POLICIES;
55
			}
56
			if (self::$decoder->getElementStartTag(SYNC_SETTINGS_DEVICEINFORMATION)) {
57
				$requestName = SYNC_SETTINGS_DEVICEINFORMATION;
58
			}
59
60
			if (!$requestName) {
61
				break;
62
			}
63
64
			// set is available for OOF, device password and device information
65
			switch ($requestName) {
66
				case SYNC_PROVISION_REMOTEWIPE:
67
				case SYNC_PROVISION_ACCOUNTONLYREMOTEWIPE:
68
					if (!self::$decoder->getElementStartTag(SYNC_PROVISION_STATUS)) {
69
						return false;
70
					}
71
72
					$instatus = self::$decoder->getElementContent();
0 ignored issues
show
Unused Code introduced by
The assignment to $instatus is dead and can be removed.
Loading history...
73
74
					if (!self::$decoder->getElementEndTag()) {
75
						return false;
76
					}
77
78
					if (!self::$decoder->getElementEndTag()) {
79
						return false;
80
					}
81
82
					$phase2 = false;
83
					$rwstatusWiped = true;
84
					// TODO check - do it after while(1) finished?
85
					break;
86
87
				case SYNC_PROVISION_POLICIES:
88
					if (!self::$decoder->getElementStartTag(SYNC_PROVISION_POLICY)) {
89
						return false;
90
					}
91
92
					if (!self::$decoder->getElementStartTag(SYNC_PROVISION_POLICYTYPE)) {
93
						return false;
94
					}
95
96
					$policytype = self::$decoder->getElementContent();
97
					if ($policytype != 'MS-WAP-Provisioning-XML' && $policytype != 'MS-EAS-Provisioning-WBXML') {
98
						$status = SYNC_PROVISION_STATUS_SERVERERROR;
99
					}
100
					if (!self::$decoder->getElementEndTag()) { // policytype
101
						return false;
102
					}
103
104
					if (self::$decoder->getElementStartTag(SYNC_PROVISION_POLICYKEY)) {
105
						$devpolicykey = self::$decoder->getElementContent();
0 ignored issues
show
Unused Code introduced by
The assignment to $devpolicykey is dead and can be removed.
Loading history...
106
107
						if (!self::$decoder->getElementEndTag()) {
108
							return false;
109
						}
110
111
						if (!self::$decoder->getElementStartTag(SYNC_PROVISION_STATUS)) {
112
							return false;
113
						}
114
115
						$instatus = self::$decoder->getElementContent();
116
117
						if (!self::$decoder->getElementEndTag()) {
118
							return false;
119
						}
120
121
						$phase2 = false;
122
					}
123
124
					if (!self::$decoder->getElementEndTag()) { // policy
125
						return false;
126
					}
127
128
					if (!self::$decoder->getElementEndTag()) { // policies
129
						return false;
130
					}
131
					break;
132
133
				case SYNC_SETTINGS_DEVICEINFORMATION:
134
					// AS14.1 and later clients pass Device Information on the initial Provision request
135
					if (!self::$decoder->getElementStartTag(SYNC_SETTINGS_SET)) {
136
						return false;
137
					}
138
					$deviceInfoSet = true;
139
					$deviceinformation = new SyncDeviceInformation();
140
					$deviceinformation->Decode(self::$decoder);
141
					$deviceinformation->Status = SYNC_SETTINGSSTATUS_SUCCESS;
142
					if (!$wipeRequest) {
143
						// for this operation the device manager is available
144
						GSync::GetDeviceManager()->SaveDeviceInformation($deviceinformation);
145
					}
146
					else {
147
						SLog::Write(LOGLEVEL_DEBUG, "Ignoring incoming device information as WIPE is due.");
148
					}
149
					if (!self::$decoder->getElementEndTag()) {  // SYNC_SETTINGS_SET
150
						return false;
151
					}
152
					if (!self::$decoder->getElementEndTag()) {  // SYNC_SETTINGS_DEVICEINFORMATION
153
						return false;
154
					}
155
					break;
156
157
				default:
158
					// TODO: a special status code needed?
159
					SLog::Write(LOGLEVEL_WARN, sprintf("This property ('%s') is not allowed to be used in a provision request", $requestName));
160
			}
161
		}
162
163
		if (!self::$decoder->getElementEndTag()) { // provision
164
			return false;
165
		}
166
167
		if (PROVISIONING !== true) {
0 ignored issues
show
introduced by
The condition PROVISIONING !== true is always false.
Loading history...
168
			SLog::Write(LOGLEVEL_INFO, "No policies deployed to device");
169
			$policystatus = SYNC_PROVISION_POLICYSTATUS_NOPOLICY;
170
		}
171
172
		self::$encoder->StartWBXML();
173
174
		// just create a temporary key (i.e. iPhone OS4 Beta does not like policykey 0 in response)
175
		$policykey = GSync::GetProvisioningManager()->GenerateProvisioningPolicyKey();
176
177
		self::$encoder->startTag(SYNC_PROVISION_PROVISION);
178
179
		self::$encoder->startTag(SYNC_PROVISION_STATUS);
180
		self::$encoder->content($status);
181
		self::$encoder->endTag();
182
183
		if ($deviceInfoSet) {
184
			self::$encoder->startTag(SYNC_SETTINGS_DEVICEINFORMATION);
185
			self::$encoder->startTag(SYNC_SETTINGS_STATUS);
186
			self::$encoder->content($deviceinformation->Status);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $deviceinformation does not seem to be defined for all execution paths leading up to this point.
Loading history...
187
			self::$encoder->endTag(); // SYNC_SETTINGS_STATUS
188
			self::$encoder->endTag(); // SYNC_SETTINGS_DEVICEINFORMATION
189
		}
190
191
		self::$encoder->startTag(SYNC_PROVISION_POLICIES);
192
		self::$encoder->startTag(SYNC_PROVISION_POLICY);
193
194
		if (isset($policytype)) {
195
			self::$encoder->startTag(SYNC_PROVISION_POLICYTYPE);
196
			self::$encoder->content($policytype);
197
			self::$encoder->endTag();
198
		}
199
200
		self::$encoder->startTag(SYNC_PROVISION_STATUS);
201
		self::$encoder->content($policystatus);
202
		self::$encoder->endTag();
203
204
		self::$encoder->startTag(SYNC_PROVISION_POLICYKEY);
205
		self::$encoder->content($policykey);
206
		self::$encoder->endTag();
207
208
		if ($phase2 && $policystatus === SYNC_PROVISION_POLICYSTATUS_SUCCESS) {
209
			self::$encoder->startTag(SYNC_PROVISION_DATA);
210
			if ($policytype == 'MS-WAP-Provisioning-XML') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $policytype does not seem to be defined for all execution paths leading up to this point.
Loading history...
211
				self::$encoder->content('<wap-provisioningdoc><characteristic type="SecurityPolicy"><parm name="4131" value="1"/><parm name="4133" value="1"/></characteristic></wap-provisioningdoc>');
212
			}
213
			elseif ($policytype == 'MS-EAS-Provisioning-WBXML') {
214
				self::$encoder->startTag(SYNC_PROVISION_EASPROVISIONDOC);
215
216
				// get the provisioning object and log the loaded policy values
217
				$prov = GSync::GetProvisioningManager()->GetProvisioningObject(true);
218
				if (!$prov->Check()) {
219
					throw new FatalException("Invalid policies!");
220
				}
221
222
				GSync::GetProvisioningManager()->SavePolicyHash($prov);
223
				$prov->Encode(self::$encoder);
224
				self::$encoder->endTag();
225
			}
226
			else {
227
				SLog::Write(LOGLEVEL_WARN, "Wrong policy type");
228
				self::$topCollector->AnnounceInformation("Policytype not supported", true);
229
230
				return false;
231
			}
232
			self::$topCollector->AnnounceInformation("Updated provisioning", true);
233
234
			self::$encoder->endTag(); // data
235
		}
236
		self::$encoder->endTag(); // policy
237
		self::$encoder->endTag(); // policies
238
239
		// set the new final policy key in the provisioning manager
240
		if (!$phase2 && !$wipeRequest) {
241
			GSync::GetProvisioningManager()->SetProvisioningPolicyKey($policykey);
242
			self::$topCollector->AnnounceInformation("Policies deployed", true);
243
		}
244
245
		// wipe data if a higher RWSTATUS is requested
246
		if ($rwstatus > SYNC_PROVISION_RWSTATUS_OK && $policystatus === SYNC_PROVISION_POLICYSTATUS_SUCCESS) {
247
			if ($rwstatus <= SYNC_PROVISION_RWSTATUS_WIPED) {
248
				self::$encoder->startTag(SYNC_PROVISION_REMOTEWIPE, false, true);
249
				GSync::GetProvisioningManager()->SetProvisioningWipeStatus(($rwstatusWiped) ? SYNC_PROVISION_RWSTATUS_WIPED : SYNC_PROVISION_RWSTATUS_REQUESTED);
250
				self::$topCollector->AnnounceInformation(sprintf("Remote wipe %s", ($rwstatusWiped) ? "executed" : "requested"), true);
251
			}
252
			else {
253
				self::$encoder->startTag(SYNC_PROVISION_ACCOUNTONLYREMOTEWIPE, false, true);
254
				GSync::GetProvisioningManager()->SetProvisioningWipeStatus(($rwstatusWiped) ? SYNC_PROVISION_RWSTATUS_WIPED_ACCOUNT_ONLY : SYNC_PROVISION_RWSTATUS_REQUESTED_ACCOUNT_ONLY);
255
				self::$topCollector->AnnounceInformation(sprintf("Account Only Remote wipe %s", ($rwstatusWiped) ? "executed" : "requested"), true);
256
			}
257
		}
258
259
		self::$encoder->endTag(); // provision
260
261
		return true;
262
	}
263
}
264