Passed
Push — master ( 34e8da...f497d2 )
by
unknown
06:10 queued 02:50
created
lib/core/synccollections.php 2 patches
Indentation   +903 added lines, -903 removed lines patch added patch discarded remove patch
@@ -19,907 +19,907 @@
 block discarded – undo
19 19
  */
20 20
 
21 21
 class SyncCollections implements Iterator {
22
-	public const ERROR_NO_COLLECTIONS = 1;
23
-	public const ERROR_WRONG_HIERARCHY = 2;
24
-	public const OBSOLETE_CONNECTION = 3;
25
-	public const HIERARCHY_CHANGED = 4;
26
-
27
-	private $stateManager;
28
-
29
-	private $collections = [];
30
-	private $addparms = [];
31
-	private $changes = [];
32
-	private $saveData = true;
33
-
34
-	private $refPolicyKey = false;
35
-	private $refLifetime = false;
36
-
37
-	private $globalWindowSize;
38
-	private $lastSyncTime;
39
-
40
-	private $waitingTime = 0;
41
-	private $hierarchyExporterChecked = false;
42
-	private $loggedGlobalWindowSizeOverwrite = false;
43
-
44
-	/**
45
-	 * Invalidates all pingable flags for all folders.
46
-	 *
47
-	 * @return bool
48
-	 */
49
-	public static function InvalidatePingableFlags() {
50
-		SLog::Write(LOGLEVEL_DEBUG, "SyncCollections::InvalidatePingableFlags(): Invalidating now");
51
-
52
-		try {
53
-			$sc = new SyncCollections();
54
-			$sc->LoadAllCollections();
55
-			foreach ($sc as $folderid => $spa) {
56
-				if ($spa->GetPingableFlag() == true) {
57
-					$spa->DelPingableFlag();
58
-					$sc->SaveCollection($spa);
59
-				}
60
-			}
61
-
62
-			return true;
63
-		}
64
-		catch (GSyncException $e) {
65
-		}
66
-
67
-		return false;
68
-	}
69
-
70
-	/**
71
-	 * Constructor.
72
-	 */
73
-	public function __construct() {
74
-	}
75
-
76
-	/**
77
-	 * Sets the StateManager for this object
78
-	 * If this is not done and a method needs it, the StateManager will be
79
-	 * requested from the DeviceManager.
80
-	 *
81
-	 * @param StateManager $statemanager
82
-	 *
83
-	 * @return
84
-	 */
85
-	public function SetStateManager($statemanager) {
86
-		$this->stateManager = $statemanager;
87
-	}
88
-
89
-	/**
90
-	 * Loads all collections known for the current device.
91
-	 *
92
-	 * @param bool $overwriteLoaded  (opt) overwrites Collection with saved state if set to true
93
-	 * @param bool $loadState        (opt) indicates if the collection sync state should be loaded, default false
94
-	 * @param bool $checkPermissions (opt) if set to true each folder will pass
95
-	 *                               through a backend->Setup() to check permissions.
96
-	 *                               If this fails a StatusException will be thrown.
97
-	 * @param bool $loadHierarchy    (opt) if the hierarchy sync states should be loaded, default false
98
-	 * @param bool $confirmedOnly    (opt) indicates if only confirmed states should be loaded, default: false
99
-	 *
100
-	 * @throws StatusException       with SyncCollections::ERROR_WRONG_HIERARCHY if permission check fails
101
-	 * @throws StateInvalidException if the sync state can not be found or relation between states is invalid ($loadState = true)
102
-	 *
103
-	 * @return bool
104
-	 */
105
-	public function LoadAllCollections($overwriteLoaded = false, $loadState = false, $checkPermissions = false, $loadHierarchy = false, $confirmedOnly = false) {
106
-		$this->loadStateManager();
107
-
108
-		// this operation should not remove old state counters
109
-		$this->stateManager->DoNotDeleteOldStates();
110
-
111
-		$invalidStates = false;
112
-		foreach ($this->stateManager->GetSynchedFolders() as $folderid) {
113
-			if ($overwriteLoaded === false && isset($this->collections[$folderid])) {
114
-				continue;
115
-			}
116
-
117
-			// Load Collection!
118
-			if (!$this->LoadCollection($folderid, $loadState, $checkPermissions, $confirmedOnly)) {
119
-				$invalidStates = true;
120
-			}
121
-		}
122
-
123
-		// load the hierarchy data - there are no permissions to verify so we just set it to false
124
-		if ($loadHierarchy && !$this->LoadCollection(false, $loadState, false, false)) {
125
-			throw new StatusException("Invalid states found while loading hierarchy data. Forcing hierarchy sync");
126
-		}
127
-
128
-		if ($invalidStates) {
129
-			throw new StateInvalidException("Invalid states found while loading collections. Forcing sync");
130
-		}
131
-
132
-		return true;
133
-	}
134
-
135
-	/**
136
-	 * Loads all collections known for the current device.
137
-	 *
138
-	 * @param string $folderid         folder id to be loaded
139
-	 * @param bool   $loadState        (opt) indicates if the collection sync state should be loaded, default true
140
-	 * @param bool   $checkPermissions (opt) if set to true each folder will pass
141
-	 *                                 through a backend->Setup() to check permissions.
142
-	 *                                 If this fails a StatusException will be thrown.
143
-	 * @param bool   $confirmedOnly    (opt) indicates if only confirmed states should be loaded, default: false
144
-	 *
145
-	 * @throws StatusException       with SyncCollections::ERROR_WRONG_HIERARCHY if permission check fails
146
-	 * @throws StateInvalidException if the sync state can not be found or relation between states is invalid ($loadState = true)
147
-	 *
148
-	 * @return bool
149
-	 */
150
-	public function LoadCollection($folderid, $loadState = false, $checkPermissions = false, $confirmedOnly = false) {
151
-		$this->loadStateManager();
152
-
153
-		try {
154
-			// Get SyncParameters for the folder from the state
155
-			$spa = $this->stateManager->GetSynchedFolderState($folderid, !$loadState);
156
-
157
-			// TODO remove resync of folders
158
-			// this forces a resync of all states
159
-			if (!$spa instanceof SyncParameters) {
160
-				throw new StateInvalidException("Saved state are not of type SyncParameters");
161
-			}
162
-
163
-			if ($spa->GetUuidCounter() == 0) {
164
-				SLog::Write(LOGLEVEL_DEBUG, "SyncCollections->LoadCollection(): Found collection with move state only, ignoring.");
165
-
166
-				return true;
167
-			}
168
-		}
169
-		catch (StateInvalidException $sive) {
170
-			// in case there is something wrong with the state, just stop here
171
-			// later when trying to retrieve the SyncParameters nothing will be found
172
-
173
-			if ($folderid === false) {
174
-				throw new StatusException(sprintf("SyncCollections->LoadCollection(): could not get FOLDERDATA state of the hierarchy uuid: %s", $spa->GetUuid()), self::ERROR_WRONG_HIERARCHY);
175
-			}
176
-
177
-			// we also generate a fake change, so a sync on this folder is triggered
178
-			$this->changes[$folderid] = 1;
179
-
180
-			return false;
181
-		}
182
-
183
-		// if this is an additional folder the backend has to be setup correctly
184
-		if ($checkPermissions === true && !GSync::GetBackend()->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
185
-			throw new StatusException(sprintf("SyncCollections->LoadCollection(): could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), self::ERROR_WRONG_HIERARCHY);
186
-		}
187
-
188
-		// add collection to object
189
-		$addStatus = $this->AddCollection($spa);
190
-
191
-		// load the latest known syncstate if requested
192
-		if ($addStatus && $loadState === true) {
193
-			try {
194
-				// make sure the hierarchy cache is loaded when we are loading hierarchy states
195
-				$this->addparms[$folderid]["state"] = $this->stateManager->GetSyncState($spa->GetLatestSyncKey($confirmedOnly), ($folderid === false));
196
-			}
197
-			catch (StateNotFoundException $snfe) {
198
-				// if we can't find the state, first we should try a sync of that folder, so
199
-				// we generate a fake change, so a sync on this folder is triggered
200
-				$this->changes[$folderid] = 1;
201
-
202
-				// make sure this folder is fully synched on next Sync request
203
-				$this->invalidateFolderStat($spa);
204
-
205
-				return false;
206
-			}
207
-		}
208
-
209
-		return $addStatus;
210
-	}
211
-
212
-	/**
213
-	 * Saves a SyncParameters Object.
214
-	 *
215
-	 * @param SyncParamerts $spa
216
-	 *
217
-	 * @return bool
218
-	 */
219
-	public function SaveCollection($spa) {
220
-		if (!$this->saveData || !$spa->HasFolderId()) {
221
-			return false;
222
-		}
223
-
224
-		if ($spa->IsDataChanged()) {
225
-			$this->loadStateManager();
226
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->SaveCollection(): Data of folder '%s' changed", $spa->GetFolderId()));
227
-
228
-			// save new windowsize
229
-			if (isset($this->globalWindowSize)) {
230
-				$spa->SetWindowSize($this->globalWindowSize);
231
-			}
232
-
233
-			// update latest lifetime
234
-			if (isset($this->refLifetime)) {
235
-				$spa->SetReferenceLifetime($this->refLifetime);
236
-			}
237
-
238
-			return $this->stateManager->SetSynchedFolderState($spa);
239
-		}
240
-
241
-		return false;
242
-	}
243
-
244
-	/**
245
-	 * Adds a SyncParameters object to the current list of collections.
246
-	 *
247
-	 * @param SyncParameters $spa
248
-	 *
249
-	 * @return bool
250
-	 */
251
-	public function AddCollection($spa) {
252
-		if (!$spa->HasFolderId()) {
253
-			return false;
254
-		}
255
-
256
-		$this->collections[$spa->GetFolderId()] = $spa;
257
-
258
-		SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->AddCollection(): Folder id '%s' : ref. Lifetime '%s', last sync at '%s'", $spa->GetFolderId(), $spa->GetReferenceLifetime(), $spa->GetLastSyncTime()));
259
-		if ($spa->HasLastSyncTime() && $spa->GetLastSyncTime() > $this->lastSyncTime) {
260
-			$this->lastSyncTime = $spa->GetLastSyncTime();
261
-
262
-			// use SyncParameters PolicyKey as reference if available
263
-			if ($spa->HasReferencePolicyKey()) {
264
-				$this->refPolicyKey = $spa->GetReferencePolicyKey();
265
-			}
266
-
267
-			// use SyncParameters LifeTime as reference if available
268
-			if ($spa->HasReferenceLifetime()) {
269
-				$this->refLifetime = $spa->GetReferenceLifetime();
270
-			}
271
-
272
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->AddCollection(): Updated reference PolicyKey '%s', reference Lifetime '%s', Last sync at '%s'", $this->refPolicyKey, $this->refLifetime, $this->lastSyncTime));
273
-		}
274
-
275
-		return true;
276
-	}
277
-
278
-	/**
279
-	 * Returns a previousily added or loaded SyncParameters object for a folderid.
280
-	 *
281
-	 * @param SyncParameters $spa
282
-	 * @param mixed          $folderid
283
-	 *
284
-	 * @return SyncParameters / boolean      false if no SyncParameters object is found for folderid
285
-	 */
286
-	public function GetCollection($folderid) {
287
-		if (isset($this->collections[$folderid])) {
288
-			return $this->collections[$folderid];
289
-		}
290
-
291
-		return false;
292
-	}
293
-
294
-	/**
295
-	 * Indicates if there are any loaded CPOs.
296
-	 *
297
-	 * @return bool
298
-	 */
299
-	public function HasCollections() {
300
-		return !empty($this->collections);
301
-	}
302
-
303
-	/**
304
-	 * Indicates the amount of collections loaded.
305
-	 *
306
-	 * @return int
307
-	 */
308
-	public function GetCollectionCount() {
309
-		return count($this->collections);
310
-	}
311
-
312
-	/**
313
-	 * Add a non-permanent key/value pair for a SyncParameters object.
314
-	 *
315
-	 * @param SyncParameters $spa   target SyncParameters
316
-	 * @param string         $key
317
-	 * @param mixed          $value
318
-	 *
319
-	 * @return bool
320
-	 */
321
-	public function AddParameter($spa, $key, $value) {
322
-		if (!$spa->HasFolderId()) {
323
-			return false;
324
-		}
325
-
326
-		$folderid = $spa->GetFolderId();
327
-		if (!isset($this->addparms[$folderid])) {
328
-			$this->addparms[$folderid] = [];
329
-		}
330
-
331
-		$this->addparms[$folderid][$key] = $value;
332
-
333
-		return true;
334
-	}
335
-
336
-	/**
337
-	 * Returns a previousily set non-permanent value for a SyncParameters object.
338
-	 *
339
-	 * @param SyncParameters $spa target SyncParameters
340
-	 * @param string         $key
341
-	 *
342
-	 * @return mixed returns 'null' if nothing set
343
-	 */
344
-	public function GetParameter($spa, $key) {
345
-		if (!$spa->HasFolderId()) {
346
-			return null;
347
-		}
348
-
349
-		if (isset($this->addparms[$spa->GetFolderId()], $this->addparms[$spa->GetFolderId()][$key])) {
350
-			return $this->addparms[$spa->GetFolderId()][$key];
351
-		}
352
-
353
-		return null;
354
-	}
355
-
356
-	/**
357
-	 * Returns the latest known PolicyKey to be used as reference.
358
-	 *
359
-	 * @return int/boolean       returns false if nothing found in collections
360
-	 */
361
-	public function GetReferencePolicyKey() {
362
-		return $this->refPolicyKey;
363
-	}
364
-
365
-	/**
366
-	 * Sets a global window size which should be used for all collections
367
-	 * in a case of a heartbeat and/or partial sync.
368
-	 *
369
-	 * @param int $windowsize
370
-	 *
371
-	 * @return bool
372
-	 */
373
-	public function SetGlobalWindowSize($windowsize) {
374
-		$this->globalWindowSize = $windowsize;
375
-
376
-		return true;
377
-	}
378
-
379
-	/**
380
-	 * Returns the global window size of items to be exported in total over all
381
-	 * requested collections.
382
-	 *
383
-	 * @return int/boolean          returns requested windows size, 512 (max) or the
384
-	 *                              value of config SYNC_MAX_ITEMS if it is lower
385
-	 */
386
-	public function GetGlobalWindowSize() {
387
-		// take the requested global windowsize or the max 512 if not defined
388
-		if (isset($this->globalWindowSize)) {
389
-			$globalWindowSize = $this->globalWindowSize;
390
-		}
391
-		else {
392
-			$globalWindowSize = WINDOW_SIZE_MAX; // 512 by default
393
-		}
394
-
395
-		if (defined("SYNC_MAX_ITEMS") && $globalWindowSize > SYNC_MAX_ITEMS) {
396
-			if (!$this->loggedGlobalWindowSizeOverwrite) {
397
-				SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->GetGlobalWindowSize() overwriting requested global window size of %d by %d forced in configuration.", $globalWindowSize, SYNC_MAX_ITEMS));
398
-				$this->loggedGlobalWindowSizeOverwrite = true;
399
-			}
400
-			$globalWindowSize = SYNC_MAX_ITEMS;
401
-		}
402
-
403
-		return $globalWindowSize;
404
-	}
405
-
406
-	/**
407
-	 * Sets the lifetime for heartbeat or ping connections.
408
-	 *
409
-	 * @param int $lifetime time in seconds
410
-	 *
411
-	 * @return bool
412
-	 */
413
-	public function SetLifetime($lifetime) {
414
-		$this->refLifetime = $lifetime;
415
-
416
-		return true;
417
-	}
418
-
419
-	/**
420
-	 * Sets the lifetime for heartbeat or ping connections
421
-	 * previousily set or saved in a collection.
422
-	 *
423
-	 * @return int returns PING_HIGHER_BOUND_LIFETIME as default if nothing set or not available.
424
-	 *             If PING_HIGHER_BOUND_LIFETIME is not set, returns 600.
425
-	 */
426
-	public function GetLifetime() {
427
-		if (!isset($this->refLifetime) || $this->refLifetime === false) {
428
-			if (PING_HIGHER_BOUND_LIFETIME !== false) {
429
-				return PING_HIGHER_BOUND_LIFETIME;
430
-			}
431
-
432
-			return 600;
433
-		}
434
-
435
-		return $this->refLifetime;
436
-	}
437
-
438
-	/**
439
-	 * Returns the timestamp of the last synchronization for all
440
-	 * loaded collections.
441
-	 *
442
-	 * @return int timestamp
443
-	 */
444
-	public function GetLastSyncTime() {
445
-		return $this->lastSyncTime;
446
-	}
447
-
448
-	/**
449
-	 * Checks if the currently known collections for changes for $lifetime seconds.
450
-	 * If the backend provides a ChangesSink the sink will be used.
451
-	 * If not every $interval seconds an exporter will be configured for each
452
-	 * folder to perform GetChangeCount().
453
-	 *
454
-	 * @param int  $lifetime     (opt) total lifetime to wait for changes / default 600s
455
-	 * @param int  $interval     (opt) time between blocking operations of sink or polling / default 30s
456
-	 * @param bool $onlyPingable (opt) only check for folders which have the PingableFlag
457
-	 *
458
-	 * @throws StatusException with code SyncCollections::ERROR_NO_COLLECTIONS if no collections available
459
-	 *                         with code SyncCollections::ERROR_WRONG_HIERARCHY if there were errors getting changes
460
-	 *
461
-	 * @return bool indicating if changes were found
462
-	 */
463
-	public function CheckForChanges($lifetime = 600, $interval = 30, $onlyPingable = false) {
464
-		$classes = [];
465
-		foreach ($this->collections as $folderid => $spa) {
466
-			if ($onlyPingable && $spa->GetPingableFlag() !== true || !$folderid) {
467
-				continue;
468
-			}
469
-
470
-			$class = $this->getPingClass($spa);
471
-
472
-			if (!isset($classes[$class])) {
473
-				$classes[$class] = 0;
474
-			}
475
-			++$classes[$class];
476
-		}
477
-		if (empty($classes)) {
478
-			$checkClasses = "policies only";
479
-		}
480
-		elseif (array_sum($classes) > 4) {
481
-			$checkClasses = "";
482
-			foreach ($classes as $class => $count) {
483
-				if ($count == 1) {
484
-					$checkClasses .= sprintf("%s ", $class);
485
-				}
486
-				else {
487
-					$checkClasses .= sprintf("%s(%d) ", $class, $count);
488
-				}
489
-			}
490
-		}
491
-		else {
492
-			$checkClasses = implode(" ", array_keys($classes));
493
-		}
494
-
495
-		$pingTracking = new PingTracking();
496
-		$this->changes = [];
497
-
498
-		GSync::GetDeviceManager()->AnnounceProcessAsPush();
499
-		GSync::GetTopCollector()->AnnounceInformation(sprintf("lifetime %ds", $lifetime), true);
500
-		SLog::Write(LOGLEVEL_INFO, sprintf("SyncCollections->CheckForChanges(): Waiting for %s changes... (lifetime %d seconds)", (empty($classes)) ? 'policy' : 'store', $lifetime));
501
-
502
-		// use changes sink where available
503
-		$changesSink = GSync::GetBackend()->HasChangesSink();
504
-
505
-		// create changessink and check folder stats if there are folders to Ping
506
-		if (!empty($classes)) {
507
-			// initialize all possible folders
508
-			foreach ($this->collections as $folderid => $spa) {
509
-				if (($onlyPingable && $spa->GetPingableFlag() !== true) || !$folderid) {
510
-					continue;
511
-				}
512
-
513
-				$backendFolderId = $spa->GetBackendFolderId();
514
-
515
-				// get the user store if this is a additional folder
516
-				$store = GSync::GetAdditionalSyncFolderStore($backendFolderId);
517
-
518
-				// initialize sink if no immediate changes were found so far
519
-				if ($changesSink && empty($this->changes)) {
520
-					GSync::GetBackend()->Setup($store);
521
-					if (!GSync::GetBackend()->ChangesSinkInitialize($backendFolderId)) {
522
-						throw new StatusException(sprintf("Error initializing ChangesSink for folder id %s/%s", $folderid, $backendFolderId), self::ERROR_WRONG_HIERARCHY);
523
-					}
524
-				}
525
-
526
-				// check if the folder stat changed since the last sync, if so generate a change for it (only on first run)
527
-				$currentFolderStat = GSync::GetBackend()->GetFolderStat($store, $backendFolderId);
528
-				if ($this->waitingTime == 0 && GSync::GetBackend()->HasFolderStats() && $currentFolderStat !== false && $spa->IsExporterRunRequired($currentFolderStat, true)) {
529
-					$this->changes[$spa->GetFolderId()] = 1;
530
-				}
531
-			}
532
-		}
533
-
534
-		if (!empty($this->changes)) {
535
-			SLog::Write(LOGLEVEL_DEBUG, "SyncCollections->CheckForChanges(): Using ChangesSink but found changes verifying the folder stats");
536
-
537
-			return true;
538
-		}
539
-
540
-		// wait for changes
541
-		$started = time();
542
-		$endat = time() + $lifetime;
543
-
544
-		// always use policy key from the request if it was sent
545
-		$policyKey = $this->GetReferencePolicyKey();
546
-		if (Request::WasPolicyKeySent() && Request::GetPolicyKey() != 0) {
547
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("refpolkey:'%s', sent polkey:'%s'", $policyKey, Request::GetPolicyKey()));
548
-			$policyKey = Request::GetPolicyKey();
549
-		}
550
-		while (($now = time()) < $endat) {
551
-			// how long are we waiting for changes
552
-			$this->waitingTime = $now - $started;
553
-
554
-			$nextInterval = $interval;
555
-			// we should not block longer than the lifetime
556
-			if ($endat - $now < $nextInterval) {
557
-				$nextInterval = $endat - $now;
558
-			}
559
-
560
-			// Check if provisioning is necessary
561
-			// if a PolicyKey was sent use it. If not, compare with the ReferencePolicyKey
562
-			if (PROVISIONING === true && $policyKey !== false && GSync::GetProvisioningManager()->ProvisioningRequired($policyKey, true, false)) {
563
-				// the hierarchysync forces provisioning
564
-				throw new StatusException("SyncCollections->CheckForChanges(): Policies or PolicyKey changed. Provisioning required.", self::ERROR_WRONG_HIERARCHY);
565
-			}
566
-
567
-			// Check if a hierarchy sync is necessary
568
-			if ($this->countHierarchyChange()) {
569
-				throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED);
570
-			}
571
-
572
-			// Check if there are newer requests
573
-			// If so, this process should be terminated if more than 60 secs to go
574
-			if ($pingTracking->DoForcePingTimeout()) {
575
-				// do not update CPOs because another process has already read them!
576
-				$this->saveData = false;
577
-
578
-				// more than 60 secs to go?
579
-				if (($now + 60) < $endat) {
580
-					GSync::GetTopCollector()->AnnounceInformation(sprintf("Forced timeout after %ds", ($now - $started)), true);
581
-
582
-					throw new StatusException(sprintf("SyncCollections->CheckForChanges(): Timeout forced after %ss from %ss due to other process", ($now - $started), $lifetime), self::OBSOLETE_CONNECTION);
583
-				}
584
-			}
585
-
586
-			// Use changes sink if available
587
-			if ($changesSink) {
588
-				GSync::GetTopCollector()->AnnounceInformation(sprintf("Sink %d/%ds on %s", ($now - $started), $lifetime, $checkClasses));
589
-				$notifications = GSync::GetBackend()->ChangesSink($nextInterval);
590
-
591
-				// how long are we waiting for changes
592
-				$this->waitingTime = time() - $started;
593
-
594
-				$validNotifications = false;
595
-				foreach ($notifications as $backendFolderId) {
596
-					// Check hierarchy notifications
597
-					if ($backendFolderId === IBackend::HIERARCHYNOTIFICATION) {
598
-						// wait two seconds before validating this notification, because it could potentially be made by the mobile and we need some time to update the states.
599
-						sleep(2);
600
-						// check received hierarchy notifications by exporting
601
-						if ($this->countHierarchyChange(true)) {
602
-							throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED);
603
-						}
604
-					}
605
-					else {
606
-						// the backend will notify on the backend folderid
607
-						$folderid = GSync::GetDeviceManager()->GetFolderIdForBackendId($backendFolderId);
608
-
609
-						// check if the notification on the folder is within our filter
610
-						if ($this->CountChange($folderid)) {
611
-							SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s'", $folderid));
612
-							$validNotifications = true;
613
-							$this->waitingTime = time() - $started;
614
-						}
615
-						else {
616
-							SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s', but it is not relevant", $folderid));
617
-						}
618
-					}
619
-				}
620
-				if ($validNotifications) {
621
-					return true;
622
-				}
623
-			}
624
-			// use polling mechanism
625
-			else {
626
-				GSync::GetTopCollector()->AnnounceInformation(sprintf("Polling %d/%ds on %s", ($now - $started), $lifetime, $checkClasses));
627
-				if ($this->CountChanges($onlyPingable)) {
628
-					SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Found changes polling"));
629
-
630
-					return true;
631
-				}
632
-
633
-				sleep($nextInterval);
634
-			} // end polling
635
-		} // end wait for changes
636
-		SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): no changes found after %ds", time() - $started));
637
-
638
-		return false;
639
-	}
640
-
641
-	/**
642
-	 * Checks if the currently known collections for
643
-	 * changes performing Exporter->GetChangeCount().
644
-	 *
645
-	 * @param bool $onlyPingable (opt) only check for folders which have the PingableFlag
646
-	 *
647
-	 * @return bool indicating if changes were found or not
648
-	 */
649
-	public function CountChanges($onlyPingable = false) {
650
-		$changesAvailable = false;
651
-		foreach ($this->collections as $folderid => $spa) {
652
-			if ($onlyPingable && $spa->GetPingableFlag() !== true) {
653
-				continue;
654
-			}
655
-
656
-			if (isset($this->addparms[$spa->GetFolderId()]["status"]) && $this->addparms[$spa->GetFolderId()]["status"] != SYNC_STATUS_SUCCESS) {
657
-				continue;
658
-			}
659
-
660
-			if ($this->CountChange($folderid)) {
661
-				$changesAvailable = true;
662
-			}
663
-		}
664
-
665
-		return $changesAvailable;
666
-	}
667
-
668
-	/**
669
-	 * Checks a folder for changes performing Exporter->GetChangeCount().
670
-	 *
671
-	 * @param string $folderid counts changes for a folder
672
-	 *
673
-	 * @return bool indicating if changes were found or not
674
-	 */
675
-	private function CountChange($folderid) {
676
-		$spa = $this->GetCollection($folderid);
677
-
678
-		if (!$spa) {
679
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CountChange(): Could not get SyncParameters object from cache for folderid '%s' to verify notification. Ignoring.", $folderid));
680
-
681
-			return false;
682
-		}
683
-
684
-		$backendFolderId = GSync::GetDeviceManager()->GetBackendIdForFolderId($folderid);
685
-		// switch user store if this is a additional folder (additional true -> do not debug)
686
-		GSync::GetBackend()->Setup(GSync::GetAdditionalSyncFolderStore($backendFolderId, true));
687
-		$changecount = false;
688
-
689
-		try {
690
-			$exporter = GSync::GetBackend()->GetExporter($backendFolderId);
691
-			if ($exporter !== false && isset($this->addparms[$folderid]["state"])) {
692
-				$importer = false;
693
-				$exporter->Config($this->addparms[$folderid]["state"], BACKEND_DISCARD_DATA);
694
-				$exporter->ConfigContentParameters($spa->GetCPO());
695
-				$ret = $exporter->InitializeExporter($importer);
696
-
697
-				if ($ret !== false) {
698
-					$changecount = $exporter->GetChangeCount();
699
-				}
700
-			}
701
-		}
702
-		catch (StatusException $ste) {
703
-			if ($ste->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) {
704
-				SLog::Write(LOGLEVEL_WARN, "SyncCollections->CountChange(): exporter can not be re-configured due to state error, emulating change in folder to force Sync.");
705
-				$this->changes[$folderid] = 1;
706
-				// make sure this folder is fully synched on next Sync request
707
-				$this->invalidateFolderStat($spa);
708
-
709
-				return true;
710
-			}
711
-
712
-			throw new StatusException("SyncCollections->CountChange(): exporter can not be re-configured.", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN);
713
-		}
714
-
715
-		// start over if exporter can not be configured atm
716
-		if ($changecount === false) {
717
-			SLog::Write(LOGLEVEL_WARN, "SyncCollections->CountChange(): no changes received from Exporter.");
718
-		}
719
-
720
-		$this->changes[$folderid] = $changecount;
721
-
722
-		return $changecount > 0;
723
-	}
724
-
725
-	/**
726
-	 * Checks the hierarchy for changes.
727
-	 *
728
-	 * @param bool       export changes, default: false
729
-	 * @param mixed $exportChanges
730
-	 *
731
-	 * @return bool indicating if changes were found or not
732
-	 */
733
-	private function countHierarchyChange($exportChanges = false) {
734
-		$folderid = false;
735
-
736
-		// Check with device manager if the hierarchy should be reloaded.
737
-		// New additional folders are loaded here.
738
-		if (GSync::GetDeviceManager()->IsHierarchySyncRequired()) {
739
-			SLog::Write(LOGLEVEL_DEBUG, "SyncCollections->countHierarchyChange(): DeviceManager says HierarchySync is required.");
740
-
741
-			return true;
742
-		}
743
-
744
-		$changecount = false;
745
-		if ($exportChanges || $this->hierarchyExporterChecked === false) {
746
-			try {
747
-				// if this is a validation (not first run), make sure to load the hierarchy data again
748
-				if ($this->hierarchyExporterChecked === true && !$this->LoadCollection(false, true, false)) {
749
-					throw new StatusException("Invalid states found while re-loading hierarchy data.");
750
-				}
751
-
752
-				$changesMem = GSync::GetDeviceManager()->GetHierarchyChangesWrapper();
753
-				// the hierarchyCache should now fully be initialized - check for changes in the additional folders
754
-				$changesMem->Config(GSync::GetAdditionalSyncFolders(false));
755
-
756
-				// reset backend to the main store
757
-				GSync::GetBackend()->Setup(false);
758
-				$exporter = GSync::GetBackend()->GetExporter();
759
-				if ($exporter !== false && isset($this->addparms[$folderid]["state"])) {
760
-					$exporter->Config($this->addparms[$folderid]["state"]);
761
-					$ret = $exporter->InitializeExporter($changesMem);
762
-					while (is_array($exporter->Synchronize()));
763
-
764
-					if ($ret !== false) {
765
-						$changecount = $changesMem->GetChangeCount();
766
-					}
767
-
768
-					$this->hierarchyExporterChecked = true;
769
-				}
770
-			}
771
-			catch (StatusException $ste) {
772
-				throw new StatusException("SyncCollections->countHierarchyChange(): exporter can not be re-configured.", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN);
773
-			}
774
-
775
-			// start over if exporter can not be configured atm
776
-			if ($changecount === false) {
777
-				SLog::Write(LOGLEVEL_WARN, "SyncCollections->countHierarchyChange(): no changes received from Exporter.");
778
-			}
779
-		}
780
-
781
-		return $changecount > 0;
782
-	}
783
-
784
-	/**
785
-	 * Returns an array with all folderid and the amount of changes found.
786
-	 *
787
-	 * @return array
788
-	 */
789
-	public function GetChangedFolderIds() {
790
-		return $this->changes;
791
-	}
792
-
793
-	/**
794
-	 * Indicates if there are folders which are pingable.
795
-	 *
796
-	 * @return bool
797
-	 */
798
-	public function PingableFolders() {
799
-		foreach ($this->collections as $folderid => $spa) {
800
-			if ($spa->GetPingableFlag() == true) {
801
-				return true;
802
-			}
803
-		}
804
-
805
-		return false;
806
-	}
807
-
808
-	/**
809
-	 * Indicates if the process did wait in a sink, polling or before running a
810
-	 * regular export to find changes.
811
-	 *
812
-	 * @return bool
813
-	 */
814
-	public function WaitedForChanges() {
815
-		SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->WaitedForChanges: waited for %d seconds", $this->waitingTime));
816
-
817
-		return $this->waitingTime > 0;
818
-	}
819
-
820
-	/**
821
-	 * Indicates how many seconds the process did wait in a sink, polling or before running a
822
-	 * regular export to find changes.
823
-	 *
824
-	 * @return int
825
-	 */
826
-	public function GetWaitedSeconds() {
827
-		return $this->waitingTime;
828
-	}
829
-
830
-	/**
831
-	 * Returns how the current folder should be called in the PING comment.
832
-	 *
833
-	 * @param SyncParameters $spa
834
-	 *
835
-	 * @return string
836
-	 */
837
-	private function getPingClass($spa) {
838
-		$class = $spa->GetContentClass();
839
-		if ($class == "Calendar" && strpos($spa->GetFolderId(), DeviceManager::FLD_ORIGIN_GAB) === 0) {
840
-			$class = "GAB";
841
-		}
842
-
843
-		return $class;
844
-	}
845
-
846
-	/**
847
-	 * Simple Iterator Interface implementation to traverse through collections.
848
-	 */
849
-
850
-	/**
851
-	 * Rewind the Iterator to the first element.
852
-	 *
853
-	 * @return
854
-	 */
855
-	public function rewind() {
856
-		return reset($this->collections);
857
-	}
858
-
859
-	/**
860
-	 * Returns the current element.
861
-	 *
862
-	 * @return mixed
863
-	 */
864
-	public function current() {
865
-		return current($this->collections);
866
-	}
867
-
868
-	/**
869
-	 * Return the key of the current element.
870
-	 *
871
-	 * @return scalar on success, or NULL on failure
872
-	 */
873
-	public function key() {
874
-		return key($this->collections);
875
-	}
876
-
877
-	/**
878
-	 * Move forward to next element.
879
-	 *
880
-	 * @return
881
-	 */
882
-	public function next() {
883
-		return next($this->collections);
884
-	}
885
-
886
-	/**
887
-	 * Checks if current position is valid.
888
-	 *
889
-	 * @return bool
890
-	 */
891
-	public function valid() {
892
-		return key($this->collections) != null && key($this->collections) != false;
893
-	}
894
-
895
-	/**
896
-	 * Gets the StateManager from the DeviceManager
897
-	 * if it's not available.
898
-	 *
899
-	 * @return
900
-	 */
901
-	private function loadStateManager() {
902
-		if (!isset($this->stateManager)) {
903
-			$this->stateManager = GSync::GetDeviceManager()->GetStateManager();
904
-		}
905
-	}
906
-
907
-	/**
908
-	 * Remove folder statistics from a SyncParameter object.
909
-	 *
910
-	 * @param SyncParameters $spa
911
-	 *
912
-	 * @return
913
-	 */
914
-	private function invalidateFolderStat($spa) {
915
-		if ($spa->HasFolderStat()) {
916
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->invalidateFolderStat(): removing folder stat '%s' for folderid '%s'", $spa->GetFolderStat(), $spa->GetFolderId()));
917
-			$spa->DelFolderStat();
918
-			$this->SaveCollection($spa);
919
-
920
-			return true;
921
-		}
922
-
923
-		return false;
924
-	}
22
+    public const ERROR_NO_COLLECTIONS = 1;
23
+    public const ERROR_WRONG_HIERARCHY = 2;
24
+    public const OBSOLETE_CONNECTION = 3;
25
+    public const HIERARCHY_CHANGED = 4;
26
+
27
+    private $stateManager;
28
+
29
+    private $collections = [];
30
+    private $addparms = [];
31
+    private $changes = [];
32
+    private $saveData = true;
33
+
34
+    private $refPolicyKey = false;
35
+    private $refLifetime = false;
36
+
37
+    private $globalWindowSize;
38
+    private $lastSyncTime;
39
+
40
+    private $waitingTime = 0;
41
+    private $hierarchyExporterChecked = false;
42
+    private $loggedGlobalWindowSizeOverwrite = false;
43
+
44
+    /**
45
+     * Invalidates all pingable flags for all folders.
46
+     *
47
+     * @return bool
48
+     */
49
+    public static function InvalidatePingableFlags() {
50
+        SLog::Write(LOGLEVEL_DEBUG, "SyncCollections::InvalidatePingableFlags(): Invalidating now");
51
+
52
+        try {
53
+            $sc = new SyncCollections();
54
+            $sc->LoadAllCollections();
55
+            foreach ($sc as $folderid => $spa) {
56
+                if ($spa->GetPingableFlag() == true) {
57
+                    $spa->DelPingableFlag();
58
+                    $sc->SaveCollection($spa);
59
+                }
60
+            }
61
+
62
+            return true;
63
+        }
64
+        catch (GSyncException $e) {
65
+        }
66
+
67
+        return false;
68
+    }
69
+
70
+    /**
71
+     * Constructor.
72
+     */
73
+    public function __construct() {
74
+    }
75
+
76
+    /**
77
+     * Sets the StateManager for this object
78
+     * If this is not done and a method needs it, the StateManager will be
79
+     * requested from the DeviceManager.
80
+     *
81
+     * @param StateManager $statemanager
82
+     *
83
+     * @return
84
+     */
85
+    public function SetStateManager($statemanager) {
86
+        $this->stateManager = $statemanager;
87
+    }
88
+
89
+    /**
90
+     * Loads all collections known for the current device.
91
+     *
92
+     * @param bool $overwriteLoaded  (opt) overwrites Collection with saved state if set to true
93
+     * @param bool $loadState        (opt) indicates if the collection sync state should be loaded, default false
94
+     * @param bool $checkPermissions (opt) if set to true each folder will pass
95
+     *                               through a backend->Setup() to check permissions.
96
+     *                               If this fails a StatusException will be thrown.
97
+     * @param bool $loadHierarchy    (opt) if the hierarchy sync states should be loaded, default false
98
+     * @param bool $confirmedOnly    (opt) indicates if only confirmed states should be loaded, default: false
99
+     *
100
+     * @throws StatusException       with SyncCollections::ERROR_WRONG_HIERARCHY if permission check fails
101
+     * @throws StateInvalidException if the sync state can not be found or relation between states is invalid ($loadState = true)
102
+     *
103
+     * @return bool
104
+     */
105
+    public function LoadAllCollections($overwriteLoaded = false, $loadState = false, $checkPermissions = false, $loadHierarchy = false, $confirmedOnly = false) {
106
+        $this->loadStateManager();
107
+
108
+        // this operation should not remove old state counters
109
+        $this->stateManager->DoNotDeleteOldStates();
110
+
111
+        $invalidStates = false;
112
+        foreach ($this->stateManager->GetSynchedFolders() as $folderid) {
113
+            if ($overwriteLoaded === false && isset($this->collections[$folderid])) {
114
+                continue;
115
+            }
116
+
117
+            // Load Collection!
118
+            if (!$this->LoadCollection($folderid, $loadState, $checkPermissions, $confirmedOnly)) {
119
+                $invalidStates = true;
120
+            }
121
+        }
122
+
123
+        // load the hierarchy data - there are no permissions to verify so we just set it to false
124
+        if ($loadHierarchy && !$this->LoadCollection(false, $loadState, false, false)) {
125
+            throw new StatusException("Invalid states found while loading hierarchy data. Forcing hierarchy sync");
126
+        }
127
+
128
+        if ($invalidStates) {
129
+            throw new StateInvalidException("Invalid states found while loading collections. Forcing sync");
130
+        }
131
+
132
+        return true;
133
+    }
134
+
135
+    /**
136
+     * Loads all collections known for the current device.
137
+     *
138
+     * @param string $folderid         folder id to be loaded
139
+     * @param bool   $loadState        (opt) indicates if the collection sync state should be loaded, default true
140
+     * @param bool   $checkPermissions (opt) if set to true each folder will pass
141
+     *                                 through a backend->Setup() to check permissions.
142
+     *                                 If this fails a StatusException will be thrown.
143
+     * @param bool   $confirmedOnly    (opt) indicates if only confirmed states should be loaded, default: false
144
+     *
145
+     * @throws StatusException       with SyncCollections::ERROR_WRONG_HIERARCHY if permission check fails
146
+     * @throws StateInvalidException if the sync state can not be found or relation between states is invalid ($loadState = true)
147
+     *
148
+     * @return bool
149
+     */
150
+    public function LoadCollection($folderid, $loadState = false, $checkPermissions = false, $confirmedOnly = false) {
151
+        $this->loadStateManager();
152
+
153
+        try {
154
+            // Get SyncParameters for the folder from the state
155
+            $spa = $this->stateManager->GetSynchedFolderState($folderid, !$loadState);
156
+
157
+            // TODO remove resync of folders
158
+            // this forces a resync of all states
159
+            if (!$spa instanceof SyncParameters) {
160
+                throw new StateInvalidException("Saved state are not of type SyncParameters");
161
+            }
162
+
163
+            if ($spa->GetUuidCounter() == 0) {
164
+                SLog::Write(LOGLEVEL_DEBUG, "SyncCollections->LoadCollection(): Found collection with move state only, ignoring.");
165
+
166
+                return true;
167
+            }
168
+        }
169
+        catch (StateInvalidException $sive) {
170
+            // in case there is something wrong with the state, just stop here
171
+            // later when trying to retrieve the SyncParameters nothing will be found
172
+
173
+            if ($folderid === false) {
174
+                throw new StatusException(sprintf("SyncCollections->LoadCollection(): could not get FOLDERDATA state of the hierarchy uuid: %s", $spa->GetUuid()), self::ERROR_WRONG_HIERARCHY);
175
+            }
176
+
177
+            // we also generate a fake change, so a sync on this folder is triggered
178
+            $this->changes[$folderid] = 1;
179
+
180
+            return false;
181
+        }
182
+
183
+        // if this is an additional folder the backend has to be setup correctly
184
+        if ($checkPermissions === true && !GSync::GetBackend()->Setup(GSync::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) {
185
+            throw new StatusException(sprintf("SyncCollections->LoadCollection(): could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), self::ERROR_WRONG_HIERARCHY);
186
+        }
187
+
188
+        // add collection to object
189
+        $addStatus = $this->AddCollection($spa);
190
+
191
+        // load the latest known syncstate if requested
192
+        if ($addStatus && $loadState === true) {
193
+            try {
194
+                // make sure the hierarchy cache is loaded when we are loading hierarchy states
195
+                $this->addparms[$folderid]["state"] = $this->stateManager->GetSyncState($spa->GetLatestSyncKey($confirmedOnly), ($folderid === false));
196
+            }
197
+            catch (StateNotFoundException $snfe) {
198
+                // if we can't find the state, first we should try a sync of that folder, so
199
+                // we generate a fake change, so a sync on this folder is triggered
200
+                $this->changes[$folderid] = 1;
201
+
202
+                // make sure this folder is fully synched on next Sync request
203
+                $this->invalidateFolderStat($spa);
204
+
205
+                return false;
206
+            }
207
+        }
208
+
209
+        return $addStatus;
210
+    }
211
+
212
+    /**
213
+     * Saves a SyncParameters Object.
214
+     *
215
+     * @param SyncParamerts $spa
216
+     *
217
+     * @return bool
218
+     */
219
+    public function SaveCollection($spa) {
220
+        if (!$this->saveData || !$spa->HasFolderId()) {
221
+            return false;
222
+        }
223
+
224
+        if ($spa->IsDataChanged()) {
225
+            $this->loadStateManager();
226
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->SaveCollection(): Data of folder '%s' changed", $spa->GetFolderId()));
227
+
228
+            // save new windowsize
229
+            if (isset($this->globalWindowSize)) {
230
+                $spa->SetWindowSize($this->globalWindowSize);
231
+            }
232
+
233
+            // update latest lifetime
234
+            if (isset($this->refLifetime)) {
235
+                $spa->SetReferenceLifetime($this->refLifetime);
236
+            }
237
+
238
+            return $this->stateManager->SetSynchedFolderState($spa);
239
+        }
240
+
241
+        return false;
242
+    }
243
+
244
+    /**
245
+     * Adds a SyncParameters object to the current list of collections.
246
+     *
247
+     * @param SyncParameters $spa
248
+     *
249
+     * @return bool
250
+     */
251
+    public function AddCollection($spa) {
252
+        if (!$spa->HasFolderId()) {
253
+            return false;
254
+        }
255
+
256
+        $this->collections[$spa->GetFolderId()] = $spa;
257
+
258
+        SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->AddCollection(): Folder id '%s' : ref. Lifetime '%s', last sync at '%s'", $spa->GetFolderId(), $spa->GetReferenceLifetime(), $spa->GetLastSyncTime()));
259
+        if ($spa->HasLastSyncTime() && $spa->GetLastSyncTime() > $this->lastSyncTime) {
260
+            $this->lastSyncTime = $spa->GetLastSyncTime();
261
+
262
+            // use SyncParameters PolicyKey as reference if available
263
+            if ($spa->HasReferencePolicyKey()) {
264
+                $this->refPolicyKey = $spa->GetReferencePolicyKey();
265
+            }
266
+
267
+            // use SyncParameters LifeTime as reference if available
268
+            if ($spa->HasReferenceLifetime()) {
269
+                $this->refLifetime = $spa->GetReferenceLifetime();
270
+            }
271
+
272
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->AddCollection(): Updated reference PolicyKey '%s', reference Lifetime '%s', Last sync at '%s'", $this->refPolicyKey, $this->refLifetime, $this->lastSyncTime));
273
+        }
274
+
275
+        return true;
276
+    }
277
+
278
+    /**
279
+     * Returns a previousily added or loaded SyncParameters object for a folderid.
280
+     *
281
+     * @param SyncParameters $spa
282
+     * @param mixed          $folderid
283
+     *
284
+     * @return SyncParameters / boolean      false if no SyncParameters object is found for folderid
285
+     */
286
+    public function GetCollection($folderid) {
287
+        if (isset($this->collections[$folderid])) {
288
+            return $this->collections[$folderid];
289
+        }
290
+
291
+        return false;
292
+    }
293
+
294
+    /**
295
+     * Indicates if there are any loaded CPOs.
296
+     *
297
+     * @return bool
298
+     */
299
+    public function HasCollections() {
300
+        return !empty($this->collections);
301
+    }
302
+
303
+    /**
304
+     * Indicates the amount of collections loaded.
305
+     *
306
+     * @return int
307
+     */
308
+    public function GetCollectionCount() {
309
+        return count($this->collections);
310
+    }
311
+
312
+    /**
313
+     * Add a non-permanent key/value pair for a SyncParameters object.
314
+     *
315
+     * @param SyncParameters $spa   target SyncParameters
316
+     * @param string         $key
317
+     * @param mixed          $value
318
+     *
319
+     * @return bool
320
+     */
321
+    public function AddParameter($spa, $key, $value) {
322
+        if (!$spa->HasFolderId()) {
323
+            return false;
324
+        }
325
+
326
+        $folderid = $spa->GetFolderId();
327
+        if (!isset($this->addparms[$folderid])) {
328
+            $this->addparms[$folderid] = [];
329
+        }
330
+
331
+        $this->addparms[$folderid][$key] = $value;
332
+
333
+        return true;
334
+    }
335
+
336
+    /**
337
+     * Returns a previousily set non-permanent value for a SyncParameters object.
338
+     *
339
+     * @param SyncParameters $spa target SyncParameters
340
+     * @param string         $key
341
+     *
342
+     * @return mixed returns 'null' if nothing set
343
+     */
344
+    public function GetParameter($spa, $key) {
345
+        if (!$spa->HasFolderId()) {
346
+            return null;
347
+        }
348
+
349
+        if (isset($this->addparms[$spa->GetFolderId()], $this->addparms[$spa->GetFolderId()][$key])) {
350
+            return $this->addparms[$spa->GetFolderId()][$key];
351
+        }
352
+
353
+        return null;
354
+    }
355
+
356
+    /**
357
+     * Returns the latest known PolicyKey to be used as reference.
358
+     *
359
+     * @return int/boolean       returns false if nothing found in collections
360
+     */
361
+    public function GetReferencePolicyKey() {
362
+        return $this->refPolicyKey;
363
+    }
364
+
365
+    /**
366
+     * Sets a global window size which should be used for all collections
367
+     * in a case of a heartbeat and/or partial sync.
368
+     *
369
+     * @param int $windowsize
370
+     *
371
+     * @return bool
372
+     */
373
+    public function SetGlobalWindowSize($windowsize) {
374
+        $this->globalWindowSize = $windowsize;
375
+
376
+        return true;
377
+    }
378
+
379
+    /**
380
+     * Returns the global window size of items to be exported in total over all
381
+     * requested collections.
382
+     *
383
+     * @return int/boolean          returns requested windows size, 512 (max) or the
384
+     *                              value of config SYNC_MAX_ITEMS if it is lower
385
+     */
386
+    public function GetGlobalWindowSize() {
387
+        // take the requested global windowsize or the max 512 if not defined
388
+        if (isset($this->globalWindowSize)) {
389
+            $globalWindowSize = $this->globalWindowSize;
390
+        }
391
+        else {
392
+            $globalWindowSize = WINDOW_SIZE_MAX; // 512 by default
393
+        }
394
+
395
+        if (defined("SYNC_MAX_ITEMS") && $globalWindowSize > SYNC_MAX_ITEMS) {
396
+            if (!$this->loggedGlobalWindowSizeOverwrite) {
397
+                SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->GetGlobalWindowSize() overwriting requested global window size of %d by %d forced in configuration.", $globalWindowSize, SYNC_MAX_ITEMS));
398
+                $this->loggedGlobalWindowSizeOverwrite = true;
399
+            }
400
+            $globalWindowSize = SYNC_MAX_ITEMS;
401
+        }
402
+
403
+        return $globalWindowSize;
404
+    }
405
+
406
+    /**
407
+     * Sets the lifetime for heartbeat or ping connections.
408
+     *
409
+     * @param int $lifetime time in seconds
410
+     *
411
+     * @return bool
412
+     */
413
+    public function SetLifetime($lifetime) {
414
+        $this->refLifetime = $lifetime;
415
+
416
+        return true;
417
+    }
418
+
419
+    /**
420
+     * Sets the lifetime for heartbeat or ping connections
421
+     * previousily set or saved in a collection.
422
+     *
423
+     * @return int returns PING_HIGHER_BOUND_LIFETIME as default if nothing set or not available.
424
+     *             If PING_HIGHER_BOUND_LIFETIME is not set, returns 600.
425
+     */
426
+    public function GetLifetime() {
427
+        if (!isset($this->refLifetime) || $this->refLifetime === false) {
428
+            if (PING_HIGHER_BOUND_LIFETIME !== false) {
429
+                return PING_HIGHER_BOUND_LIFETIME;
430
+            }
431
+
432
+            return 600;
433
+        }
434
+
435
+        return $this->refLifetime;
436
+    }
437
+
438
+    /**
439
+     * Returns the timestamp of the last synchronization for all
440
+     * loaded collections.
441
+     *
442
+     * @return int timestamp
443
+     */
444
+    public function GetLastSyncTime() {
445
+        return $this->lastSyncTime;
446
+    }
447
+
448
+    /**
449
+     * Checks if the currently known collections for changes for $lifetime seconds.
450
+     * If the backend provides a ChangesSink the sink will be used.
451
+     * If not every $interval seconds an exporter will be configured for each
452
+     * folder to perform GetChangeCount().
453
+     *
454
+     * @param int  $lifetime     (opt) total lifetime to wait for changes / default 600s
455
+     * @param int  $interval     (opt) time between blocking operations of sink or polling / default 30s
456
+     * @param bool $onlyPingable (opt) only check for folders which have the PingableFlag
457
+     *
458
+     * @throws StatusException with code SyncCollections::ERROR_NO_COLLECTIONS if no collections available
459
+     *                         with code SyncCollections::ERROR_WRONG_HIERARCHY if there were errors getting changes
460
+     *
461
+     * @return bool indicating if changes were found
462
+     */
463
+    public function CheckForChanges($lifetime = 600, $interval = 30, $onlyPingable = false) {
464
+        $classes = [];
465
+        foreach ($this->collections as $folderid => $spa) {
466
+            if ($onlyPingable && $spa->GetPingableFlag() !== true || !$folderid) {
467
+                continue;
468
+            }
469
+
470
+            $class = $this->getPingClass($spa);
471
+
472
+            if (!isset($classes[$class])) {
473
+                $classes[$class] = 0;
474
+            }
475
+            ++$classes[$class];
476
+        }
477
+        if (empty($classes)) {
478
+            $checkClasses = "policies only";
479
+        }
480
+        elseif (array_sum($classes) > 4) {
481
+            $checkClasses = "";
482
+            foreach ($classes as $class => $count) {
483
+                if ($count == 1) {
484
+                    $checkClasses .= sprintf("%s ", $class);
485
+                }
486
+                else {
487
+                    $checkClasses .= sprintf("%s(%d) ", $class, $count);
488
+                }
489
+            }
490
+        }
491
+        else {
492
+            $checkClasses = implode(" ", array_keys($classes));
493
+        }
494
+
495
+        $pingTracking = new PingTracking();
496
+        $this->changes = [];
497
+
498
+        GSync::GetDeviceManager()->AnnounceProcessAsPush();
499
+        GSync::GetTopCollector()->AnnounceInformation(sprintf("lifetime %ds", $lifetime), true);
500
+        SLog::Write(LOGLEVEL_INFO, sprintf("SyncCollections->CheckForChanges(): Waiting for %s changes... (lifetime %d seconds)", (empty($classes)) ? 'policy' : 'store', $lifetime));
501
+
502
+        // use changes sink where available
503
+        $changesSink = GSync::GetBackend()->HasChangesSink();
504
+
505
+        // create changessink and check folder stats if there are folders to Ping
506
+        if (!empty($classes)) {
507
+            // initialize all possible folders
508
+            foreach ($this->collections as $folderid => $spa) {
509
+                if (($onlyPingable && $spa->GetPingableFlag() !== true) || !$folderid) {
510
+                    continue;
511
+                }
512
+
513
+                $backendFolderId = $spa->GetBackendFolderId();
514
+
515
+                // get the user store if this is a additional folder
516
+                $store = GSync::GetAdditionalSyncFolderStore($backendFolderId);
517
+
518
+                // initialize sink if no immediate changes were found so far
519
+                if ($changesSink && empty($this->changes)) {
520
+                    GSync::GetBackend()->Setup($store);
521
+                    if (!GSync::GetBackend()->ChangesSinkInitialize($backendFolderId)) {
522
+                        throw new StatusException(sprintf("Error initializing ChangesSink for folder id %s/%s", $folderid, $backendFolderId), self::ERROR_WRONG_HIERARCHY);
523
+                    }
524
+                }
525
+
526
+                // check if the folder stat changed since the last sync, if so generate a change for it (only on first run)
527
+                $currentFolderStat = GSync::GetBackend()->GetFolderStat($store, $backendFolderId);
528
+                if ($this->waitingTime == 0 && GSync::GetBackend()->HasFolderStats() && $currentFolderStat !== false && $spa->IsExporterRunRequired($currentFolderStat, true)) {
529
+                    $this->changes[$spa->GetFolderId()] = 1;
530
+                }
531
+            }
532
+        }
533
+
534
+        if (!empty($this->changes)) {
535
+            SLog::Write(LOGLEVEL_DEBUG, "SyncCollections->CheckForChanges(): Using ChangesSink but found changes verifying the folder stats");
536
+
537
+            return true;
538
+        }
539
+
540
+        // wait for changes
541
+        $started = time();
542
+        $endat = time() + $lifetime;
543
+
544
+        // always use policy key from the request if it was sent
545
+        $policyKey = $this->GetReferencePolicyKey();
546
+        if (Request::WasPolicyKeySent() && Request::GetPolicyKey() != 0) {
547
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("refpolkey:'%s', sent polkey:'%s'", $policyKey, Request::GetPolicyKey()));
548
+            $policyKey = Request::GetPolicyKey();
549
+        }
550
+        while (($now = time()) < $endat) {
551
+            // how long are we waiting for changes
552
+            $this->waitingTime = $now - $started;
553
+
554
+            $nextInterval = $interval;
555
+            // we should not block longer than the lifetime
556
+            if ($endat - $now < $nextInterval) {
557
+                $nextInterval = $endat - $now;
558
+            }
559
+
560
+            // Check if provisioning is necessary
561
+            // if a PolicyKey was sent use it. If not, compare with the ReferencePolicyKey
562
+            if (PROVISIONING === true && $policyKey !== false && GSync::GetProvisioningManager()->ProvisioningRequired($policyKey, true, false)) {
563
+                // the hierarchysync forces provisioning
564
+                throw new StatusException("SyncCollections->CheckForChanges(): Policies or PolicyKey changed. Provisioning required.", self::ERROR_WRONG_HIERARCHY);
565
+            }
566
+
567
+            // Check if a hierarchy sync is necessary
568
+            if ($this->countHierarchyChange()) {
569
+                throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED);
570
+            }
571
+
572
+            // Check if there are newer requests
573
+            // If so, this process should be terminated if more than 60 secs to go
574
+            if ($pingTracking->DoForcePingTimeout()) {
575
+                // do not update CPOs because another process has already read them!
576
+                $this->saveData = false;
577
+
578
+                // more than 60 secs to go?
579
+                if (($now + 60) < $endat) {
580
+                    GSync::GetTopCollector()->AnnounceInformation(sprintf("Forced timeout after %ds", ($now - $started)), true);
581
+
582
+                    throw new StatusException(sprintf("SyncCollections->CheckForChanges(): Timeout forced after %ss from %ss due to other process", ($now - $started), $lifetime), self::OBSOLETE_CONNECTION);
583
+                }
584
+            }
585
+
586
+            // Use changes sink if available
587
+            if ($changesSink) {
588
+                GSync::GetTopCollector()->AnnounceInformation(sprintf("Sink %d/%ds on %s", ($now - $started), $lifetime, $checkClasses));
589
+                $notifications = GSync::GetBackend()->ChangesSink($nextInterval);
590
+
591
+                // how long are we waiting for changes
592
+                $this->waitingTime = time() - $started;
593
+
594
+                $validNotifications = false;
595
+                foreach ($notifications as $backendFolderId) {
596
+                    // Check hierarchy notifications
597
+                    if ($backendFolderId === IBackend::HIERARCHYNOTIFICATION) {
598
+                        // wait two seconds before validating this notification, because it could potentially be made by the mobile and we need some time to update the states.
599
+                        sleep(2);
600
+                        // check received hierarchy notifications by exporting
601
+                        if ($this->countHierarchyChange(true)) {
602
+                            throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED);
603
+                        }
604
+                    }
605
+                    else {
606
+                        // the backend will notify on the backend folderid
607
+                        $folderid = GSync::GetDeviceManager()->GetFolderIdForBackendId($backendFolderId);
608
+
609
+                        // check if the notification on the folder is within our filter
610
+                        if ($this->CountChange($folderid)) {
611
+                            SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s'", $folderid));
612
+                            $validNotifications = true;
613
+                            $this->waitingTime = time() - $started;
614
+                        }
615
+                        else {
616
+                            SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s', but it is not relevant", $folderid));
617
+                        }
618
+                    }
619
+                }
620
+                if ($validNotifications) {
621
+                    return true;
622
+                }
623
+            }
624
+            // use polling mechanism
625
+            else {
626
+                GSync::GetTopCollector()->AnnounceInformation(sprintf("Polling %d/%ds on %s", ($now - $started), $lifetime, $checkClasses));
627
+                if ($this->CountChanges($onlyPingable)) {
628
+                    SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Found changes polling"));
629
+
630
+                    return true;
631
+                }
632
+
633
+                sleep($nextInterval);
634
+            } // end polling
635
+        } // end wait for changes
636
+        SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): no changes found after %ds", time() - $started));
637
+
638
+        return false;
639
+    }
640
+
641
+    /**
642
+     * Checks if the currently known collections for
643
+     * changes performing Exporter->GetChangeCount().
644
+     *
645
+     * @param bool $onlyPingable (opt) only check for folders which have the PingableFlag
646
+     *
647
+     * @return bool indicating if changes were found or not
648
+     */
649
+    public function CountChanges($onlyPingable = false) {
650
+        $changesAvailable = false;
651
+        foreach ($this->collections as $folderid => $spa) {
652
+            if ($onlyPingable && $spa->GetPingableFlag() !== true) {
653
+                continue;
654
+            }
655
+
656
+            if (isset($this->addparms[$spa->GetFolderId()]["status"]) && $this->addparms[$spa->GetFolderId()]["status"] != SYNC_STATUS_SUCCESS) {
657
+                continue;
658
+            }
659
+
660
+            if ($this->CountChange($folderid)) {
661
+                $changesAvailable = true;
662
+            }
663
+        }
664
+
665
+        return $changesAvailable;
666
+    }
667
+
668
+    /**
669
+     * Checks a folder for changes performing Exporter->GetChangeCount().
670
+     *
671
+     * @param string $folderid counts changes for a folder
672
+     *
673
+     * @return bool indicating if changes were found or not
674
+     */
675
+    private function CountChange($folderid) {
676
+        $spa = $this->GetCollection($folderid);
677
+
678
+        if (!$spa) {
679
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CountChange(): Could not get SyncParameters object from cache for folderid '%s' to verify notification. Ignoring.", $folderid));
680
+
681
+            return false;
682
+        }
683
+
684
+        $backendFolderId = GSync::GetDeviceManager()->GetBackendIdForFolderId($folderid);
685
+        // switch user store if this is a additional folder (additional true -> do not debug)
686
+        GSync::GetBackend()->Setup(GSync::GetAdditionalSyncFolderStore($backendFolderId, true));
687
+        $changecount = false;
688
+
689
+        try {
690
+            $exporter = GSync::GetBackend()->GetExporter($backendFolderId);
691
+            if ($exporter !== false && isset($this->addparms[$folderid]["state"])) {
692
+                $importer = false;
693
+                $exporter->Config($this->addparms[$folderid]["state"], BACKEND_DISCARD_DATA);
694
+                $exporter->ConfigContentParameters($spa->GetCPO());
695
+                $ret = $exporter->InitializeExporter($importer);
696
+
697
+                if ($ret !== false) {
698
+                    $changecount = $exporter->GetChangeCount();
699
+                }
700
+            }
701
+        }
702
+        catch (StatusException $ste) {
703
+            if ($ste->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) {
704
+                SLog::Write(LOGLEVEL_WARN, "SyncCollections->CountChange(): exporter can not be re-configured due to state error, emulating change in folder to force Sync.");
705
+                $this->changes[$folderid] = 1;
706
+                // make sure this folder is fully synched on next Sync request
707
+                $this->invalidateFolderStat($spa);
708
+
709
+                return true;
710
+            }
711
+
712
+            throw new StatusException("SyncCollections->CountChange(): exporter can not be re-configured.", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN);
713
+        }
714
+
715
+        // start over if exporter can not be configured atm
716
+        if ($changecount === false) {
717
+            SLog::Write(LOGLEVEL_WARN, "SyncCollections->CountChange(): no changes received from Exporter.");
718
+        }
719
+
720
+        $this->changes[$folderid] = $changecount;
721
+
722
+        return $changecount > 0;
723
+    }
724
+
725
+    /**
726
+     * Checks the hierarchy for changes.
727
+     *
728
+     * @param bool       export changes, default: false
729
+     * @param mixed $exportChanges
730
+     *
731
+     * @return bool indicating if changes were found or not
732
+     */
733
+    private function countHierarchyChange($exportChanges = false) {
734
+        $folderid = false;
735
+
736
+        // Check with device manager if the hierarchy should be reloaded.
737
+        // New additional folders are loaded here.
738
+        if (GSync::GetDeviceManager()->IsHierarchySyncRequired()) {
739
+            SLog::Write(LOGLEVEL_DEBUG, "SyncCollections->countHierarchyChange(): DeviceManager says HierarchySync is required.");
740
+
741
+            return true;
742
+        }
743
+
744
+        $changecount = false;
745
+        if ($exportChanges || $this->hierarchyExporterChecked === false) {
746
+            try {
747
+                // if this is a validation (not first run), make sure to load the hierarchy data again
748
+                if ($this->hierarchyExporterChecked === true && !$this->LoadCollection(false, true, false)) {
749
+                    throw new StatusException("Invalid states found while re-loading hierarchy data.");
750
+                }
751
+
752
+                $changesMem = GSync::GetDeviceManager()->GetHierarchyChangesWrapper();
753
+                // the hierarchyCache should now fully be initialized - check for changes in the additional folders
754
+                $changesMem->Config(GSync::GetAdditionalSyncFolders(false));
755
+
756
+                // reset backend to the main store
757
+                GSync::GetBackend()->Setup(false);
758
+                $exporter = GSync::GetBackend()->GetExporter();
759
+                if ($exporter !== false && isset($this->addparms[$folderid]["state"])) {
760
+                    $exporter->Config($this->addparms[$folderid]["state"]);
761
+                    $ret = $exporter->InitializeExporter($changesMem);
762
+                    while (is_array($exporter->Synchronize()));
763
+
764
+                    if ($ret !== false) {
765
+                        $changecount = $changesMem->GetChangeCount();
766
+                    }
767
+
768
+                    $this->hierarchyExporterChecked = true;
769
+                }
770
+            }
771
+            catch (StatusException $ste) {
772
+                throw new StatusException("SyncCollections->countHierarchyChange(): exporter can not be re-configured.", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN);
773
+            }
774
+
775
+            // start over if exporter can not be configured atm
776
+            if ($changecount === false) {
777
+                SLog::Write(LOGLEVEL_WARN, "SyncCollections->countHierarchyChange(): no changes received from Exporter.");
778
+            }
779
+        }
780
+
781
+        return $changecount > 0;
782
+    }
783
+
784
+    /**
785
+     * Returns an array with all folderid and the amount of changes found.
786
+     *
787
+     * @return array
788
+     */
789
+    public function GetChangedFolderIds() {
790
+        return $this->changes;
791
+    }
792
+
793
+    /**
794
+     * Indicates if there are folders which are pingable.
795
+     *
796
+     * @return bool
797
+     */
798
+    public function PingableFolders() {
799
+        foreach ($this->collections as $folderid => $spa) {
800
+            if ($spa->GetPingableFlag() == true) {
801
+                return true;
802
+            }
803
+        }
804
+
805
+        return false;
806
+    }
807
+
808
+    /**
809
+     * Indicates if the process did wait in a sink, polling or before running a
810
+     * regular export to find changes.
811
+     *
812
+     * @return bool
813
+     */
814
+    public function WaitedForChanges() {
815
+        SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->WaitedForChanges: waited for %d seconds", $this->waitingTime));
816
+
817
+        return $this->waitingTime > 0;
818
+    }
819
+
820
+    /**
821
+     * Indicates how many seconds the process did wait in a sink, polling or before running a
822
+     * regular export to find changes.
823
+     *
824
+     * @return int
825
+     */
826
+    public function GetWaitedSeconds() {
827
+        return $this->waitingTime;
828
+    }
829
+
830
+    /**
831
+     * Returns how the current folder should be called in the PING comment.
832
+     *
833
+     * @param SyncParameters $spa
834
+     *
835
+     * @return string
836
+     */
837
+    private function getPingClass($spa) {
838
+        $class = $spa->GetContentClass();
839
+        if ($class == "Calendar" && strpos($spa->GetFolderId(), DeviceManager::FLD_ORIGIN_GAB) === 0) {
840
+            $class = "GAB";
841
+        }
842
+
843
+        return $class;
844
+    }
845
+
846
+    /**
847
+     * Simple Iterator Interface implementation to traverse through collections.
848
+     */
849
+
850
+    /**
851
+     * Rewind the Iterator to the first element.
852
+     *
853
+     * @return
854
+     */
855
+    public function rewind() {
856
+        return reset($this->collections);
857
+    }
858
+
859
+    /**
860
+     * Returns the current element.
861
+     *
862
+     * @return mixed
863
+     */
864
+    public function current() {
865
+        return current($this->collections);
866
+    }
867
+
868
+    /**
869
+     * Return the key of the current element.
870
+     *
871
+     * @return scalar on success, or NULL on failure
872
+     */
873
+    public function key() {
874
+        return key($this->collections);
875
+    }
876
+
877
+    /**
878
+     * Move forward to next element.
879
+     *
880
+     * @return
881
+     */
882
+    public function next() {
883
+        return next($this->collections);
884
+    }
885
+
886
+    /**
887
+     * Checks if current position is valid.
888
+     *
889
+     * @return bool
890
+     */
891
+    public function valid() {
892
+        return key($this->collections) != null && key($this->collections) != false;
893
+    }
894
+
895
+    /**
896
+     * Gets the StateManager from the DeviceManager
897
+     * if it's not available.
898
+     *
899
+     * @return
900
+     */
901
+    private function loadStateManager() {
902
+        if (!isset($this->stateManager)) {
903
+            $this->stateManager = GSync::GetDeviceManager()->GetStateManager();
904
+        }
905
+    }
906
+
907
+    /**
908
+     * Remove folder statistics from a SyncParameter object.
909
+     *
910
+     * @param SyncParameters $spa
911
+     *
912
+     * @return
913
+     */
914
+    private function invalidateFolderStat($spa) {
915
+        if ($spa->HasFolderStat()) {
916
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->invalidateFolderStat(): removing folder stat '%s' for folderid '%s'", $spa->GetFolderStat(), $spa->GetFolderId()));
917
+            $spa->DelFolderStat();
918
+            $this->SaveCollection($spa);
919
+
920
+            return true;
921
+        }
922
+
923
+        return false;
924
+    }
925 925
 }
Please login to merge, or discard this patch.
Braces   +11 added lines, -22 removed lines patch added patch discarded remove patch
@@ -60,8 +60,7 @@  discard block
 block discarded – undo
60 60
 			}
61 61
 
62 62
 			return true;
63
-		}
64
-		catch (GSyncException $e) {
63
+		} catch (GSyncException $e) {
65 64
 		}
66 65
 
67 66
 		return false;
@@ -165,8 +164,7 @@  discard block
 block discarded – undo
165 164
 
166 165
 				return true;
167 166
 			}
168
-		}
169
-		catch (StateInvalidException $sive) {
167
+		} catch (StateInvalidException $sive) {
170 168
 			// in case there is something wrong with the state, just stop here
171 169
 			// later when trying to retrieve the SyncParameters nothing will be found
172 170
 
@@ -193,8 +191,7 @@  discard block
 block discarded – undo
193 191
 			try {
194 192
 				// make sure the hierarchy cache is loaded when we are loading hierarchy states
195 193
 				$this->addparms[$folderid]["state"] = $this->stateManager->GetSyncState($spa->GetLatestSyncKey($confirmedOnly), ($folderid === false));
196
-			}
197
-			catch (StateNotFoundException $snfe) {
194
+			} catch (StateNotFoundException $snfe) {
198 195
 				// if we can't find the state, first we should try a sync of that folder, so
199 196
 				// we generate a fake change, so a sync on this folder is triggered
200 197
 				$this->changes[$folderid] = 1;
@@ -387,8 +384,7 @@  discard block
 block discarded – undo
387 384
 		// take the requested global windowsize or the max 512 if not defined
388 385
 		if (isset($this->globalWindowSize)) {
389 386
 			$globalWindowSize = $this->globalWindowSize;
390
-		}
391
-		else {
387
+		} else {
392 388
 			$globalWindowSize = WINDOW_SIZE_MAX; // 512 by default
393 389
 		}
394 390
 
@@ -476,19 +472,16 @@  discard block
 block discarded – undo
476 472
 		}
477 473
 		if (empty($classes)) {
478 474
 			$checkClasses = "policies only";
479
-		}
480
-		elseif (array_sum($classes) > 4) {
475
+		} elseif (array_sum($classes) > 4) {
481 476
 			$checkClasses = "";
482 477
 			foreach ($classes as $class => $count) {
483 478
 				if ($count == 1) {
484 479
 					$checkClasses .= sprintf("%s ", $class);
485
-				}
486
-				else {
480
+				} else {
487 481
 					$checkClasses .= sprintf("%s(%d) ", $class, $count);
488 482
 				}
489 483
 			}
490
-		}
491
-		else {
484
+		} else {
492 485
 			$checkClasses = implode(" ", array_keys($classes));
493 486
 		}
494 487
 
@@ -601,8 +594,7 @@  discard block
 block discarded – undo
601 594
 						if ($this->countHierarchyChange(true)) {
602 595
 							throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED);
603 596
 						}
604
-					}
605
-					else {
597
+					} else {
606 598
 						// the backend will notify on the backend folderid
607 599
 						$folderid = GSync::GetDeviceManager()->GetFolderIdForBackendId($backendFolderId);
608 600
 
@@ -611,8 +603,7 @@  discard block
 block discarded – undo
611 603
 							SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s'", $folderid));
612 604
 							$validNotifications = true;
613 605
 							$this->waitingTime = time() - $started;
614
-						}
615
-						else {
606
+						} else {
616 607
 							SLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s', but it is not relevant", $folderid));
617 608
 						}
618 609
 					}
@@ -698,8 +689,7 @@  discard block
 block discarded – undo
698 689
 					$changecount = $exporter->GetChangeCount();
699 690
 				}
700 691
 			}
701
-		}
702
-		catch (StatusException $ste) {
692
+		} catch (StatusException $ste) {
703 693
 			if ($ste->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) {
704 694
 				SLog::Write(LOGLEVEL_WARN, "SyncCollections->CountChange(): exporter can not be re-configured due to state error, emulating change in folder to force Sync.");
705 695
 				$this->changes[$folderid] = 1;
@@ -767,8 +757,7 @@  discard block
 block discarded – undo
767 757
 
768 758
 					$this->hierarchyExporterChecked = true;
769 759
 				}
770
-			}
771
-			catch (StatusException $ste) {
760
+			} catch (StatusException $ste) {
772 761
 				throw new StatusException("SyncCollections->countHierarchyChange(): exporter can not be re-configured.", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN);
773 762
 			}
774 763
 
Please login to merge, or discard this patch.
lib/core/streamimporter.php 2 patches
Indentation   +255 added lines, -255 removed lines patch added patch discarded remove patch
@@ -8,259 +8,259 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 class ImportChangesStream implements IImportChanges {
11
-	private $encoder;
12
-	private $objclass;
13
-	private $seenObjects;
14
-	private $importedMsgs;
15
-	private $checkForIgnoredMessages;
16
-	private $classAsString;
17
-
18
-	/**
19
-	 * Constructor of the StreamImporter.
20
-	 *
21
-	 * @param WBXMLEncoder $encoder Objects are streamed to this encoder
22
-	 * @param SyncObject   $class   SyncObject class (only these are accepted when streaming content messages)
23
-	 */
24
-	public function __construct(&$encoder, $class) {
25
-		$this->encoder = &$encoder;
26
-		$this->objclass = $class;
27
-		$this->classAsString = (is_object($class)) ? get_class($class) : '';
28
-		$this->seenObjects = [];
29
-		$this->importedMsgs = 0;
30
-		$this->checkForIgnoredMessages = true;
31
-	}
32
-
33
-	/**
34
-	 * Implement interface - never used.
35
-	 *
36
-	 * @param mixed $state
37
-	 * @param mixed $flags
38
-	 */
39
-	public function Config($state, $flags = 0) {
40
-		return true;
41
-	}
42
-
43
-	public function ConfigContentParameters($contentparameters) {
44
-		return true;
45
-	}
46
-
47
-	public function GetState() {
48
-		return false;
49
-	}
50
-
51
-	public function LoadConflicts($contentparameters, $state) {
52
-		return true;
53
-	}
54
-
55
-	/**
56
-	 * Imports a single message.
57
-	 *
58
-	 * @param string     $id
59
-	 * @param SyncObject $message
60
-	 *
61
-	 * @return bool
62
-	 */
63
-	public function ImportMessageChange($id, $message) {
64
-		// ignore other SyncObjects
65
-		if (!($message instanceof $this->classAsString)) {
66
-			return false;
67
-		}
68
-
69
-		// prevent sending the same object twice in one request
70
-		if (in_array($id, $this->seenObjects)) {
71
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("Object '%s' discarded! Object already sent in this request.", $id));
72
-
73
-			return true;
74
-		}
75
-
76
-		++$this->importedMsgs;
77
-		$this->seenObjects[] = $id;
78
-
79
-		// checks if the next message may cause a loop or is broken
80
-		if (GSync::GetDeviceManager()->DoNotStreamMessage($id, $message)) {
81
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportMessageChange('%s'): message ignored and requested to be removed from mobile", $id));
82
-
83
-			// this is an internal operation & should not trigger an update in the device manager
84
-			$this->checkForIgnoredMessages = false;
85
-			$stat = $this->ImportMessageDeletion($id);
86
-			$this->checkForIgnoredMessages = true;
87
-
88
-			return $stat;
89
-		}
90
-
91
-		if ($message->flags === false || $message->flags === SYNC_NEWMESSAGE) {
92
-			$this->encoder->startTag(SYNC_ADD);
93
-		}
94
-		else {
95
-			// on update of an SyncEmail we only export the flags and categories
96
-			if ($message instanceof SyncMail && ((isset($message->flag) && $message->flag instanceof SyncMailFlags) || isset($message->categories))) {
97
-				$newmessage = new SyncMail();
98
-				$newmessage->read = $message->read;
99
-				if (isset($message->flag)) {
100
-					$newmessage->flag = $message->flag;
101
-				}
102
-				if (isset($message->lastverbexectime)) {
103
-					$newmessage->lastverbexectime = $message->lastverbexectime;
104
-				}
105
-				if (isset($message->lastverbexecuted)) {
106
-					$newmessage->lastverbexecuted = $message->lastverbexecuted;
107
-				}
108
-				if (isset($message->categories)) {
109
-					$newmessage->categories = $message->categories;
110
-				}
111
-				$message = $newmessage;
112
-				unset($newmessage);
113
-				SLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportMessageChange('%s'): SyncMail message updated. Message content is striped, only flags/categories are streamed.", $id));
114
-			}
115
-
116
-			$this->encoder->startTag(SYNC_MODIFY);
117
-		}
118
-
119
-		// TAG: SYNC_ADD / SYNC_MODIFY
120
-		$this->encoder->startTag(SYNC_SERVERENTRYID);
121
-		$this->encoder->content($id);
122
-		$this->encoder->endTag();
123
-		$this->encoder->startTag(SYNC_DATA);
124
-		$message->Encode($this->encoder);
125
-		$this->encoder->endTag();
126
-		$this->encoder->endTag();
127
-
128
-		return true;
129
-	}
130
-
131
-	/**
132
-	 * Imports a deletion.
133
-	 *
134
-	 * @param string $id
135
-	 * @param bool   $asSoftDelete (opt) if true, the deletion is exported as "SoftDelete", else as "Remove" - default: false
136
-	 *
137
-	 * @return bool
138
-	 */
139
-	public function ImportMessageDeletion($id, $asSoftDelete = false) {
140
-		if ($this->checkForIgnoredMessages) {
141
-			GSync::GetDeviceManager()->RemoveBrokenMessage($id);
142
-		}
143
-
144
-		++$this->importedMsgs;
145
-		if ($asSoftDelete) {
146
-			$this->encoder->startTag(SYNC_SOFTDELETE);
147
-		}
148
-		else {
149
-			$this->encoder->startTag(SYNC_REMOVE);
150
-		}
151
-		$this->encoder->startTag(SYNC_SERVERENTRYID);
152
-		$this->encoder->content($id);
153
-		$this->encoder->endTag();
154
-		$this->encoder->endTag();
155
-
156
-		return true;
157
-	}
158
-
159
-	/**
160
-	 * Imports a change in 'read' flag
161
-	 * Can only be applied to SyncMail (Email) requests.
162
-	 *
163
-	 * @param string $id
164
-	 * @param int    $flags      - read/unread
165
-	 * @param array  $categories
166
-	 *
167
-	 * @return bool
168
-	 */
169
-	public function ImportMessageReadFlag($id, $flags, $categories = []) {
170
-		if (!($this->objclass instanceof SyncMail)) {
171
-			return false;
172
-		}
173
-
174
-		++$this->importedMsgs;
175
-
176
-		$this->encoder->startTag(SYNC_MODIFY);
177
-		$this->encoder->startTag(SYNC_SERVERENTRYID);
178
-		$this->encoder->content($id);
179
-		$this->encoder->endTag();
180
-		$this->encoder->startTag(SYNC_DATA);
181
-		$this->encoder->startTag(SYNC_POOMMAIL_READ);
182
-		$this->encoder->content($flags);
183
-		$this->encoder->endTag();
184
-		if (!empty($categories) && is_array($categories)) {
185
-			$this->encoder->startTag(SYNC_POOMMAIL_CATEGORIES);
186
-			foreach ($categories as $category) {
187
-				$this->encoder->startTag(SYNC_POOMMAIL_CATEGORY);
188
-				$this->encoder->content($category);
189
-				$this->encoder->endTag();
190
-			}
191
-			$this->encoder->endTag();
192
-		}
193
-		$this->encoder->endTag();
194
-		$this->encoder->endTag();
195
-
196
-		return true;
197
-	}
198
-
199
-	/**
200
-	 * ImportMessageMove is not implemented, as this operation can not be streamed to a WBXMLEncoder.
201
-	 *
202
-	 * @param string $id
203
-	 * @param int    $flags     read/unread
204
-	 * @param mixed  $newfolder
205
-	 *
206
-	 * @return bool
207
-	 */
208
-	public function ImportMessageMove($id, $newfolder) {
209
-		return true;
210
-	}
211
-
212
-	/**
213
-	 * Imports a change on a folder.
214
-	 *
215
-	 * @param object $folder SyncFolder
216
-	 *
217
-	 * @return boolean/SyncObject           status/object with the ath least the serverid of the folder set
218
-	 */
219
-	public function ImportFolderChange($folder) {
220
-		// checks if the next message may cause a loop or is broken
221
-		if (GSync::GetDeviceManager(false) && GSync::GetDeviceManager()->DoNotStreamMessage($folder->serverid, $folder)) {
222
-			SLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportFolderChange('%s'): folder ignored as requested by DeviceManager.", $folder->serverid));
223
-
224
-			return true;
225
-		}
226
-
227
-		// send a modify flag if the folder is already known on the device
228
-		if (isset($folder->flags) && $folder->flags === SYNC_NEWMESSAGE) {
229
-			$this->encoder->startTag(SYNC_FOLDERHIERARCHY_ADD);
230
-		}
231
-		else {
232
-			$this->encoder->startTag(SYNC_FOLDERHIERARCHY_UPDATE);
233
-		}
234
-
235
-		$folder->Encode($this->encoder);
236
-		$this->encoder->endTag();
237
-
238
-		return true;
239
-	}
240
-
241
-	/**
242
-	 * Imports a folder deletion.
243
-	 *
244
-	 * @param SyncFolder $folder at least "serverid" needs to be set
245
-	 *
246
-	 * @return bool
247
-	 */
248
-	public function ImportFolderDeletion($folder) {
249
-		$this->encoder->startTag(SYNC_FOLDERHIERARCHY_REMOVE);
250
-		$this->encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID);
251
-		$this->encoder->content($folder->serverid);
252
-		$this->encoder->endTag();
253
-		$this->encoder->endTag();
254
-
255
-		return true;
256
-	}
257
-
258
-	/**
259
-	 * Returns the number of messages which were changed, deleted and had changed read status.
260
-	 *
261
-	 * @return int
262
-	 */
263
-	public function GetImportedMessages() {
264
-		return $this->importedMsgs;
265
-	}
11
+    private $encoder;
12
+    private $objclass;
13
+    private $seenObjects;
14
+    private $importedMsgs;
15
+    private $checkForIgnoredMessages;
16
+    private $classAsString;
17
+
18
+    /**
19
+     * Constructor of the StreamImporter.
20
+     *
21
+     * @param WBXMLEncoder $encoder Objects are streamed to this encoder
22
+     * @param SyncObject   $class   SyncObject class (only these are accepted when streaming content messages)
23
+     */
24
+    public function __construct(&$encoder, $class) {
25
+        $this->encoder = &$encoder;
26
+        $this->objclass = $class;
27
+        $this->classAsString = (is_object($class)) ? get_class($class) : '';
28
+        $this->seenObjects = [];
29
+        $this->importedMsgs = 0;
30
+        $this->checkForIgnoredMessages = true;
31
+    }
32
+
33
+    /**
34
+     * Implement interface - never used.
35
+     *
36
+     * @param mixed $state
37
+     * @param mixed $flags
38
+     */
39
+    public function Config($state, $flags = 0) {
40
+        return true;
41
+    }
42
+
43
+    public function ConfigContentParameters($contentparameters) {
44
+        return true;
45
+    }
46
+
47
+    public function GetState() {
48
+        return false;
49
+    }
50
+
51
+    public function LoadConflicts($contentparameters, $state) {
52
+        return true;
53
+    }
54
+
55
+    /**
56
+     * Imports a single message.
57
+     *
58
+     * @param string     $id
59
+     * @param SyncObject $message
60
+     *
61
+     * @return bool
62
+     */
63
+    public function ImportMessageChange($id, $message) {
64
+        // ignore other SyncObjects
65
+        if (!($message instanceof $this->classAsString)) {
66
+            return false;
67
+        }
68
+
69
+        // prevent sending the same object twice in one request
70
+        if (in_array($id, $this->seenObjects)) {
71
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("Object '%s' discarded! Object already sent in this request.", $id));
72
+
73
+            return true;
74
+        }
75
+
76
+        ++$this->importedMsgs;
77
+        $this->seenObjects[] = $id;
78
+
79
+        // checks if the next message may cause a loop or is broken
80
+        if (GSync::GetDeviceManager()->DoNotStreamMessage($id, $message)) {
81
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportMessageChange('%s'): message ignored and requested to be removed from mobile", $id));
82
+
83
+            // this is an internal operation & should not trigger an update in the device manager
84
+            $this->checkForIgnoredMessages = false;
85
+            $stat = $this->ImportMessageDeletion($id);
86
+            $this->checkForIgnoredMessages = true;
87
+
88
+            return $stat;
89
+        }
90
+
91
+        if ($message->flags === false || $message->flags === SYNC_NEWMESSAGE) {
92
+            $this->encoder->startTag(SYNC_ADD);
93
+        }
94
+        else {
95
+            // on update of an SyncEmail we only export the flags and categories
96
+            if ($message instanceof SyncMail && ((isset($message->flag) && $message->flag instanceof SyncMailFlags) || isset($message->categories))) {
97
+                $newmessage = new SyncMail();
98
+                $newmessage->read = $message->read;
99
+                if (isset($message->flag)) {
100
+                    $newmessage->flag = $message->flag;
101
+                }
102
+                if (isset($message->lastverbexectime)) {
103
+                    $newmessage->lastverbexectime = $message->lastverbexectime;
104
+                }
105
+                if (isset($message->lastverbexecuted)) {
106
+                    $newmessage->lastverbexecuted = $message->lastverbexecuted;
107
+                }
108
+                if (isset($message->categories)) {
109
+                    $newmessage->categories = $message->categories;
110
+                }
111
+                $message = $newmessage;
112
+                unset($newmessage);
113
+                SLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportMessageChange('%s'): SyncMail message updated. Message content is striped, only flags/categories are streamed.", $id));
114
+            }
115
+
116
+            $this->encoder->startTag(SYNC_MODIFY);
117
+        }
118
+
119
+        // TAG: SYNC_ADD / SYNC_MODIFY
120
+        $this->encoder->startTag(SYNC_SERVERENTRYID);
121
+        $this->encoder->content($id);
122
+        $this->encoder->endTag();
123
+        $this->encoder->startTag(SYNC_DATA);
124
+        $message->Encode($this->encoder);
125
+        $this->encoder->endTag();
126
+        $this->encoder->endTag();
127
+
128
+        return true;
129
+    }
130
+
131
+    /**
132
+     * Imports a deletion.
133
+     *
134
+     * @param string $id
135
+     * @param bool   $asSoftDelete (opt) if true, the deletion is exported as "SoftDelete", else as "Remove" - default: false
136
+     *
137
+     * @return bool
138
+     */
139
+    public function ImportMessageDeletion($id, $asSoftDelete = false) {
140
+        if ($this->checkForIgnoredMessages) {
141
+            GSync::GetDeviceManager()->RemoveBrokenMessage($id);
142
+        }
143
+
144
+        ++$this->importedMsgs;
145
+        if ($asSoftDelete) {
146
+            $this->encoder->startTag(SYNC_SOFTDELETE);
147
+        }
148
+        else {
149
+            $this->encoder->startTag(SYNC_REMOVE);
150
+        }
151
+        $this->encoder->startTag(SYNC_SERVERENTRYID);
152
+        $this->encoder->content($id);
153
+        $this->encoder->endTag();
154
+        $this->encoder->endTag();
155
+
156
+        return true;
157
+    }
158
+
159
+    /**
160
+     * Imports a change in 'read' flag
161
+     * Can only be applied to SyncMail (Email) requests.
162
+     *
163
+     * @param string $id
164
+     * @param int    $flags      - read/unread
165
+     * @param array  $categories
166
+     *
167
+     * @return bool
168
+     */
169
+    public function ImportMessageReadFlag($id, $flags, $categories = []) {
170
+        if (!($this->objclass instanceof SyncMail)) {
171
+            return false;
172
+        }
173
+
174
+        ++$this->importedMsgs;
175
+
176
+        $this->encoder->startTag(SYNC_MODIFY);
177
+        $this->encoder->startTag(SYNC_SERVERENTRYID);
178
+        $this->encoder->content($id);
179
+        $this->encoder->endTag();
180
+        $this->encoder->startTag(SYNC_DATA);
181
+        $this->encoder->startTag(SYNC_POOMMAIL_READ);
182
+        $this->encoder->content($flags);
183
+        $this->encoder->endTag();
184
+        if (!empty($categories) && is_array($categories)) {
185
+            $this->encoder->startTag(SYNC_POOMMAIL_CATEGORIES);
186
+            foreach ($categories as $category) {
187
+                $this->encoder->startTag(SYNC_POOMMAIL_CATEGORY);
188
+                $this->encoder->content($category);
189
+                $this->encoder->endTag();
190
+            }
191
+            $this->encoder->endTag();
192
+        }
193
+        $this->encoder->endTag();
194
+        $this->encoder->endTag();
195
+
196
+        return true;
197
+    }
198
+
199
+    /**
200
+     * ImportMessageMove is not implemented, as this operation can not be streamed to a WBXMLEncoder.
201
+     *
202
+     * @param string $id
203
+     * @param int    $flags     read/unread
204
+     * @param mixed  $newfolder
205
+     *
206
+     * @return bool
207
+     */
208
+    public function ImportMessageMove($id, $newfolder) {
209
+        return true;
210
+    }
211
+
212
+    /**
213
+     * Imports a change on a folder.
214
+     *
215
+     * @param object $folder SyncFolder
216
+     *
217
+     * @return boolean/SyncObject           status/object with the ath least the serverid of the folder set
218
+     */
219
+    public function ImportFolderChange($folder) {
220
+        // checks if the next message may cause a loop or is broken
221
+        if (GSync::GetDeviceManager(false) && GSync::GetDeviceManager()->DoNotStreamMessage($folder->serverid, $folder)) {
222
+            SLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportFolderChange('%s'): folder ignored as requested by DeviceManager.", $folder->serverid));
223
+
224
+            return true;
225
+        }
226
+
227
+        // send a modify flag if the folder is already known on the device
228
+        if (isset($folder->flags) && $folder->flags === SYNC_NEWMESSAGE) {
229
+            $this->encoder->startTag(SYNC_FOLDERHIERARCHY_ADD);
230
+        }
231
+        else {
232
+            $this->encoder->startTag(SYNC_FOLDERHIERARCHY_UPDATE);
233
+        }
234
+
235
+        $folder->Encode($this->encoder);
236
+        $this->encoder->endTag();
237
+
238
+        return true;
239
+    }
240
+
241
+    /**
242
+     * Imports a folder deletion.
243
+     *
244
+     * @param SyncFolder $folder at least "serverid" needs to be set
245
+     *
246
+     * @return bool
247
+     */
248
+    public function ImportFolderDeletion($folder) {
249
+        $this->encoder->startTag(SYNC_FOLDERHIERARCHY_REMOVE);
250
+        $this->encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID);
251
+        $this->encoder->content($folder->serverid);
252
+        $this->encoder->endTag();
253
+        $this->encoder->endTag();
254
+
255
+        return true;
256
+    }
257
+
258
+    /**
259
+     * Returns the number of messages which were changed, deleted and had changed read status.
260
+     *
261
+     * @return int
262
+     */
263
+    public function GetImportedMessages() {
264
+        return $this->importedMsgs;
265
+    }
266 266
 }
Please login to merge, or discard this patch.
Braces   +3 added lines, -6 removed lines patch added patch discarded remove patch
@@ -90,8 +90,7 @@  discard block
 block discarded – undo
90 90
 
91 91
 		if ($message->flags === false || $message->flags === SYNC_NEWMESSAGE) {
92 92
 			$this->encoder->startTag(SYNC_ADD);
93
-		}
94
-		else {
93
+		} else {
95 94
 			// on update of an SyncEmail we only export the flags and categories
96 95
 			if ($message instanceof SyncMail && ((isset($message->flag) && $message->flag instanceof SyncMailFlags) || isset($message->categories))) {
97 96
 				$newmessage = new SyncMail();
@@ -144,8 +143,7 @@  discard block
 block discarded – undo
144 143
 		++$this->importedMsgs;
145 144
 		if ($asSoftDelete) {
146 145
 			$this->encoder->startTag(SYNC_SOFTDELETE);
147
-		}
148
-		else {
146
+		} else {
149 147
 			$this->encoder->startTag(SYNC_REMOVE);
150 148
 		}
151 149
 		$this->encoder->startTag(SYNC_SERVERENTRYID);
@@ -227,8 +225,7 @@  discard block
 block discarded – undo
227 225
 		// send a modify flag if the folder is already known on the device
228 226
 		if (isset($folder->flags) && $folder->flags === SYNC_NEWMESSAGE) {
229 227
 			$this->encoder->startTag(SYNC_FOLDERHIERARCHY_ADD);
230
-		}
231
-		else {
228
+		} else {
232 229
 			$this->encoder->startTag(SYNC_FOLDERHIERARCHY_UPDATE);
233 230
 		}
234 231
 
Please login to merge, or discard this patch.
lib/core/streamer.php 2 patches
Indentation   +488 added lines, -488 removed lines patch added patch discarded remove patch
@@ -10,495 +10,495 @@
 block discarded – undo
10 10
  */
11 11
 
12 12
 class Streamer implements JsonSerializable {
13
-	public const STREAMER_VAR = 1;
14
-	public const STREAMER_ARRAY = 2;
15
-	public const STREAMER_TYPE = 3;
16
-	public const STREAMER_PROP = 4;
17
-	public const STREAMER_RONOTIFY = 5;
18
-	public const STREAMER_VALUEMAP = 20;
19
-	public const STREAMER_TYPE_DATE = 1;
20
-	public const STREAMER_TYPE_HEX = 2;
21
-	public const STREAMER_TYPE_DATE_DASHES = 3;
22
-	public const STREAMER_TYPE_STREAM = 4; // deprecated
23
-	public const STREAMER_TYPE_IGNORE = 5;
24
-	public const STREAMER_TYPE_SEND_EMPTY = 6;
25
-	public const STREAMER_TYPE_NO_CONTAINER = 7;
26
-	public const STREAMER_TYPE_COMMA_SEPARATED = 8;
27
-	public const STREAMER_TYPE_SEMICOLON_SEPARATED = 9;
28
-	public const STREAMER_TYPE_MULTIPART = 10;
29
-	public const STREAMER_TYPE_STREAM_ASBASE64 = 11;
30
-	public const STREAMER_TYPE_STREAM_ASPLAIN = 12;
31
-	public const STREAMER_PRIVATE = 13;
32
-	public const STRIP_PRIVATE_DATA = 1;
33
-	public const STRIP_PRIVATE_SUBSTITUTE = 'Private';
34
-
35
-	protected $mapping;
36
-	public $flags;
37
-	public $content;
38
-
39
-	/**
40
-	 * Constructor.
41
-	 *
42
-	 * @param array $mapping internal mapping of variables
43
-	 */
44
-	public function __construct($mapping) {
45
-		$this->mapping = $mapping;
46
-		$this->flags = false;
47
-	}
48
-
49
-	/**
50
-	 * Return the streamer mapping for this object.
51
-	 */
52
-	public function GetMapping() {
53
-		return $this->mapping;
54
-	}
55
-
56
-	/**
57
-	 * Decodes the WBXML from a WBXMLdecoder until we reach the same depth level of WBXML.
58
-	 * This means that if there are multiple objects at this level, then only the first is
59
-	 * decoded SubOjects are auto-instantiated and decoded using the same functionality.
60
-	 *
61
-	 * @param WBXMLDecoder $decoder
62
-	 */
63
-	public function Decode(&$decoder) {
64
-		WBXMLDecoder::ResetInWhile("decodeMain");
65
-		while (WBXMLDecoder::InWhile("decodeMain")) {
66
-			$entity = $decoder->getElement();
67
-
68
-			if ($entity[EN_TYPE] == EN_TYPE_STARTTAG) {
69
-				if (!($entity[EN_FLAGS] & EN_FLAGS_CONTENT)) {
70
-					$map = $this->mapping[$entity[EN_TAG]];
71
-					if (isset($map[self::STREAMER_ARRAY])) {
72
-						$this->{$map[self::STREAMER_VAR]} = [];
73
-					}
74
-					elseif (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
75
-						$this->{$map[self::STREAMER_VAR]} = "1";
76
-					}
77
-					elseif (!isset($map[self::STREAMER_TYPE])) {
78
-						$this->{$map[self::STREAMER_VAR]} = "";
79
-					}
80
-					elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES) {
81
-						$this->{$map[self::STREAMER_VAR]} = "";
82
-					}
83
-
84
-					continue;
85
-				}
86
-				// Found a start tag
87
-				if (!isset($this->mapping[$entity[EN_TAG]])) {
88
-					// This tag shouldn't be here, abort
89
-					SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("Tag '%s' unexpected in type XML type '%s'", $entity[EN_TAG], get_class($this)));
90
-
91
-					return false;
92
-				}
93
-
94
-				$map = $this->mapping[$entity[EN_TAG]];
95
-
96
-				// Handle an array
97
-				if (isset($map[self::STREAMER_ARRAY])) {
98
-					WBXMLDecoder::ResetInWhile("decodeArray");
99
-					while (WBXMLDecoder::InWhile("decodeArray")) {
100
-						// do not get start tag for an array without a container
101
-						if (!(isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_NO_CONTAINER)) {
102
-							if (!$decoder->getElementStartTag($map[self::STREAMER_ARRAY])) {
103
-								break;
104
-							}
105
-						}
106
-						if (isset($map[self::STREAMER_TYPE])) {
107
-							$decoded = new $map[self::STREAMER_TYPE]();
108
-
109
-							$decoded->Decode($decoder);
110
-						}
111
-						else {
112
-							$decoded = $decoder->getElementContent();
113
-						}
114
-
115
-						if (!isset($this->{$map[self::STREAMER_VAR]})) {
116
-							$this->{$map[self::STREAMER_VAR]} = [$decoded];
117
-						}
118
-						else {
119
-							array_push($this->{$map[self::STREAMER_VAR]}, $decoded);
120
-						}
121
-
122
-						if (!$decoder->getElementEndTag()) { // end tag of a container element
123
-							return false;
124
-						}
125
-
126
-						if (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_NO_CONTAINER) {
127
-							$e = $decoder->peek();
128
-							// go back to the initial while if another block of no container elements is found
129
-							if ($e[EN_TYPE] == EN_TYPE_STARTTAG) {
130
-								continue 2;
131
-							}
132
-							// break on end tag because no container elements block end is reached
133
-							if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
134
-								break;
135
-							}
136
-							if (empty($e)) {
137
-								break;
138
-							}
139
-						}
140
-					}
141
-					// do not get end tag for an array without a container
142
-					if (!(isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_NO_CONTAINER)) {
143
-						if (!$decoder->getElementEndTag()) { // end tag of container
144
-							return false;
145
-						}
146
-					}
147
-				}
148
-				else { // Handle single value
149
-					if (isset($map[self::STREAMER_TYPE])) {
150
-						// Complex type, decode recursively
151
-						if ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES) {
152
-							$decoded = $this->parseDate($decoder->getElementContent());
153
-							if (!$decoder->getElementEndTag()) {
154
-								return false;
155
-							}
156
-						}
157
-						elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) {
158
-							$decoded = hex2bin($decoder->getElementContent());
159
-							if (!$decoder->getElementEndTag()) {
160
-								return false;
161
-							}
162
-						}
163
-						// explode comma or semicolon strings into arrays
164
-						elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_SEMICOLON_SEPARATED) {
165
-							$glue = ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED) ? ", " : "; ";
166
-							$decoded = explode($glue, $decoder->getElementContent());
167
-							if (!$decoder->getElementEndTag()) {
168
-								return false;
169
-							}
170
-						}
171
-						elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN) {
172
-							$decoded = StringStreamWrapper::Open($decoder->getElementContent());
173
-							if (!$decoder->getElementEndTag()) {
174
-								return false;
175
-							}
176
-						}
177
-						else {
178
-							$subdecoder = new $map[self::STREAMER_TYPE]();
179
-							if ($subdecoder->Decode($decoder) === false) {
180
-								return false;
181
-							}
182
-
183
-							$decoded = $subdecoder;
184
-
185
-							if (!$decoder->getElementEndTag()) {
186
-								SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("No end tag for '%s'", $entity[EN_TAG]));
187
-
188
-								return false;
189
-							}
190
-						}
191
-					}
192
-					else {
193
-						// Simple type, just get content
194
-						$decoded = $decoder->getElementContent();
195
-
196
-						if ($decoded === false) {
197
-							// the tag is declared to have content, but no content is available.
198
-							// set an empty content
199
-							$decoded = "";
200
-						}
201
-
202
-						if (!$decoder->getElementEndTag()) {
203
-							SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("Unable to get end tag for '%s'", $entity[EN_TAG]));
204
-
205
-							return false;
206
-						}
207
-					}
208
-					// $decoded now contains data object (or string)
209
-					$this->{$map[self::STREAMER_VAR]} = $decoded;
210
-				}
211
-			}
212
-			elseif ($entity[EN_TYPE] == EN_TYPE_ENDTAG) {
213
-				$decoder->ungetElement($entity);
214
-
215
-				break;
216
-			}
217
-			else {
218
-				SLog::Write(LOGLEVEL_WBXMLSTACK, "Unexpected content in type");
219
-
220
-				break;
221
-			}
222
-		}
223
-	}
224
-
225
-	/**
226
-	 * Encodes this object and any subobjects - output is ordered according to mapping.
227
-	 *
228
-	 * @param WBXMLEncoder $encoder
229
-	 */
230
-	public function Encode(&$encoder) {
231
-		// A return value if anything was streamed. We need for empty tags.
232
-		$streamed = false;
233
-		foreach ($this->mapping as $tag => $map) {
234
-			if (isset($this->{$map[self::STREAMER_VAR]})) {
235
-				// Variable is available
236
-				if (is_object($this->{$map[self::STREAMER_VAR]})) {
237
-					// Subobjects can do their own encoding
238
-					if ($this->{$map[self::STREAMER_VAR]} instanceof Streamer) {
239
-						$encoder->startTag($tag);
240
-						$res = $this->{$map[self::STREAMER_VAR]}->Encode($encoder);
241
-						$encoder->endTag();
242
-						// nothing was streamed in previous encode but it should be streamed empty anyway
243
-						if (!$res && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
244
-							$encoder->startTag($tag, false, true);
245
-						}
246
-					}
247
-					else {
248
-						SLog::Write(LOGLEVEL_ERROR, sprintf("Streamer->Encode(): parameter '%s' of object %s is not of type Streamer", $map[self::STREAMER_VAR], get_class($this)));
249
-					}
250
-				}
251
-				// Array of objects
252
-				elseif (isset($map[self::STREAMER_ARRAY])) {
253
-					if (empty($this->{$map[self::STREAMER_VAR]}) && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
254
-						$encoder->startTag($tag, false, true);
255
-					}
256
-					else {
257
-						// Outputs array container (eg Attachments)
258
-						// Do not output start and end tag when type is STREAMER_TYPE_NO_CONTAINER
259
-						if (!isset($map[self::STREAMER_PROP]) || $map[self::STREAMER_PROP] != self::STREAMER_TYPE_NO_CONTAINER) {
260
-							$encoder->startTag($tag);
261
-						}
262
-
263
-						foreach ($this->{$map[self::STREAMER_VAR]} as $element) {
264
-							if (is_object($element)) {
265
-								$encoder->startTag($map[self::STREAMER_ARRAY]); // Outputs object container (eg Attachment)
266
-								$element->Encode($encoder);
267
-								$encoder->endTag();
268
-							}
269
-							else {
270
-								if (strlen($element) == 0)
271
-									  // Do not output empty items. Not sure if we should output an empty tag with $encoder->startTag($map[self::STREAMER_ARRAY], false, true);
272
-									  ; else {
273
-									  	$encoder->startTag($map[self::STREAMER_ARRAY]);
274
-									  	$encoder->content($element);
275
-									  	$encoder->endTag();
276
-									  	$streamed = true;
277
-									  }
278
-							}
279
-						}
280
-
281
-						if (!isset($map[self::STREAMER_PROP]) || $map[self::STREAMER_PROP] != self::STREAMER_TYPE_NO_CONTAINER) {
282
-							$encoder->endTag();
283
-						}
284
-					}
285
-				}
286
-				else {
287
-					if (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_IGNORE) {
288
-						continue;
289
-					}
290
-
291
-					if ($encoder->getMultipart() && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_MULTIPART) {
292
-						$encoder->addBodypartStream($this->{$map[self::STREAMER_VAR]});
293
-						$encoder->startTag(SYNC_ITEMOPERATIONS_PART);
294
-						$encoder->content($encoder->getBodypartsCount());
295
-						$encoder->endTag();
296
-
297
-						continue;
298
-					}
299
-
300
-					// Simple type
301
-					if (!isset($map[self::STREAMER_TYPE]) && strlen($this->{$map[self::STREAMER_VAR]}) == 0) {
302
-						// send empty tags
303
-						if (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
304
-							$encoder->startTag($tag, false, true);
305
-						}
306
-
307
-						// Do not output empty items. See above: $encoder->startTag($tag, false, true);
308
-						continue;
309
-					}
310
-					$encoder->startTag($tag);
311
-
312
-					if (isset($map[self::STREAMER_TYPE]) && ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES)) {
313
-						if ($this->{$map[self::STREAMER_VAR]} != 0) { // don't output 1-1-1970
314
-							$encoder->content($this->formatDate($this->{$map[self::STREAMER_VAR]}, $map[self::STREAMER_TYPE]));
315
-						}
316
-					}
317
-					elseif (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) {
318
-						$encoder->content(strtoupper(bin2hex($this->{$map[self::STREAMER_VAR]})));
319
-					}
320
-					elseif (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN) {
321
-						$encoder->contentStream($this->{$map[self::STREAMER_VAR]}, false);
322
-					}
323
-					elseif (isset($map[self::STREAMER_TYPE]) && ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASBASE64 || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM)) {
324
-						$encoder->contentStream($this->{$map[self::STREAMER_VAR]}, true);
325
-					}
326
-					// implode comma or semicolon arrays into a string
327
-					elseif (isset($map[self::STREAMER_TYPE]) && is_array($this->{$map[self::STREAMER_VAR]}) &&
328
-						($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_SEMICOLON_SEPARATED)) {
329
-						$glue = ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED) ? ", " : "; ";
330
-						$encoder->content(implode($glue, $this->{$map[self::STREAMER_VAR]}));
331
-					}
332
-					else {
333
-						$encoder->content($this->{$map[self::STREAMER_VAR]});
334
-					}
335
-					$encoder->endTag();
336
-					$streamed = true;
337
-				}
338
-			}
339
-		}
340
-		// Output our own content
341
-		if (isset($this->content)) {
342
-			$encoder->content($this->content);
343
-		}
344
-
345
-		return $streamed;
346
-	}
347
-
348
-	/**
349
-	 * Removes not necessary data from the object.
350
-	 *
351
-	 * @param mixed $flags
352
-	 *
353
-	 * @return bool
354
-	 */
355
-	public function StripData($flags = 0) {
356
-		foreach ($this->mapping as $k => $v) {
357
-			if (isset($this->{$v[self::STREAMER_VAR]})) {
358
-				if (is_object($this->{$v[self::STREAMER_VAR]}) && method_exists($this->{$v[self::STREAMER_VAR]}, "StripData")) {
359
-					$this->{$v[self::STREAMER_VAR]}->StripData($flags);
360
-				}
361
-				elseif (isset($v[self::STREAMER_ARRAY]) && !empty($this->{$v[self::STREAMER_VAR]})) {
362
-					foreach ($this->{$v[self::STREAMER_VAR]} as $element) {
363
-						if (is_object($element) && method_exists($element, "StripData")) {
364
-							$element->StripData($flags);
365
-						}
366
-						elseif ($flags === Streamer::STRIP_PRIVATE_DATA && isset($v[self::STREAMER_PRIVATE])) {
367
-							if ($v[self::STREAMER_PRIVATE] !== true) {
368
-								$this->{$v[self::STREAMER_VAR]} = $v[self::STREAMER_PRIVATE];
369
-							}
370
-							else {
371
-								unset($this->{$v[self::STREAMER_VAR]});
372
-							}
373
-						}
374
-					}
375
-				}
376
-				elseif ($flags === Streamer::STRIP_PRIVATE_DATA && isset($v[self::STREAMER_PRIVATE])) {
377
-					if ($v[self::STREAMER_PRIVATE] !== true) {
378
-						$this->{$v[self::STREAMER_VAR]} = $v[self::STREAMER_PRIVATE];
379
-					}
380
-					else {
381
-						unset($this->{$v[self::STREAMER_VAR]});
382
-					}
383
-				}
384
-			}
385
-		}
386
-		if ($flags === 0) {
387
-			unset($this->mapping);
388
-		}
389
-
390
-		return true;
391
-	}
392
-
393
-	/**
394
-	 * Returns SyncObject's streamer variable names.
395
-	 *
396
-	 * @return array
397
-	 */
398
-	public function GetStreamerVars() {
399
-		$streamerVars = [];
400
-		foreach ($this->mapping as $v) {
401
-			$streamerVars[] = $v[self::STREAMER_VAR];
402
-		}
403
-
404
-		return $streamerVars;
405
-	}
406
-
407
-	/**
408
-	 * JsonSerializable interface method.
409
-	 *
410
-	 * Serializes the object to a value that can be serialized natively by json_encode()
411
-	 *
412
-	 * @return array
413
-	 */
414
-	public function jsonSerialize() {
415
-		$data = [];
416
-		foreach ($this->mapping as $k => $v) {
417
-			if (isset($this->{$v[self::STREAMER_VAR]})) {
418
-				$data[$v[self::STREAMER_VAR]] = $this->{$v[self::STREAMER_VAR]};
419
-			}
420
-		}
421
-
422
-		return [
423
-			'gsSyncStateClass' => get_class($this),
424
-			'data' => $data,
425
-		];
426
-	}
427
-
428
-	/**
429
-	 * Restores the object from a value provided by json_decode.
430
-	 *
431
-	 * @param $stdObj   stdClass Object
432
-	 */
433
-	public function jsonDeserialize($stdObj) {
434
-		foreach ($stdObj->data as $k => $v) {
435
-			if (is_object($v) && isset($v->gsSyncStateClass)) {
436
-				SLog::Write(LOGLEVEL_DEBUG, sprintf("Streamer->jsonDeserialize(): top class '%s'", $v->gsSyncStateClass));
437
-				$this->{$k} = new $v->gsSyncStateClass();
438
-				$this->{$k}->jsonDeserialize($v);
439
-			}
440
-			else {
441
-				$this->{$k} = $v;
442
-			}
443
-		}
444
-	}
445
-
446
-	/*----------------------------------------------------------------------------------------------------------
13
+    public const STREAMER_VAR = 1;
14
+    public const STREAMER_ARRAY = 2;
15
+    public const STREAMER_TYPE = 3;
16
+    public const STREAMER_PROP = 4;
17
+    public const STREAMER_RONOTIFY = 5;
18
+    public const STREAMER_VALUEMAP = 20;
19
+    public const STREAMER_TYPE_DATE = 1;
20
+    public const STREAMER_TYPE_HEX = 2;
21
+    public const STREAMER_TYPE_DATE_DASHES = 3;
22
+    public const STREAMER_TYPE_STREAM = 4; // deprecated
23
+    public const STREAMER_TYPE_IGNORE = 5;
24
+    public const STREAMER_TYPE_SEND_EMPTY = 6;
25
+    public const STREAMER_TYPE_NO_CONTAINER = 7;
26
+    public const STREAMER_TYPE_COMMA_SEPARATED = 8;
27
+    public const STREAMER_TYPE_SEMICOLON_SEPARATED = 9;
28
+    public const STREAMER_TYPE_MULTIPART = 10;
29
+    public const STREAMER_TYPE_STREAM_ASBASE64 = 11;
30
+    public const STREAMER_TYPE_STREAM_ASPLAIN = 12;
31
+    public const STREAMER_PRIVATE = 13;
32
+    public const STRIP_PRIVATE_DATA = 1;
33
+    public const STRIP_PRIVATE_SUBSTITUTE = 'Private';
34
+
35
+    protected $mapping;
36
+    public $flags;
37
+    public $content;
38
+
39
+    /**
40
+     * Constructor.
41
+     *
42
+     * @param array $mapping internal mapping of variables
43
+     */
44
+    public function __construct($mapping) {
45
+        $this->mapping = $mapping;
46
+        $this->flags = false;
47
+    }
48
+
49
+    /**
50
+     * Return the streamer mapping for this object.
51
+     */
52
+    public function GetMapping() {
53
+        return $this->mapping;
54
+    }
55
+
56
+    /**
57
+     * Decodes the WBXML from a WBXMLdecoder until we reach the same depth level of WBXML.
58
+     * This means that if there are multiple objects at this level, then only the first is
59
+     * decoded SubOjects are auto-instantiated and decoded using the same functionality.
60
+     *
61
+     * @param WBXMLDecoder $decoder
62
+     */
63
+    public function Decode(&$decoder) {
64
+        WBXMLDecoder::ResetInWhile("decodeMain");
65
+        while (WBXMLDecoder::InWhile("decodeMain")) {
66
+            $entity = $decoder->getElement();
67
+
68
+            if ($entity[EN_TYPE] == EN_TYPE_STARTTAG) {
69
+                if (!($entity[EN_FLAGS] & EN_FLAGS_CONTENT)) {
70
+                    $map = $this->mapping[$entity[EN_TAG]];
71
+                    if (isset($map[self::STREAMER_ARRAY])) {
72
+                        $this->{$map[self::STREAMER_VAR]} = [];
73
+                    }
74
+                    elseif (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
75
+                        $this->{$map[self::STREAMER_VAR]} = "1";
76
+                    }
77
+                    elseif (!isset($map[self::STREAMER_TYPE])) {
78
+                        $this->{$map[self::STREAMER_VAR]} = "";
79
+                    }
80
+                    elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES) {
81
+                        $this->{$map[self::STREAMER_VAR]} = "";
82
+                    }
83
+
84
+                    continue;
85
+                }
86
+                // Found a start tag
87
+                if (!isset($this->mapping[$entity[EN_TAG]])) {
88
+                    // This tag shouldn't be here, abort
89
+                    SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("Tag '%s' unexpected in type XML type '%s'", $entity[EN_TAG], get_class($this)));
90
+
91
+                    return false;
92
+                }
93
+
94
+                $map = $this->mapping[$entity[EN_TAG]];
95
+
96
+                // Handle an array
97
+                if (isset($map[self::STREAMER_ARRAY])) {
98
+                    WBXMLDecoder::ResetInWhile("decodeArray");
99
+                    while (WBXMLDecoder::InWhile("decodeArray")) {
100
+                        // do not get start tag for an array without a container
101
+                        if (!(isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_NO_CONTAINER)) {
102
+                            if (!$decoder->getElementStartTag($map[self::STREAMER_ARRAY])) {
103
+                                break;
104
+                            }
105
+                        }
106
+                        if (isset($map[self::STREAMER_TYPE])) {
107
+                            $decoded = new $map[self::STREAMER_TYPE]();
108
+
109
+                            $decoded->Decode($decoder);
110
+                        }
111
+                        else {
112
+                            $decoded = $decoder->getElementContent();
113
+                        }
114
+
115
+                        if (!isset($this->{$map[self::STREAMER_VAR]})) {
116
+                            $this->{$map[self::STREAMER_VAR]} = [$decoded];
117
+                        }
118
+                        else {
119
+                            array_push($this->{$map[self::STREAMER_VAR]}, $decoded);
120
+                        }
121
+
122
+                        if (!$decoder->getElementEndTag()) { // end tag of a container element
123
+                            return false;
124
+                        }
125
+
126
+                        if (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_NO_CONTAINER) {
127
+                            $e = $decoder->peek();
128
+                            // go back to the initial while if another block of no container elements is found
129
+                            if ($e[EN_TYPE] == EN_TYPE_STARTTAG) {
130
+                                continue 2;
131
+                            }
132
+                            // break on end tag because no container elements block end is reached
133
+                            if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
134
+                                break;
135
+                            }
136
+                            if (empty($e)) {
137
+                                break;
138
+                            }
139
+                        }
140
+                    }
141
+                    // do not get end tag for an array without a container
142
+                    if (!(isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_NO_CONTAINER)) {
143
+                        if (!$decoder->getElementEndTag()) { // end tag of container
144
+                            return false;
145
+                        }
146
+                    }
147
+                }
148
+                else { // Handle single value
149
+                    if (isset($map[self::STREAMER_TYPE])) {
150
+                        // Complex type, decode recursively
151
+                        if ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES) {
152
+                            $decoded = $this->parseDate($decoder->getElementContent());
153
+                            if (!$decoder->getElementEndTag()) {
154
+                                return false;
155
+                            }
156
+                        }
157
+                        elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) {
158
+                            $decoded = hex2bin($decoder->getElementContent());
159
+                            if (!$decoder->getElementEndTag()) {
160
+                                return false;
161
+                            }
162
+                        }
163
+                        // explode comma or semicolon strings into arrays
164
+                        elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_SEMICOLON_SEPARATED) {
165
+                            $glue = ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED) ? ", " : "; ";
166
+                            $decoded = explode($glue, $decoder->getElementContent());
167
+                            if (!$decoder->getElementEndTag()) {
168
+                                return false;
169
+                            }
170
+                        }
171
+                        elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN) {
172
+                            $decoded = StringStreamWrapper::Open($decoder->getElementContent());
173
+                            if (!$decoder->getElementEndTag()) {
174
+                                return false;
175
+                            }
176
+                        }
177
+                        else {
178
+                            $subdecoder = new $map[self::STREAMER_TYPE]();
179
+                            if ($subdecoder->Decode($decoder) === false) {
180
+                                return false;
181
+                            }
182
+
183
+                            $decoded = $subdecoder;
184
+
185
+                            if (!$decoder->getElementEndTag()) {
186
+                                SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("No end tag for '%s'", $entity[EN_TAG]));
187
+
188
+                                return false;
189
+                            }
190
+                        }
191
+                    }
192
+                    else {
193
+                        // Simple type, just get content
194
+                        $decoded = $decoder->getElementContent();
195
+
196
+                        if ($decoded === false) {
197
+                            // the tag is declared to have content, but no content is available.
198
+                            // set an empty content
199
+                            $decoded = "";
200
+                        }
201
+
202
+                        if (!$decoder->getElementEndTag()) {
203
+                            SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("Unable to get end tag for '%s'", $entity[EN_TAG]));
204
+
205
+                            return false;
206
+                        }
207
+                    }
208
+                    // $decoded now contains data object (or string)
209
+                    $this->{$map[self::STREAMER_VAR]} = $decoded;
210
+                }
211
+            }
212
+            elseif ($entity[EN_TYPE] == EN_TYPE_ENDTAG) {
213
+                $decoder->ungetElement($entity);
214
+
215
+                break;
216
+            }
217
+            else {
218
+                SLog::Write(LOGLEVEL_WBXMLSTACK, "Unexpected content in type");
219
+
220
+                break;
221
+            }
222
+        }
223
+    }
224
+
225
+    /**
226
+     * Encodes this object and any subobjects - output is ordered according to mapping.
227
+     *
228
+     * @param WBXMLEncoder $encoder
229
+     */
230
+    public function Encode(&$encoder) {
231
+        // A return value if anything was streamed. We need for empty tags.
232
+        $streamed = false;
233
+        foreach ($this->mapping as $tag => $map) {
234
+            if (isset($this->{$map[self::STREAMER_VAR]})) {
235
+                // Variable is available
236
+                if (is_object($this->{$map[self::STREAMER_VAR]})) {
237
+                    // Subobjects can do their own encoding
238
+                    if ($this->{$map[self::STREAMER_VAR]} instanceof Streamer) {
239
+                        $encoder->startTag($tag);
240
+                        $res = $this->{$map[self::STREAMER_VAR]}->Encode($encoder);
241
+                        $encoder->endTag();
242
+                        // nothing was streamed in previous encode but it should be streamed empty anyway
243
+                        if (!$res && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
244
+                            $encoder->startTag($tag, false, true);
245
+                        }
246
+                    }
247
+                    else {
248
+                        SLog::Write(LOGLEVEL_ERROR, sprintf("Streamer->Encode(): parameter '%s' of object %s is not of type Streamer", $map[self::STREAMER_VAR], get_class($this)));
249
+                    }
250
+                }
251
+                // Array of objects
252
+                elseif (isset($map[self::STREAMER_ARRAY])) {
253
+                    if (empty($this->{$map[self::STREAMER_VAR]}) && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
254
+                        $encoder->startTag($tag, false, true);
255
+                    }
256
+                    else {
257
+                        // Outputs array container (eg Attachments)
258
+                        // Do not output start and end tag when type is STREAMER_TYPE_NO_CONTAINER
259
+                        if (!isset($map[self::STREAMER_PROP]) || $map[self::STREAMER_PROP] != self::STREAMER_TYPE_NO_CONTAINER) {
260
+                            $encoder->startTag($tag);
261
+                        }
262
+
263
+                        foreach ($this->{$map[self::STREAMER_VAR]} as $element) {
264
+                            if (is_object($element)) {
265
+                                $encoder->startTag($map[self::STREAMER_ARRAY]); // Outputs object container (eg Attachment)
266
+                                $element->Encode($encoder);
267
+                                $encoder->endTag();
268
+                            }
269
+                            else {
270
+                                if (strlen($element) == 0)
271
+                                        // Do not output empty items. Not sure if we should output an empty tag with $encoder->startTag($map[self::STREAMER_ARRAY], false, true);
272
+                                        ; else {
273
+                                            $encoder->startTag($map[self::STREAMER_ARRAY]);
274
+                                            $encoder->content($element);
275
+                                            $encoder->endTag();
276
+                                            $streamed = true;
277
+                                        }
278
+                            }
279
+                        }
280
+
281
+                        if (!isset($map[self::STREAMER_PROP]) || $map[self::STREAMER_PROP] != self::STREAMER_TYPE_NO_CONTAINER) {
282
+                            $encoder->endTag();
283
+                        }
284
+                    }
285
+                }
286
+                else {
287
+                    if (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_IGNORE) {
288
+                        continue;
289
+                    }
290
+
291
+                    if ($encoder->getMultipart() && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_MULTIPART) {
292
+                        $encoder->addBodypartStream($this->{$map[self::STREAMER_VAR]});
293
+                        $encoder->startTag(SYNC_ITEMOPERATIONS_PART);
294
+                        $encoder->content($encoder->getBodypartsCount());
295
+                        $encoder->endTag();
296
+
297
+                        continue;
298
+                    }
299
+
300
+                    // Simple type
301
+                    if (!isset($map[self::STREAMER_TYPE]) && strlen($this->{$map[self::STREAMER_VAR]}) == 0) {
302
+                        // send empty tags
303
+                        if (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
304
+                            $encoder->startTag($tag, false, true);
305
+                        }
306
+
307
+                        // Do not output empty items. See above: $encoder->startTag($tag, false, true);
308
+                        continue;
309
+                    }
310
+                    $encoder->startTag($tag);
311
+
312
+                    if (isset($map[self::STREAMER_TYPE]) && ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES)) {
313
+                        if ($this->{$map[self::STREAMER_VAR]} != 0) { // don't output 1-1-1970
314
+                            $encoder->content($this->formatDate($this->{$map[self::STREAMER_VAR]}, $map[self::STREAMER_TYPE]));
315
+                        }
316
+                    }
317
+                    elseif (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) {
318
+                        $encoder->content(strtoupper(bin2hex($this->{$map[self::STREAMER_VAR]})));
319
+                    }
320
+                    elseif (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN) {
321
+                        $encoder->contentStream($this->{$map[self::STREAMER_VAR]}, false);
322
+                    }
323
+                    elseif (isset($map[self::STREAMER_TYPE]) && ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASBASE64 || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM)) {
324
+                        $encoder->contentStream($this->{$map[self::STREAMER_VAR]}, true);
325
+                    }
326
+                    // implode comma or semicolon arrays into a string
327
+                    elseif (isset($map[self::STREAMER_TYPE]) && is_array($this->{$map[self::STREAMER_VAR]}) &&
328
+                        ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_SEMICOLON_SEPARATED)) {
329
+                        $glue = ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED) ? ", " : "; ";
330
+                        $encoder->content(implode($glue, $this->{$map[self::STREAMER_VAR]}));
331
+                    }
332
+                    else {
333
+                        $encoder->content($this->{$map[self::STREAMER_VAR]});
334
+                    }
335
+                    $encoder->endTag();
336
+                    $streamed = true;
337
+                }
338
+            }
339
+        }
340
+        // Output our own content
341
+        if (isset($this->content)) {
342
+            $encoder->content($this->content);
343
+        }
344
+
345
+        return $streamed;
346
+    }
347
+
348
+    /**
349
+     * Removes not necessary data from the object.
350
+     *
351
+     * @param mixed $flags
352
+     *
353
+     * @return bool
354
+     */
355
+    public function StripData($flags = 0) {
356
+        foreach ($this->mapping as $k => $v) {
357
+            if (isset($this->{$v[self::STREAMER_VAR]})) {
358
+                if (is_object($this->{$v[self::STREAMER_VAR]}) && method_exists($this->{$v[self::STREAMER_VAR]}, "StripData")) {
359
+                    $this->{$v[self::STREAMER_VAR]}->StripData($flags);
360
+                }
361
+                elseif (isset($v[self::STREAMER_ARRAY]) && !empty($this->{$v[self::STREAMER_VAR]})) {
362
+                    foreach ($this->{$v[self::STREAMER_VAR]} as $element) {
363
+                        if (is_object($element) && method_exists($element, "StripData")) {
364
+                            $element->StripData($flags);
365
+                        }
366
+                        elseif ($flags === Streamer::STRIP_PRIVATE_DATA && isset($v[self::STREAMER_PRIVATE])) {
367
+                            if ($v[self::STREAMER_PRIVATE] !== true) {
368
+                                $this->{$v[self::STREAMER_VAR]} = $v[self::STREAMER_PRIVATE];
369
+                            }
370
+                            else {
371
+                                unset($this->{$v[self::STREAMER_VAR]});
372
+                            }
373
+                        }
374
+                    }
375
+                }
376
+                elseif ($flags === Streamer::STRIP_PRIVATE_DATA && isset($v[self::STREAMER_PRIVATE])) {
377
+                    if ($v[self::STREAMER_PRIVATE] !== true) {
378
+                        $this->{$v[self::STREAMER_VAR]} = $v[self::STREAMER_PRIVATE];
379
+                    }
380
+                    else {
381
+                        unset($this->{$v[self::STREAMER_VAR]});
382
+                    }
383
+                }
384
+            }
385
+        }
386
+        if ($flags === 0) {
387
+            unset($this->mapping);
388
+        }
389
+
390
+        return true;
391
+    }
392
+
393
+    /**
394
+     * Returns SyncObject's streamer variable names.
395
+     *
396
+     * @return array
397
+     */
398
+    public function GetStreamerVars() {
399
+        $streamerVars = [];
400
+        foreach ($this->mapping as $v) {
401
+            $streamerVars[] = $v[self::STREAMER_VAR];
402
+        }
403
+
404
+        return $streamerVars;
405
+    }
406
+
407
+    /**
408
+     * JsonSerializable interface method.
409
+     *
410
+     * Serializes the object to a value that can be serialized natively by json_encode()
411
+     *
412
+     * @return array
413
+     */
414
+    public function jsonSerialize() {
415
+        $data = [];
416
+        foreach ($this->mapping as $k => $v) {
417
+            if (isset($this->{$v[self::STREAMER_VAR]})) {
418
+                $data[$v[self::STREAMER_VAR]] = $this->{$v[self::STREAMER_VAR]};
419
+            }
420
+        }
421
+
422
+        return [
423
+            'gsSyncStateClass' => get_class($this),
424
+            'data' => $data,
425
+        ];
426
+    }
427
+
428
+    /**
429
+     * Restores the object from a value provided by json_decode.
430
+     *
431
+     * @param $stdObj   stdClass Object
432
+     */
433
+    public function jsonDeserialize($stdObj) {
434
+        foreach ($stdObj->data as $k => $v) {
435
+            if (is_object($v) && isset($v->gsSyncStateClass)) {
436
+                SLog::Write(LOGLEVEL_DEBUG, sprintf("Streamer->jsonDeserialize(): top class '%s'", $v->gsSyncStateClass));
437
+                $this->{$k} = new $v->gsSyncStateClass();
438
+                $this->{$k}->jsonDeserialize($v);
439
+            }
440
+            else {
441
+                $this->{$k} = $v;
442
+            }
443
+        }
444
+    }
445
+
446
+    /*----------------------------------------------------------------------------------------------------------
447 447
 	 * Private methods for conversion
448 448
 	 */
449 449
 
450
-	/**
451
-	 * Formats a timestamp
452
-	 * Oh yeah, this is beautiful. Exchange outputs date fields differently in calendar items
453
-	 * and emails. We could just always send one or the other, but unfortunately nokia's 'Mail for
454
-	 *  exchange' depends on this quirk. So we have to send a different date type depending on where
455
-	 * it's used. Sigh.
456
-	 *
457
-	 * @param int $ts
458
-	 * @param int $type
459
-	 *
460
-	 * @return string
461
-	 */
462
-	private function formatDate($ts, $type) {
463
-		if ($type == self::STREAMER_TYPE_DATE) {
464
-			return gmstrftime("%Y%m%dT%H%M%SZ", $ts);
465
-		}
466
-		if ($type == self::STREAMER_TYPE_DATE_DASHES) {
467
-			return gmstrftime("%Y-%m-%dT%H:%M:%S.000Z", $ts);
468
-		}
469
-		// fallback to dashes (should never be reached)
470
-		return gmstrftime("%Y-%m-%dT%H:%M:%S.000Z", $ts);
471
-	}
472
-
473
-	/**
474
-	 * Transforms an AS timestamp into a unix timestamp.
475
-	 *
476
-	 * @param string $ts
477
-	 *
478
-	 * @return long
479
-	 */
480
-	public function parseDate($ts) {
481
-		if (preg_match("/(\\d{4})[^0-9]*(\\d{2})[^0-9]*(\\d{2})(T(\\d{2})[^0-9]*(\\d{2})[^0-9]*(\\d{2})(.\\d+)?Z){0,1}$/", $ts, $matches)) {
482
-			if ($matches[1] >= 2038) {
483
-				$matches[1] = 2038;
484
-				$matches[2] = 1;
485
-				$matches[3] = 18;
486
-				$matches[5] = $matches[6] = $matches[7] = 0;
487
-			}
488
-
489
-			if (!isset($matches[5])) {
490
-				$matches[5] = 0;
491
-			}
492
-			if (!isset($matches[6])) {
493
-				$matches[6] = 0;
494
-			}
495
-			if (!isset($matches[7])) {
496
-				$matches[7] = 0;
497
-			}
498
-
499
-			return gmmktime($matches[5], $matches[6], $matches[7], $matches[2], $matches[3], $matches[1]);
500
-		}
501
-
502
-		return 0;
503
-	}
450
+    /**
451
+     * Formats a timestamp
452
+     * Oh yeah, this is beautiful. Exchange outputs date fields differently in calendar items
453
+     * and emails. We could just always send one or the other, but unfortunately nokia's 'Mail for
454
+     *  exchange' depends on this quirk. So we have to send a different date type depending on where
455
+     * it's used. Sigh.
456
+     *
457
+     * @param int $ts
458
+     * @param int $type
459
+     *
460
+     * @return string
461
+     */
462
+    private function formatDate($ts, $type) {
463
+        if ($type == self::STREAMER_TYPE_DATE) {
464
+            return gmstrftime("%Y%m%dT%H%M%SZ", $ts);
465
+        }
466
+        if ($type == self::STREAMER_TYPE_DATE_DASHES) {
467
+            return gmstrftime("%Y-%m-%dT%H:%M:%S.000Z", $ts);
468
+        }
469
+        // fallback to dashes (should never be reached)
470
+        return gmstrftime("%Y-%m-%dT%H:%M:%S.000Z", $ts);
471
+    }
472
+
473
+    /**
474
+     * Transforms an AS timestamp into a unix timestamp.
475
+     *
476
+     * @param string $ts
477
+     *
478
+     * @return long
479
+     */
480
+    public function parseDate($ts) {
481
+        if (preg_match("/(\\d{4})[^0-9]*(\\d{2})[^0-9]*(\\d{2})(T(\\d{2})[^0-9]*(\\d{2})[^0-9]*(\\d{2})(.\\d+)?Z){0,1}$/", $ts, $matches)) {
482
+            if ($matches[1] >= 2038) {
483
+                $matches[1] = 2038;
484
+                $matches[2] = 1;
485
+                $matches[3] = 18;
486
+                $matches[5] = $matches[6] = $matches[7] = 0;
487
+            }
488
+
489
+            if (!isset($matches[5])) {
490
+                $matches[5] = 0;
491
+            }
492
+            if (!isset($matches[6])) {
493
+                $matches[6] = 0;
494
+            }
495
+            if (!isset($matches[7])) {
496
+                $matches[7] = 0;
497
+            }
498
+
499
+            return gmmktime($matches[5], $matches[6], $matches[7], $matches[2], $matches[3], $matches[1]);
500
+        }
501
+
502
+        return 0;
503
+    }
504 504
 }
Please login to merge, or discard this patch.
Braces   +26 added lines, -52 removed lines patch added patch discarded remove patch
@@ -70,14 +70,11 @@  discard block
 block discarded – undo
70 70
 					$map = $this->mapping[$entity[EN_TAG]];
71 71
 					if (isset($map[self::STREAMER_ARRAY])) {
72 72
 						$this->{$map[self::STREAMER_VAR]} = [];
73
-					}
74
-					elseif (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
73
+					} elseif (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
75 74
 						$this->{$map[self::STREAMER_VAR]} = "1";
76
-					}
77
-					elseif (!isset($map[self::STREAMER_TYPE])) {
75
+					} elseif (!isset($map[self::STREAMER_TYPE])) {
78 76
 						$this->{$map[self::STREAMER_VAR]} = "";
79
-					}
80
-					elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES) {
77
+					} elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES) {
81 78
 						$this->{$map[self::STREAMER_VAR]} = "";
82 79
 					}
83 80
 
@@ -107,15 +104,13 @@  discard block
 block discarded – undo
107 104
 							$decoded = new $map[self::STREAMER_TYPE]();
108 105
 
109 106
 							$decoded->Decode($decoder);
110
-						}
111
-						else {
107
+						} else {
112 108
 							$decoded = $decoder->getElementContent();
113 109
 						}
114 110
 
115 111
 						if (!isset($this->{$map[self::STREAMER_VAR]})) {
116 112
 							$this->{$map[self::STREAMER_VAR]} = [$decoded];
117
-						}
118
-						else {
113
+						} else {
119 114
 							array_push($this->{$map[self::STREAMER_VAR]}, $decoded);
120 115
 						}
121 116
 
@@ -144,8 +139,7 @@  discard block
 block discarded – undo
144 139
 							return false;
145 140
 						}
146 141
 					}
147
-				}
148
-				else { // Handle single value
142
+				} else { // Handle single value
149 143
 					if (isset($map[self::STREAMER_TYPE])) {
150 144
 						// Complex type, decode recursively
151 145
 						if ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES) {
@@ -153,8 +147,7 @@  discard block
 block discarded – undo
153 147
 							if (!$decoder->getElementEndTag()) {
154 148
 								return false;
155 149
 							}
156
-						}
157
-						elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) {
150
+						} elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) {
158 151
 							$decoded = hex2bin($decoder->getElementContent());
159 152
 							if (!$decoder->getElementEndTag()) {
160 153
 								return false;
@@ -167,14 +160,12 @@  discard block
 block discarded – undo
167 160
 							if (!$decoder->getElementEndTag()) {
168 161
 								return false;
169 162
 							}
170
-						}
171
-						elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN) {
163
+						} elseif ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN) {
172 164
 							$decoded = StringStreamWrapper::Open($decoder->getElementContent());
173 165
 							if (!$decoder->getElementEndTag()) {
174 166
 								return false;
175 167
 							}
176
-						}
177
-						else {
168
+						} else {
178 169
 							$subdecoder = new $map[self::STREAMER_TYPE]();
179 170
 							if ($subdecoder->Decode($decoder) === false) {
180 171
 								return false;
@@ -188,8 +179,7 @@  discard block
 block discarded – undo
188 179
 								return false;
189 180
 							}
190 181
 						}
191
-					}
192
-					else {
182
+					} else {
193 183
 						// Simple type, just get content
194 184
 						$decoded = $decoder->getElementContent();
195 185
 
@@ -208,13 +198,11 @@  discard block
 block discarded – undo
208 198
 					// $decoded now contains data object (or string)
209 199
 					$this->{$map[self::STREAMER_VAR]} = $decoded;
210 200
 				}
211
-			}
212
-			elseif ($entity[EN_TYPE] == EN_TYPE_ENDTAG) {
201
+			} elseif ($entity[EN_TYPE] == EN_TYPE_ENDTAG) {
213 202
 				$decoder->ungetElement($entity);
214 203
 
215 204
 				break;
216
-			}
217
-			else {
205
+			} else {
218 206
 				SLog::Write(LOGLEVEL_WBXMLSTACK, "Unexpected content in type");
219 207
 
220 208
 				break;
@@ -243,8 +231,7 @@  discard block
 block discarded – undo
243 231
 						if (!$res && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
244 232
 							$encoder->startTag($tag, false, true);
245 233
 						}
246
-					}
247
-					else {
234
+					} else {
248 235
 						SLog::Write(LOGLEVEL_ERROR, sprintf("Streamer->Encode(): parameter '%s' of object %s is not of type Streamer", $map[self::STREAMER_VAR], get_class($this)));
249 236
 					}
250 237
 				}
@@ -252,8 +239,7 @@  discard block
 block discarded – undo
252 239
 				elseif (isset($map[self::STREAMER_ARRAY])) {
253 240
 					if (empty($this->{$map[self::STREAMER_VAR]}) && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) {
254 241
 						$encoder->startTag($tag, false, true);
255
-					}
256
-					else {
242
+					} else {
257 243
 						// Outputs array container (eg Attachments)
258 244
 						// Do not output start and end tag when type is STREAMER_TYPE_NO_CONTAINER
259 245
 						if (!isset($map[self::STREAMER_PROP]) || $map[self::STREAMER_PROP] != self::STREAMER_TYPE_NO_CONTAINER) {
@@ -265,8 +251,7 @@  discard block
 block discarded – undo
265 251
 								$encoder->startTag($map[self::STREAMER_ARRAY]); // Outputs object container (eg Attachment)
266 252
 								$element->Encode($encoder);
267 253
 								$encoder->endTag();
268
-							}
269
-							else {
254
+							} else {
270 255
 								if (strlen($element) == 0)
271 256
 									  // Do not output empty items. Not sure if we should output an empty tag with $encoder->startTag($map[self::STREAMER_ARRAY], false, true);
272 257
 									  ; else {
@@ -282,8 +267,7 @@  discard block
 block discarded – undo
282 267
 							$encoder->endTag();
283 268
 						}
284 269
 					}
285
-				}
286
-				else {
270
+				} else {
287 271
 					if (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_IGNORE) {
288 272
 						continue;
289 273
 					}
@@ -313,14 +297,11 @@  discard block
 block discarded – undo
313 297
 						if ($this->{$map[self::STREAMER_VAR]} != 0) { // don't output 1-1-1970
314 298
 							$encoder->content($this->formatDate($this->{$map[self::STREAMER_VAR]}, $map[self::STREAMER_TYPE]));
315 299
 						}
316
-					}
317
-					elseif (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) {
300
+					} elseif (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) {
318 301
 						$encoder->content(strtoupper(bin2hex($this->{$map[self::STREAMER_VAR]})));
319
-					}
320
-					elseif (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN) {
302
+					} elseif (isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASPLAIN) {
321 303
 						$encoder->contentStream($this->{$map[self::STREAMER_VAR]}, false);
322
-					}
323
-					elseif (isset($map[self::STREAMER_TYPE]) && ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASBASE64 || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM)) {
304
+					} elseif (isset($map[self::STREAMER_TYPE]) && ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM_ASBASE64 || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM)) {
324 305
 						$encoder->contentStream($this->{$map[self::STREAMER_VAR]}, true);
325 306
 					}
326 307
 					// implode comma or semicolon arrays into a string
@@ -328,8 +309,7 @@  discard block
 block discarded – undo
328 309
 						($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_SEMICOLON_SEPARATED)) {
329 310
 						$glue = ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED) ? ", " : "; ";
330 311
 						$encoder->content(implode($glue, $this->{$map[self::STREAMER_VAR]}));
331
-					}
332
-					else {
312
+					} else {
333 313
 						$encoder->content($this->{$map[self::STREAMER_VAR]});
334 314
 					}
335 315
 					$encoder->endTag();
@@ -357,27 +337,22 @@  discard block
 block discarded – undo
357 337
 			if (isset($this->{$v[self::STREAMER_VAR]})) {
358 338
 				if (is_object($this->{$v[self::STREAMER_VAR]}) && method_exists($this->{$v[self::STREAMER_VAR]}, "StripData")) {
359 339
 					$this->{$v[self::STREAMER_VAR]}->StripData($flags);
360
-				}
361
-				elseif (isset($v[self::STREAMER_ARRAY]) && !empty($this->{$v[self::STREAMER_VAR]})) {
340
+				} elseif (isset($v[self::STREAMER_ARRAY]) && !empty($this->{$v[self::STREAMER_VAR]})) {
362 341
 					foreach ($this->{$v[self::STREAMER_VAR]} as $element) {
363 342
 						if (is_object($element) && method_exists($element, "StripData")) {
364 343
 							$element->StripData($flags);
365
-						}
366
-						elseif ($flags === Streamer::STRIP_PRIVATE_DATA && isset($v[self::STREAMER_PRIVATE])) {
344
+						} elseif ($flags === Streamer::STRIP_PRIVATE_DATA && isset($v[self::STREAMER_PRIVATE])) {
367 345
 							if ($v[self::STREAMER_PRIVATE] !== true) {
368 346
 								$this->{$v[self::STREAMER_VAR]} = $v[self::STREAMER_PRIVATE];
369
-							}
370
-							else {
347
+							} else {
371 348
 								unset($this->{$v[self::STREAMER_VAR]});
372 349
 							}
373 350
 						}
374 351
 					}
375
-				}
376
-				elseif ($flags === Streamer::STRIP_PRIVATE_DATA && isset($v[self::STREAMER_PRIVATE])) {
352
+				} elseif ($flags === Streamer::STRIP_PRIVATE_DATA && isset($v[self::STREAMER_PRIVATE])) {
377 353
 					if ($v[self::STREAMER_PRIVATE] !== true) {
378 354
 						$this->{$v[self::STREAMER_VAR]} = $v[self::STREAMER_PRIVATE];
379
-					}
380
-					else {
355
+					} else {
381 356
 						unset($this->{$v[self::STREAMER_VAR]});
382 357
 					}
383 358
 				}
@@ -436,8 +411,7 @@  discard block
 block discarded – undo
436 411
 				SLog::Write(LOGLEVEL_DEBUG, sprintf("Streamer->jsonDeserialize(): top class '%s'", $v->gsSyncStateClass));
437 412
 				$this->{$k} = new $v->gsSyncStateClass();
438 413
 				$this->{$k}->jsonDeserialize($v);
439
-			}
440
-			else {
414
+			} else {
441 415
 				$this->{$k} = $v;
442 416
 			}
443 417
 		}
Please login to merge, or discard this patch.
lib/core/slog.php 3 patches
Indentation   +192 added lines, -192 removed lines patch added patch discarded remove patch
@@ -8,142 +8,142 @@  discard block
 block discarded – undo
8 8
  */
9 9
 
10 10
 class SLog {
11
-	private static $wbxmlDebug = '';
12
-	private static $lastLogs = [];
13
-
14
-	/**
15
-	 * @var Log
16
-	 */
17
-	private static $logger;
18
-
19
-	/**
20
-	 * Initializes the logging.
21
-	 *
22
-	 * @return bool
23
-	 */
24
-	public static function Initialize() {
25
-		// define some constants for the logging
26
-		if (!defined('LOGUSERLEVEL')) {
27
-			define('LOGUSERLEVEL', LOGLEVEL_OFF);
28
-		}
29
-
30
-		if (!defined('LOGLEVEL')) {
31
-			define('LOGLEVEL', LOGLEVEL_OFF);
32
-		}
33
-
34
-		$logger = self::getLogger();
35
-
36
-		return true;
37
-	}
38
-
39
-	/**
40
-	 * Check if WBXML logging is enabled in current LOG(USER)LEVEL.
41
-	 *
42
-	 * @return bool
43
-	 */
44
-	public static function IsWbxmlDebugEnabled() {
45
-		return LOGLEVEL >= LOGLEVEL_WBXML || (LOGUSERLEVEL >= LOGLEVEL_WBXML && self::getLogger()->HasSpecialLogUsers());
46
-	}
47
-
48
-	/**
49
-	 * Writes a log line.
50
-	 *
51
-	 * @param int    $loglevel one of the defined LOGLEVELS
52
-	 * @param string $message
53
-	 * @param bool   $truncate indicate if the message should be truncated, default true
54
-	 */
55
-	public static function Write($loglevel, $message, $truncate = true) {
56
-		// truncate messages longer than 10 KB
57
-		$messagesize = strlen($message);
58
-		if ($truncate && $messagesize > 10240) {
59
-			$message = substr($message, 0, 10240) . sprintf(" <log message with %d bytes truncated>", $messagesize);
60
-		}
61
-
62
-		self::$lastLogs[$loglevel] = $message;
63
-
64
-		try {
65
-			self::getLogger()->Log($loglevel, $message);
66
-		}
67
-		catch (\Exception $e) {
68
-			// @TODO How should we handle logging error ?
69
-			// Ignore any error.
70
-		}
71
-
72
-		if ($loglevel & LOGLEVEL_WBXMLSTACK) {
73
-			self::$wbxmlDebug .= $message . PHP_EOL;
74
-		}
75
-	}
76
-
77
-	/**
78
-	 * Returns logged information about the WBXML stack.
79
-	 *
80
-	 * @return string
81
-	 */
82
-	public static function GetWBXMLDebugInfo() {
83
-		return trim(self::$wbxmlDebug);
84
-	}
85
-
86
-	/**
87
-	 * Returns the last message logged for a log level.
88
-	 *
89
-	 * @param int $loglevel one of the defined LOGLEVELS
90
-	 *
91
-	 * @return string/false     returns false if there was no message logged in that level
92
-	 */
93
-	public static function GetLastMessage($loglevel) {
94
-		return (isset(self::$lastLogs[$loglevel])) ? self::$lastLogs[$loglevel] : false;
95
-	}
96
-
97
-	/**
98
-	 * If called, the authenticated current user gets an extra log-file.
99
-	 *
100
-	 * If called until the user is authenticated (e.g. at the end of IBackend->Logon()) all log
101
-	 * messages that happened until this point will also be logged.
102
-	 */
103
-	public static function SpecialLogUser() {
104
-		self::getLogger()->SpecialLogUser();
105
-	}
106
-
107
-	/**
108
-	 * Returns the logger object. If no logger has been initialized, FileLog will be initialized and returned.
109
-	 *
110
-	 * @throws Exception thrown if the logger class cannot be instantiated
111
-	 *
112
-	 * @return Log
113
-	 */
114
-	private static function getLogger() {
115
-		if (!self::$logger) {
116
-			global $specialLogUsers; // This variable comes from the configuration file (config.php)
117
-
118
-			$logger = LOGBACKEND_CLASS;
119
-			if (!class_exists($logger)) {
120
-				$errmsg = 'The configured logging class `' . $logger . '` does not exist. Check your configuration.';
121
-				error_log($errmsg);
122
-
123
-				throw new \Exception($errmsg);
124
-			}
125
-
126
-			// if there is an impersonated user it's used instead of the GET user
127
-			if (Request::GetImpersonatedUser()) {
128
-				$user = Request::GetImpersonatedUser();
129
-			}
130
-			else {
131
-				list($user) = Utils::SplitDomainUser(strtolower(Request::GetGETUser()));
132
-			}
133
-
134
-			self::$logger = new $logger();
135
-			self::$logger->SetUser($user);
136
-			self::$logger->SetAuthUser(Request::GetAuthUser());
137
-			self::$logger->SetSpecialLogUsers($specialLogUsers);
138
-			self::$logger->SetDevid(Request::GetDeviceID());
139
-			self::$logger->SetPid(@getmypid());
140
-			self::$logger->AfterInitialize();
141
-		}
142
-
143
-		return self::$logger;
144
-	}
145
-
146
-	/*----------------------------------------------------------------------------------------------------------
11
+    private static $wbxmlDebug = '';
12
+    private static $lastLogs = [];
13
+
14
+    /**
15
+     * @var Log
16
+     */
17
+    private static $logger;
18
+
19
+    /**
20
+     * Initializes the logging.
21
+     *
22
+     * @return bool
23
+     */
24
+    public static function Initialize() {
25
+        // define some constants for the logging
26
+        if (!defined('LOGUSERLEVEL')) {
27
+            define('LOGUSERLEVEL', LOGLEVEL_OFF);
28
+        }
29
+
30
+        if (!defined('LOGLEVEL')) {
31
+            define('LOGLEVEL', LOGLEVEL_OFF);
32
+        }
33
+
34
+        $logger = self::getLogger();
35
+
36
+        return true;
37
+    }
38
+
39
+    /**
40
+     * Check if WBXML logging is enabled in current LOG(USER)LEVEL.
41
+     *
42
+     * @return bool
43
+     */
44
+    public static function IsWbxmlDebugEnabled() {
45
+        return LOGLEVEL >= LOGLEVEL_WBXML || (LOGUSERLEVEL >= LOGLEVEL_WBXML && self::getLogger()->HasSpecialLogUsers());
46
+    }
47
+
48
+    /**
49
+     * Writes a log line.
50
+     *
51
+     * @param int    $loglevel one of the defined LOGLEVELS
52
+     * @param string $message
53
+     * @param bool   $truncate indicate if the message should be truncated, default true
54
+     */
55
+    public static function Write($loglevel, $message, $truncate = true) {
56
+        // truncate messages longer than 10 KB
57
+        $messagesize = strlen($message);
58
+        if ($truncate && $messagesize > 10240) {
59
+            $message = substr($message, 0, 10240) . sprintf(" <log message with %d bytes truncated>", $messagesize);
60
+        }
61
+
62
+        self::$lastLogs[$loglevel] = $message;
63
+
64
+        try {
65
+            self::getLogger()->Log($loglevel, $message);
66
+        }
67
+        catch (\Exception $e) {
68
+            // @TODO How should we handle logging error ?
69
+            // Ignore any error.
70
+        }
71
+
72
+        if ($loglevel & LOGLEVEL_WBXMLSTACK) {
73
+            self::$wbxmlDebug .= $message . PHP_EOL;
74
+        }
75
+    }
76
+
77
+    /**
78
+     * Returns logged information about the WBXML stack.
79
+     *
80
+     * @return string
81
+     */
82
+    public static function GetWBXMLDebugInfo() {
83
+        return trim(self::$wbxmlDebug);
84
+    }
85
+
86
+    /**
87
+     * Returns the last message logged for a log level.
88
+     *
89
+     * @param int $loglevel one of the defined LOGLEVELS
90
+     *
91
+     * @return string/false     returns false if there was no message logged in that level
92
+     */
93
+    public static function GetLastMessage($loglevel) {
94
+        return (isset(self::$lastLogs[$loglevel])) ? self::$lastLogs[$loglevel] : false;
95
+    }
96
+
97
+    /**
98
+     * If called, the authenticated current user gets an extra log-file.
99
+     *
100
+     * If called until the user is authenticated (e.g. at the end of IBackend->Logon()) all log
101
+     * messages that happened until this point will also be logged.
102
+     */
103
+    public static function SpecialLogUser() {
104
+        self::getLogger()->SpecialLogUser();
105
+    }
106
+
107
+    /**
108
+     * Returns the logger object. If no logger has been initialized, FileLog will be initialized and returned.
109
+     *
110
+     * @throws Exception thrown if the logger class cannot be instantiated
111
+     *
112
+     * @return Log
113
+     */
114
+    private static function getLogger() {
115
+        if (!self::$logger) {
116
+            global $specialLogUsers; // This variable comes from the configuration file (config.php)
117
+
118
+            $logger = LOGBACKEND_CLASS;
119
+            if (!class_exists($logger)) {
120
+                $errmsg = 'The configured logging class `' . $logger . '` does not exist. Check your configuration.';
121
+                error_log($errmsg);
122
+
123
+                throw new \Exception($errmsg);
124
+            }
125
+
126
+            // if there is an impersonated user it's used instead of the GET user
127
+            if (Request::GetImpersonatedUser()) {
128
+                $user = Request::GetImpersonatedUser();
129
+            }
130
+            else {
131
+                list($user) = Utils::SplitDomainUser(strtolower(Request::GetGETUser()));
132
+            }
133
+
134
+            self::$logger = new $logger();
135
+            self::$logger->SetUser($user);
136
+            self::$logger->SetAuthUser(Request::GetAuthUser());
137
+            self::$logger->SetSpecialLogUsers($specialLogUsers);
138
+            self::$logger->SetDevid(Request::GetDeviceID());
139
+            self::$logger->SetPid(@getmypid());
140
+            self::$logger->AfterInitialize();
141
+        }
142
+
143
+        return self::$logger;
144
+    }
145
+
146
+    /*----------------------------------------------------------------------------------------------------------
147 147
 	 * private log stuff
148 148
 	 */
149 149
 }
@@ -154,67 +154,67 @@  discard block
 block discarded – undo
154 154
 
155 155
 // TODO review error handler
156 156
 function gsync_error_handler($errno, $errstr, $errfile, $errline) {
157
-	if (defined('LOG_ERROR_MASK')) {
158
-		$errno &= LOG_ERROR_MASK;
159
-	}
160
-
161
-	switch ($errno) {
162
-		case 0:
163
-			// logging disabled by LOG_ERROR_MASK
164
-			break;
165
-
166
-		case E_DEPRECATED:
167
-			// do not handle this message
168
-			break;
169
-
170
-		case E_NOTICE:
171
-		case E_WARNING:
172
-			// TODO check if there is a better way to avoid these messages
173
-			if (stripos($errfile, 'interprocessdata') !== false && stripos($errstr, 'shm_get_var()') !== false) {
174
-				break;
175
-			}
176
-			SLog::Write(LOGLEVEL_WARN, "{$errfile}:{$errline} {$errstr} ({$errno})");
177
-			break;
178
-
179
-		default:
180
-			$bt = debug_backtrace();
181
-			SLog::Write(LOGLEVEL_ERROR, "trace error: {$errfile}:{$errline} {$errstr} ({$errno}) - backtrace: " . (count($bt) - 1) . " steps");
182
-			for ($i = 1, $bt_length = count($bt); $i < $bt_length; ++$i) {
183
-				$file = $line = "unknown";
184
-				if (isset($bt[$i]['file'])) {
185
-					$file = $bt[$i]['file'];
186
-				}
187
-				if (isset($bt[$i]['line'])) {
188
-					$line = $bt[$i]['line'];
189
-				}
190
-				SLog::Write(LOGLEVEL_ERROR, "trace: {$i}:" . $file . ":" . $line . " - " . ((isset($bt[$i]['class'])) ? $bt[$i]['class'] . $bt[$i]['type'] : "") . $bt[$i]['function'] . "()");
191
-			}
192
-			// throw new Exception("An error occurred.");
193
-			break;
194
-	}
157
+    if (defined('LOG_ERROR_MASK')) {
158
+        $errno &= LOG_ERROR_MASK;
159
+    }
160
+
161
+    switch ($errno) {
162
+        case 0:
163
+            // logging disabled by LOG_ERROR_MASK
164
+            break;
165
+
166
+        case E_DEPRECATED:
167
+            // do not handle this message
168
+            break;
169
+
170
+        case E_NOTICE:
171
+        case E_WARNING:
172
+            // TODO check if there is a better way to avoid these messages
173
+            if (stripos($errfile, 'interprocessdata') !== false && stripos($errstr, 'shm_get_var()') !== false) {
174
+                break;
175
+            }
176
+            SLog::Write(LOGLEVEL_WARN, "{$errfile}:{$errline} {$errstr} ({$errno})");
177
+            break;
178
+
179
+        default:
180
+            $bt = debug_backtrace();
181
+            SLog::Write(LOGLEVEL_ERROR, "trace error: {$errfile}:{$errline} {$errstr} ({$errno}) - backtrace: " . (count($bt) - 1) . " steps");
182
+            for ($i = 1, $bt_length = count($bt); $i < $bt_length; ++$i) {
183
+                $file = $line = "unknown";
184
+                if (isset($bt[$i]['file'])) {
185
+                    $file = $bt[$i]['file'];
186
+                }
187
+                if (isset($bt[$i]['line'])) {
188
+                    $line = $bt[$i]['line'];
189
+                }
190
+                SLog::Write(LOGLEVEL_ERROR, "trace: {$i}:" . $file . ":" . $line . " - " . ((isset($bt[$i]['class'])) ? $bt[$i]['class'] . $bt[$i]['type'] : "") . $bt[$i]['function'] . "()");
191
+            }
192
+            // throw new Exception("An error occurred.");
193
+            break;
194
+    }
195 195
 }
196 196
 
197 197
 error_reporting(E_ALL);
198 198
 set_error_handler("gsync_error_handler");
199 199
 
200 200
 function gsync_fatal_handler() {
201
-	$errfile = "unknown file";
202
-	$errstr = "shutdown";
203
-	$errno = E_CORE_ERROR;
204
-	$errline = 0;
205
-
206
-	$error = error_get_last();
207
-
208
-	if ($error !== null) {
209
-		$errno = $error["type"];
210
-		$errfile = $error["file"];
211
-		$errline = $error["line"];
212
-		$errstr = $error["message"];
213
-
214
-		// do NOT log PHP Notice, Warning, Deprecated or Strict as FATAL
215
-		if ($errno & ~(E_NOTICE | E_WARNING | E_DEPRECATED | E_STRICT)) {
216
-			SLog::Write(LOGLEVEL_FATAL, sprintf("Fatal error: %s:%d - %s (%s)", $errfile, $errline, $errstr, $errno));
217
-		}
218
-	}
201
+    $errfile = "unknown file";
202
+    $errstr = "shutdown";
203
+    $errno = E_CORE_ERROR;
204
+    $errline = 0;
205
+
206
+    $error = error_get_last();
207
+
208
+    if ($error !== null) {
209
+        $errno = $error["type"];
210
+        $errfile = $error["file"];
211
+        $errline = $error["line"];
212
+        $errstr = $error["message"];
213
+
214
+        // do NOT log PHP Notice, Warning, Deprecated or Strict as FATAL
215
+        if ($errno & ~(E_NOTICE | E_WARNING | E_DEPRECATED | E_STRICT)) {
216
+            SLog::Write(LOGLEVEL_FATAL, sprintf("Fatal error: %s:%d - %s (%s)", $errfile, $errline, $errstr, $errno));
217
+        }
218
+    }
219 219
 }
220 220
 register_shutdown_function("gsync_fatal_handler");
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -56,7 +56,7 @@  discard block
 block discarded – undo
56 56
 		// truncate messages longer than 10 KB
57 57
 		$messagesize = strlen($message);
58 58
 		if ($truncate && $messagesize > 10240) {
59
-			$message = substr($message, 0, 10240) . sprintf(" <log message with %d bytes truncated>", $messagesize);
59
+			$message = substr($message, 0, 10240).sprintf(" <log message with %d bytes truncated>", $messagesize);
60 60
 		}
61 61
 
62 62
 		self::$lastLogs[$loglevel] = $message;
@@ -70,7 +70,7 @@  discard block
 block discarded – undo
70 70
 		}
71 71
 
72 72
 		if ($loglevel & LOGLEVEL_WBXMLSTACK) {
73
-			self::$wbxmlDebug .= $message . PHP_EOL;
73
+			self::$wbxmlDebug .= $message.PHP_EOL;
74 74
 		}
75 75
 	}
76 76
 
@@ -117,7 +117,7 @@  discard block
 block discarded – undo
117 117
 
118 118
 			$logger = LOGBACKEND_CLASS;
119 119
 			if (!class_exists($logger)) {
120
-				$errmsg = 'The configured logging class `' . $logger . '` does not exist. Check your configuration.';
120
+				$errmsg = 'The configured logging class `'.$logger.'` does not exist. Check your configuration.';
121 121
 				error_log($errmsg);
122 122
 
123 123
 				throw new \Exception($errmsg);
@@ -178,7 +178,7 @@  discard block
 block discarded – undo
178 178
 
179 179
 		default:
180 180
 			$bt = debug_backtrace();
181
-			SLog::Write(LOGLEVEL_ERROR, "trace error: {$errfile}:{$errline} {$errstr} ({$errno}) - backtrace: " . (count($bt) - 1) . " steps");
181
+			SLog::Write(LOGLEVEL_ERROR, "trace error: {$errfile}:{$errline} {$errstr} ({$errno}) - backtrace: ".(count($bt) - 1)." steps");
182 182
 			for ($i = 1, $bt_length = count($bt); $i < $bt_length; ++$i) {
183 183
 				$file = $line = "unknown";
184 184
 				if (isset($bt[$i]['file'])) {
@@ -187,7 +187,7 @@  discard block
 block discarded – undo
187 187
 				if (isset($bt[$i]['line'])) {
188 188
 					$line = $bt[$i]['line'];
189 189
 				}
190
-				SLog::Write(LOGLEVEL_ERROR, "trace: {$i}:" . $file . ":" . $line . " - " . ((isset($bt[$i]['class'])) ? $bt[$i]['class'] . $bt[$i]['type'] : "") . $bt[$i]['function'] . "()");
190
+				SLog::Write(LOGLEVEL_ERROR, "trace: {$i}:".$file.":".$line." - ".((isset($bt[$i]['class'])) ? $bt[$i]['class'].$bt[$i]['type'] : "").$bt[$i]['function']."()");
191 191
 			}
192 192
 			// throw new Exception("An error occurred.");
193 193
 			break;
@@ -212,7 +212,7 @@  discard block
 block discarded – undo
212 212
 		$errstr = $error["message"];
213 213
 
214 214
 		// do NOT log PHP Notice, Warning, Deprecated or Strict as FATAL
215
-		if ($errno & ~(E_NOTICE | E_WARNING | E_DEPRECATED | E_STRICT)) {
215
+		if ($errno & ~(E_NOTICE|E_WARNING|E_DEPRECATED|E_STRICT)) {
216 216
 			SLog::Write(LOGLEVEL_FATAL, sprintf("Fatal error: %s:%d - %s (%s)", $errfile, $errline, $errstr, $errno));
217 217
 		}
218 218
 	}
Please login to merge, or discard this patch.
Braces   +2 added lines, -4 removed lines patch added patch discarded remove patch
@@ -63,8 +63,7 @@  discard block
 block discarded – undo
63 63
 
64 64
 		try {
65 65
 			self::getLogger()->Log($loglevel, $message);
66
-		}
67
-		catch (\Exception $e) {
66
+		} catch (\Exception $e) {
68 67
 			// @TODO How should we handle logging error ?
69 68
 			// Ignore any error.
70 69
 		}
@@ -126,8 +125,7 @@  discard block
 block discarded – undo
126 125
 			// if there is an impersonated user it's used instead of the GET user
127 126
 			if (Request::GetImpersonatedUser()) {
128 127
 				$user = Request::GetImpersonatedUser();
129
-			}
130
-			else {
128
+			} else {
131 129
 				list($user) = Utils::SplitDomainUser(strtolower(Request::GetGETUser()));
132 130
 			}
133 131
 
Please login to merge, or discard this patch.
lib/wbxml/wbxmldecoder.php 3 patches
Indentation   +484 added lines, -484 removed lines patch added patch discarded remove patch
@@ -8,491 +8,491 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 class WBXMLDecoder extends WBXMLDefs {
11
-	private $in;
12
-	private $inLog;
13
-	private $tagcp = 0;
14
-	private $ungetbuffer;
15
-	private $log = false;
16
-	private $logStack = [];
17
-	private $inputBuffer = "";
18
-	private $isWBXML = true;
19
-	private static $loopCounter = [];
20
-	public const MAXLOOP = 5000;
21
-	public const VERSION = 0x03;
22
-
23
-	/**
24
-	 * Counts the amount of times a code part has been executed.
25
-	 * When being executed too often, the code throws a WBMXLException.
26
-	 *
27
-	 * @param string $name
28
-	 *
29
-	 * @throws WBXMLException
30
-	 *
31
-	 * @return bool
32
-	 */
33
-	public static function InWhile($name) {
34
-		if (!isset(self::$loopCounter[$name])) {
35
-			self::$loopCounter[$name] = 0;
36
-		}
37
-		else {
38
-			++self::$loopCounter[$name];
39
-		}
40
-
41
-		if (self::$loopCounter[$name] > self::MAXLOOP) {
42
-			throw new WBXMLException(sprintf("Loop count in while too high, code '%s' exceeded max. amount of permitted loops", $name));
43
-		}
44
-
45
-		return true;
46
-	}
47
-
48
-	/**
49
-	 * Resets the inWhile counter.
50
-	 *
51
-	 * @param string $name
52
-	 *
53
-	 * @return bool
54
-	 */
55
-	public static function ResetInWhile($name) {
56
-		if (isset(self::$loopCounter[$name])) {
57
-			unset(self::$loopCounter[$name]);
58
-		}
59
-
60
-		return true;
61
-	}
62
-
63
-	/**
64
-	 * WBXML Decode Constructor
65
-	 * We only handle ActiveSync WBXML, which is a subset of WBXML.
66
-	 *
67
-	 * @param stream $input the incoming data stream
68
-	 */
69
-	public function __construct($input) {
70
-		$this->log = SLog::IsWbxmlDebugEnabled();
71
-
72
-		$this->in = $input;
73
-
74
-		$version = $this->getByte();
75
-		if ($version != self::VERSION) {
76
-			$this->inputBuffer .= chr($version);
77
-			$this->isWBXML = false;
78
-
79
-			return;
80
-		}
81
-
82
-		$publicid = $this->getMBUInt();
83
-		if ($publicid !== 1) {
84
-			throw new WBXMLException("Wrong publicid : " . $publicid);
85
-		}
86
-
87
-		$charsetid = $this->getMBUInt();
88
-		if ($charsetid !== 106) {
89
-			throw new WBXMLException("Wrong charset : " . $charsetid);
90
-		}
91
-
92
-		$stringtablesize = $this->getMBUInt();
93
-		if ($stringtablesize !== 0) {
94
-			throw new WBXMLException("Wrong string table size : " . $stringtablesize);
95
-		}
96
-	}
97
-
98
-	/**
99
-	 * Returns either start, content or end, and auto-concatenates successive content.
100
-	 *
101
-	 * @return element/value
102
-	 */
103
-	public function getElement() {
104
-		$element = $this->getToken();
105
-
106
-		switch ($element[EN_TYPE]) {
107
-			case EN_TYPE_STARTTAG:
108
-				return $element;
109
-
110
-			case EN_TYPE_ENDTAG:
111
-				return $element;
112
-
113
-			case EN_TYPE_CONTENT:
114
-				WBXMLDecoder::ResetInWhile("decoderGetElement");
115
-				while (WBXMLDecoder::InWhile("decoderGetElement")) {
116
-					$next = $this->getToken();
117
-					if ($next == false) {
118
-						return false;
119
-					}
120
-					if ($next[EN_TYPE] == EN_CONTENT) {
121
-						$element[EN_CONTENT] .= $next[EN_CONTENT];
122
-					}
123
-					else {
124
-						$this->ungetElement($next);
125
-
126
-						break;
127
-					}
128
-				}
129
-
130
-				return $element;
131
-		}
132
-
133
-		return false;
134
-	}
135
-
136
-	/**
137
-	 * Get a peek at the next element.
138
-	 *
139
-	 * @return element
140
-	 */
141
-	public function peek() {
142
-		$element = $this->getElement();
143
-		$this->ungetElement($element);
144
-
145
-		return $element;
146
-	}
147
-
148
-	/**
149
-	 * Get the element of a StartTag.
150
-	 *
151
-	 * @param $tag
152
-	 *
153
-	 * @return element/boolean      returns false if not available
154
-	 */
155
-	public function getElementStartTag($tag) {
156
-		$element = $this->getToken();
157
-
158
-		if (!$element) {
159
-			return false;
160
-		}
161
-
162
-		if ($element[EN_TYPE] == EN_TYPE_STARTTAG && $element[EN_TAG] == $tag) {
163
-			return $element;
164
-		}
165
-
166
-		SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementStartTag(): unmatched WBXML tag: '%s' matching '%s' type '%s' flags '%s'", $tag, ((isset($element[EN_TAG])) ? $element[EN_TAG] : ""), ((isset($element[EN_TYPE])) ? $element[EN_TYPE] : ""), ((isset($element[EN_FLAGS])) ? $element[EN_FLAGS] : "")));
167
-		$this->ungetElement($element);
168
-
169
-		return false;
170
-	}
171
-
172
-	/**
173
-	 * Get the element of a EndTag.
174
-	 *
175
-	 * @return element/boolean      returns false if not available
176
-	 */
177
-	public function getElementEndTag() {
178
-		$element = $this->getToken();
179
-
180
-		if ($element[EN_TYPE] == EN_TYPE_ENDTAG) {
181
-			return $element;
182
-		}
183
-
184
-		SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementEndTag(): unmatched WBXML tag: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG])) ? $element[EN_TAG] : ""), ((isset($element[EN_TYPE])) ? $element[EN_TYPE] : ""), ((isset($element[EN_FLAGS])) ? $element[EN_FLAGS] : "")));
185
-
186
-		$bt = debug_backtrace();
187
-		SLog::Write(LOGLEVEL_ERROR, sprintf("WBXMLDecoder->getElementEndTag(): could not read end tag in '%s'. Please enable the LOGLEVEL_WBXML and send the log to the grommunio-sync dev team.", $bt[0]["file"] . ":" . $bt[0]["line"]));
188
-
189
-		// log the remaining wbxml content
190
-		$this->ungetElement($element);
191
-		while ($el = $this->getElement());
192
-
193
-		return false;
194
-	}
195
-
196
-	/**
197
-	 * Get the content of an element.
198
-	 *
199
-	 * @return string/boolean       returns false if not available
200
-	 */
201
-	public function getElementContent() {
202
-		$element = $this->getToken();
203
-
204
-		if ($element[EN_TYPE] == EN_TYPE_CONTENT) {
205
-			return $element[EN_CONTENT];
206
-		}
207
-
208
-		SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementContent(): unmatched WBXML content: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG])) ? $element[EN_TAG] : ""), ((isset($element[EN_TYPE])) ? $element[EN_TYPE] : ""), ((isset($element[EN_FLAGS])) ? $element[EN_FLAGS] : "")));
209
-		$this->ungetElement($element);
210
-
211
-		return false;
212
-	}
213
-
214
-	/**
215
-	 * 'Ungets' an element writing it into a buffer to be 'get' again.
216
-	 *
217
-	 * @param element $element the element to get ungetten
218
-	 *
219
-	 * @return
220
-	 */
221
-	public function ungetElement($element) {
222
-		if ($this->ungetbuffer) {
223
-			SLog::Write(LOGLEVEL_ERROR, sprintf("WBXMLDecoder->ungetElement(): WBXML double unget on tag: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG])) ? $element[EN_TAG] : ""), ((isset($element[EN_TYPE])) ? $element[EN_TYPE] : ""), ((isset($element[EN_FLAGS])) ? $element[EN_FLAGS] : "")));
224
-		}
225
-
226
-		$this->ungetbuffer = $element;
227
-	}
228
-
229
-	/**
230
-	 * Returns the plain input stream.
231
-	 *
232
-	 * @return string
233
-	 */
234
-	public function GetPlainInputStream() {
235
-		return $this->inputBuffer . stream_get_contents($this->in);
236
-	}
237
-
238
-	/**
239
-	 * Returns if the input is WBXML.
240
-	 *
241
-	 * @return bool
242
-	 */
243
-	public function IsWBXML() {
244
-		return $this->isWBXML;
245
-	}
246
-
247
-	/**
248
-	 * Reads the remaining data from the input stream.
249
-	 */
250
-	public function readRemainingData() {
251
-		SLog::Write(LOGLEVEL_DEBUG, "WBXMLDecoder->readRemainingData() reading remaining data from input stream");
252
-		while ($this->getElement());
253
-	}
254
-
255
-	/*----------------------------------------------------------------------------------------------------------
11
+    private $in;
12
+    private $inLog;
13
+    private $tagcp = 0;
14
+    private $ungetbuffer;
15
+    private $log = false;
16
+    private $logStack = [];
17
+    private $inputBuffer = "";
18
+    private $isWBXML = true;
19
+    private static $loopCounter = [];
20
+    public const MAXLOOP = 5000;
21
+    public const VERSION = 0x03;
22
+
23
+    /**
24
+     * Counts the amount of times a code part has been executed.
25
+     * When being executed too often, the code throws a WBMXLException.
26
+     *
27
+     * @param string $name
28
+     *
29
+     * @throws WBXMLException
30
+     *
31
+     * @return bool
32
+     */
33
+    public static function InWhile($name) {
34
+        if (!isset(self::$loopCounter[$name])) {
35
+            self::$loopCounter[$name] = 0;
36
+        }
37
+        else {
38
+            ++self::$loopCounter[$name];
39
+        }
40
+
41
+        if (self::$loopCounter[$name] > self::MAXLOOP) {
42
+            throw new WBXMLException(sprintf("Loop count in while too high, code '%s' exceeded max. amount of permitted loops", $name));
43
+        }
44
+
45
+        return true;
46
+    }
47
+
48
+    /**
49
+     * Resets the inWhile counter.
50
+     *
51
+     * @param string $name
52
+     *
53
+     * @return bool
54
+     */
55
+    public static function ResetInWhile($name) {
56
+        if (isset(self::$loopCounter[$name])) {
57
+            unset(self::$loopCounter[$name]);
58
+        }
59
+
60
+        return true;
61
+    }
62
+
63
+    /**
64
+     * WBXML Decode Constructor
65
+     * We only handle ActiveSync WBXML, which is a subset of WBXML.
66
+     *
67
+     * @param stream $input the incoming data stream
68
+     */
69
+    public function __construct($input) {
70
+        $this->log = SLog::IsWbxmlDebugEnabled();
71
+
72
+        $this->in = $input;
73
+
74
+        $version = $this->getByte();
75
+        if ($version != self::VERSION) {
76
+            $this->inputBuffer .= chr($version);
77
+            $this->isWBXML = false;
78
+
79
+            return;
80
+        }
81
+
82
+        $publicid = $this->getMBUInt();
83
+        if ($publicid !== 1) {
84
+            throw new WBXMLException("Wrong publicid : " . $publicid);
85
+        }
86
+
87
+        $charsetid = $this->getMBUInt();
88
+        if ($charsetid !== 106) {
89
+            throw new WBXMLException("Wrong charset : " . $charsetid);
90
+        }
91
+
92
+        $stringtablesize = $this->getMBUInt();
93
+        if ($stringtablesize !== 0) {
94
+            throw new WBXMLException("Wrong string table size : " . $stringtablesize);
95
+        }
96
+    }
97
+
98
+    /**
99
+     * Returns either start, content or end, and auto-concatenates successive content.
100
+     *
101
+     * @return element/value
102
+     */
103
+    public function getElement() {
104
+        $element = $this->getToken();
105
+
106
+        switch ($element[EN_TYPE]) {
107
+            case EN_TYPE_STARTTAG:
108
+                return $element;
109
+
110
+            case EN_TYPE_ENDTAG:
111
+                return $element;
112
+
113
+            case EN_TYPE_CONTENT:
114
+                WBXMLDecoder::ResetInWhile("decoderGetElement");
115
+                while (WBXMLDecoder::InWhile("decoderGetElement")) {
116
+                    $next = $this->getToken();
117
+                    if ($next == false) {
118
+                        return false;
119
+                    }
120
+                    if ($next[EN_TYPE] == EN_CONTENT) {
121
+                        $element[EN_CONTENT] .= $next[EN_CONTENT];
122
+                    }
123
+                    else {
124
+                        $this->ungetElement($next);
125
+
126
+                        break;
127
+                    }
128
+                }
129
+
130
+                return $element;
131
+        }
132
+
133
+        return false;
134
+    }
135
+
136
+    /**
137
+     * Get a peek at the next element.
138
+     *
139
+     * @return element
140
+     */
141
+    public function peek() {
142
+        $element = $this->getElement();
143
+        $this->ungetElement($element);
144
+
145
+        return $element;
146
+    }
147
+
148
+    /**
149
+     * Get the element of a StartTag.
150
+     *
151
+     * @param $tag
152
+     *
153
+     * @return element/boolean      returns false if not available
154
+     */
155
+    public function getElementStartTag($tag) {
156
+        $element = $this->getToken();
157
+
158
+        if (!$element) {
159
+            return false;
160
+        }
161
+
162
+        if ($element[EN_TYPE] == EN_TYPE_STARTTAG && $element[EN_TAG] == $tag) {
163
+            return $element;
164
+        }
165
+
166
+        SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementStartTag(): unmatched WBXML tag: '%s' matching '%s' type '%s' flags '%s'", $tag, ((isset($element[EN_TAG])) ? $element[EN_TAG] : ""), ((isset($element[EN_TYPE])) ? $element[EN_TYPE] : ""), ((isset($element[EN_FLAGS])) ? $element[EN_FLAGS] : "")));
167
+        $this->ungetElement($element);
168
+
169
+        return false;
170
+    }
171
+
172
+    /**
173
+     * Get the element of a EndTag.
174
+     *
175
+     * @return element/boolean      returns false if not available
176
+     */
177
+    public function getElementEndTag() {
178
+        $element = $this->getToken();
179
+
180
+        if ($element[EN_TYPE] == EN_TYPE_ENDTAG) {
181
+            return $element;
182
+        }
183
+
184
+        SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementEndTag(): unmatched WBXML tag: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG])) ? $element[EN_TAG] : ""), ((isset($element[EN_TYPE])) ? $element[EN_TYPE] : ""), ((isset($element[EN_FLAGS])) ? $element[EN_FLAGS] : "")));
185
+
186
+        $bt = debug_backtrace();
187
+        SLog::Write(LOGLEVEL_ERROR, sprintf("WBXMLDecoder->getElementEndTag(): could not read end tag in '%s'. Please enable the LOGLEVEL_WBXML and send the log to the grommunio-sync dev team.", $bt[0]["file"] . ":" . $bt[0]["line"]));
188
+
189
+        // log the remaining wbxml content
190
+        $this->ungetElement($element);
191
+        while ($el = $this->getElement());
192
+
193
+        return false;
194
+    }
195
+
196
+    /**
197
+     * Get the content of an element.
198
+     *
199
+     * @return string/boolean       returns false if not available
200
+     */
201
+    public function getElementContent() {
202
+        $element = $this->getToken();
203
+
204
+        if ($element[EN_TYPE] == EN_TYPE_CONTENT) {
205
+            return $element[EN_CONTENT];
206
+        }
207
+
208
+        SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementContent(): unmatched WBXML content: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG])) ? $element[EN_TAG] : ""), ((isset($element[EN_TYPE])) ? $element[EN_TYPE] : ""), ((isset($element[EN_FLAGS])) ? $element[EN_FLAGS] : "")));
209
+        $this->ungetElement($element);
210
+
211
+        return false;
212
+    }
213
+
214
+    /**
215
+     * 'Ungets' an element writing it into a buffer to be 'get' again.
216
+     *
217
+     * @param element $element the element to get ungetten
218
+     *
219
+     * @return
220
+     */
221
+    public function ungetElement($element) {
222
+        if ($this->ungetbuffer) {
223
+            SLog::Write(LOGLEVEL_ERROR, sprintf("WBXMLDecoder->ungetElement(): WBXML double unget on tag: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG])) ? $element[EN_TAG] : ""), ((isset($element[EN_TYPE])) ? $element[EN_TYPE] : ""), ((isset($element[EN_FLAGS])) ? $element[EN_FLAGS] : "")));
224
+        }
225
+
226
+        $this->ungetbuffer = $element;
227
+    }
228
+
229
+    /**
230
+     * Returns the plain input stream.
231
+     *
232
+     * @return string
233
+     */
234
+    public function GetPlainInputStream() {
235
+        return $this->inputBuffer . stream_get_contents($this->in);
236
+    }
237
+
238
+    /**
239
+     * Returns if the input is WBXML.
240
+     *
241
+     * @return bool
242
+     */
243
+    public function IsWBXML() {
244
+        return $this->isWBXML;
245
+    }
246
+
247
+    /**
248
+     * Reads the remaining data from the input stream.
249
+     */
250
+    public function readRemainingData() {
251
+        SLog::Write(LOGLEVEL_DEBUG, "WBXMLDecoder->readRemainingData() reading remaining data from input stream");
252
+        while ($this->getElement());
253
+    }
254
+
255
+    /*----------------------------------------------------------------------------------------------------------
256 256
 	 * Private WBXMLDecoder stuff
257 257
 	 */
258 258
 
259
-	/**
260
-	 * Returns the next token.
261
-	 *
262
-	 * @return token
263
-	 */
264
-	private function getToken() {
265
-		// See if there's something in the ungetBuffer
266
-		if ($this->ungetbuffer) {
267
-			$element = $this->ungetbuffer;
268
-			$this->ungetbuffer = false;
269
-
270
-			return $element;
271
-		}
272
-
273
-		$el = $this->_getToken();
274
-		if ($this->log && $el) {
275
-			$this->logToken($el);
276
-		}
277
-
278
-		return $el;
279
-	}
280
-
281
-	/**
282
-	 * Log the a token to SLog.
283
-	 *
284
-	 * @param string $el token
285
-	 *
286
-	 * @return
287
-	 */
288
-	private function logToken($el) {
289
-		$spaces = str_repeat(" ", count($this->logStack));
290
-
291
-		switch ($el[EN_TYPE]) {
292
-			case EN_TYPE_STARTTAG:
293
-				if ($el[EN_FLAGS] & EN_FLAGS_CONTENT) {
294
-					SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " <" . $el[EN_TAG] . ">");
295
-					array_push($this->logStack, $el[EN_TAG]);
296
-				}
297
-				else {
298
-					SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " <" . $el[EN_TAG] . "/>");
299
-				}
300
-				break;
301
-
302
-			case EN_TYPE_ENDTAG:
303
-				$tag = array_pop($this->logStack);
304
-				SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . "</" . $tag . ">");
305
-				break;
306
-
307
-			case EN_TYPE_CONTENT:
308
-				// as we concatenate the string here, the entire content is copied.
309
-				// when sending an email with an attachment this single log line (which is never logged in INFO)
310
-				// requires easily additional 20 MB of RAM. See https://jira.z-hub.io/browse/ZP-1159
311
-				$messagesize = strlen($el[EN_CONTENT]);
312
-				if ($messagesize > 10240 && !defined('WBXML_DEBUGGING')) {
313
-					$content = substr($el[EN_CONTENT], 0, 10240) . sprintf(" <log message with %d bytes truncated>", $messagesize);
314
-				}
315
-				else {
316
-					$content = $el[EN_CONTENT];
317
-				}
318
-				// Log but make sure it's not truncated again (will be slightly bigger than 10KB)
319
-				SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " " . $content, false);
320
-				break;
321
-		}
322
-	}
323
-
324
-	/**
325
-	 * Returns either a start tag, content or end tag.
326
-	 *
327
-	 * @return
328
-	 */
329
-	private function _getToken() {
330
-		// Get the data from the input stream
331
-		$element = [];
332
-
333
-		WBXMLDecoder::ResetInWhile("decoderGetToken");
334
-		while (WBXMLDecoder::InWhile("decoderGetToken")) {
335
-			$byte = fread($this->in, 1);
336
-			if ($byte === "" || $byte === false) {
337
-				break;
338
-			}
339
-			$byte = ord($byte);
340
-
341
-			switch ($byte) {
342
-				case self::WBXML_SWITCH_PAGE:
343
-					$this->tagcp = $this->getByte();
344
-					break;
345
-
346
-				case self::WBXML_END:
347
-					$element[EN_TYPE] = EN_TYPE_ENDTAG;
348
-
349
-					return $element;
350
-
351
-				case self::WBXML_STR_I:
352
-					$element[EN_TYPE] = EN_TYPE_CONTENT;
353
-					$element[EN_CONTENT] = $this->getTermStr();
354
-
355
-					return $element;
356
-
357
-				case self::WBXML_OPAQUE:
358
-					$length = $this->getMBUInt();
359
-					$element[EN_TYPE] = EN_TYPE_CONTENT;
360
-					$element[EN_CONTENT] = $this->getOpaque($length);
361
-
362
-					return $element;
363
-
364
-				case self::WBXML_ENTITY:
365
-				case self::WBXML_LITERAL:
366
-				case self::WBXML_EXT_I_0:
367
-				case self::WBXML_EXT_I_1:
368
-				case self::WBXML_EXT_I_2:
369
-				case self::WBXML_PI:
370
-				case self::WBXML_LITERAL_C:
371
-				case self::WBXML_EXT_T_0:
372
-				case self::WBXML_EXT_T_1:
373
-				case self::WBXML_EXT_T_2:
374
-				case self::WBXML_STR_T:
375
-				case self::WBXML_LITERAL_A:
376
-				case self::WBXML_EXT_0:
377
-				case self::WBXML_EXT_1:
378
-				case self::WBXML_EXT_2:
379
-				case self::WBXML_LITERAL_AC:
380
-					throw new WBXMLException("Invalid token :" . $byte);
381
-
382
-				default:
383
-					if ($byte & self::WBXML_WITH_ATTRIBUTES) {
384
-						throw new WBXMLException("Attributes are not allowed :" . $byte);
385
-					}
386
-					$element[EN_TYPE] = EN_TYPE_STARTTAG;
387
-					$element[EN_TAG] = $this->getMapping($this->tagcp, $byte & 0x3F);
388
-					$element[EN_FLAGS] = ($byte & self::WBXML_WITH_CONTENT ? EN_FLAGS_CONTENT : 0);
389
-
390
-					return $element;
391
-			}
392
-		}
393
-	}
394
-
395
-	/**
396
-	 * Reads from the stream until getting a string terminator.
397
-	 *
398
-	 * @return string
399
-	 */
400
-	private function getTermStr() {
401
-		if (defined('WBXML_DEBUGGING') && WBXML_DEBUGGING === true) {
402
-			$str = "";
403
-			while (1) {
404
-				$in = $this->getByte();
405
-				if ($in == 0) {
406
-					break;
407
-				}
408
-
409
-				$str .= chr($in);
410
-			}
411
-
412
-			return $str;
413
-		}
414
-
415
-		// there is no unlimited "length" for stream_get_line,
416
-		// so we use a huge value for "length" param (1Gb)
417
-		// (0 == PHP_SOCK_CHUNK_SIZE (8192))
418
-		// internally php read at most PHP_SOCK_CHUNK_SIZE at a time,
419
-		// so we can use a huge value for "length" without problem
420
-		return stream_get_line($this->in, 1073741824, "\0");
421
-	}
422
-
423
-	/**
424
-	 * Reads $len from the input stream.
425
-	 *
426
-	 * @param int $len
427
-	 *
428
-	 * @return string
429
-	 */
430
-	private function getOpaque($len) {
431
-		$d = stream_get_contents($this->in, $len);
432
-		if ($d === false) {
433
-			throw new HTTPReturnCodeException("WBXMLDecoder->getOpaque(): stream_get_contents === false", HTTP_CODE_500, null, LOGLEVEL_WARN);
434
-		}
435
-		$l = strlen($d);
436
-		if ($l !== $len) {
437
-			throw new HTTPReturnCodeException("WBXMLDecoder->getOpaque(): only {$l} byte read instead of {$len}", HTTP_CODE_500, null, LOGLEVEL_WARN);
438
-		}
439
-
440
-		return $d;
441
-	}
442
-
443
-	/**
444
-	 * Reads one byte from the input stream.
445
-	 *
446
-	 * @return int
447
-	 */
448
-	private function getByte() {
449
-		$ch = fread($this->in, 1);
450
-		if (strlen($ch) > 0) {
451
-			return ord($ch);
452
-		}
453
-	}
454
-
455
-	/**
456
-	 * Reads string length from the input stream.
457
-	 *
458
-	 * @return
459
-	 */
460
-	private function getMBUInt() {
461
-		$uint = 0;
462
-
463
-		while (1) {
464
-			$byte = $this->getByte();
465
-
466
-			$uint |= $byte & 0x7F;
467
-
468
-			if ($byte & 0x80) {
469
-				$uint = $uint << 7;
470
-			}
471
-			else {
472
-				break;
473
-			}
474
-		}
475
-
476
-		return $uint;
477
-	}
478
-
479
-	/**
480
-	 * Returns the mapping for a specified codepage and id.
481
-	 *
482
-	 * @param $cp   codepage
483
-	 * @param $id
484
-	 *
485
-	 * @return string
486
-	 */
487
-	private function getMapping($cp, $id) {
488
-		if (!isset($this->dtd["codes"][$cp]) || !isset($this->dtd["codes"][$cp][$id])) {
489
-			return false;
490
-		}
491
-
492
-		if (isset($this->dtd["namespaces"][$cp])) {
493
-			return $this->dtd["namespaces"][$cp] . ":" . $this->dtd["codes"][$cp][$id];
494
-		}
495
-
496
-		return $this->dtd["codes"][$cp][$id];
497
-	}
259
+    /**
260
+     * Returns the next token.
261
+     *
262
+     * @return token
263
+     */
264
+    private function getToken() {
265
+        // See if there's something in the ungetBuffer
266
+        if ($this->ungetbuffer) {
267
+            $element = $this->ungetbuffer;
268
+            $this->ungetbuffer = false;
269
+
270
+            return $element;
271
+        }
272
+
273
+        $el = $this->_getToken();
274
+        if ($this->log && $el) {
275
+            $this->logToken($el);
276
+        }
277
+
278
+        return $el;
279
+    }
280
+
281
+    /**
282
+     * Log the a token to SLog.
283
+     *
284
+     * @param string $el token
285
+     *
286
+     * @return
287
+     */
288
+    private function logToken($el) {
289
+        $spaces = str_repeat(" ", count($this->logStack));
290
+
291
+        switch ($el[EN_TYPE]) {
292
+            case EN_TYPE_STARTTAG:
293
+                if ($el[EN_FLAGS] & EN_FLAGS_CONTENT) {
294
+                    SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " <" . $el[EN_TAG] . ">");
295
+                    array_push($this->logStack, $el[EN_TAG]);
296
+                }
297
+                else {
298
+                    SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " <" . $el[EN_TAG] . "/>");
299
+                }
300
+                break;
301
+
302
+            case EN_TYPE_ENDTAG:
303
+                $tag = array_pop($this->logStack);
304
+                SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . "</" . $tag . ">");
305
+                break;
306
+
307
+            case EN_TYPE_CONTENT:
308
+                // as we concatenate the string here, the entire content is copied.
309
+                // when sending an email with an attachment this single log line (which is never logged in INFO)
310
+                // requires easily additional 20 MB of RAM. See https://jira.z-hub.io/browse/ZP-1159
311
+                $messagesize = strlen($el[EN_CONTENT]);
312
+                if ($messagesize > 10240 && !defined('WBXML_DEBUGGING')) {
313
+                    $content = substr($el[EN_CONTENT], 0, 10240) . sprintf(" <log message with %d bytes truncated>", $messagesize);
314
+                }
315
+                else {
316
+                    $content = $el[EN_CONTENT];
317
+                }
318
+                // Log but make sure it's not truncated again (will be slightly bigger than 10KB)
319
+                SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " " . $content, false);
320
+                break;
321
+        }
322
+    }
323
+
324
+    /**
325
+     * Returns either a start tag, content or end tag.
326
+     *
327
+     * @return
328
+     */
329
+    private function _getToken() {
330
+        // Get the data from the input stream
331
+        $element = [];
332
+
333
+        WBXMLDecoder::ResetInWhile("decoderGetToken");
334
+        while (WBXMLDecoder::InWhile("decoderGetToken")) {
335
+            $byte = fread($this->in, 1);
336
+            if ($byte === "" || $byte === false) {
337
+                break;
338
+            }
339
+            $byte = ord($byte);
340
+
341
+            switch ($byte) {
342
+                case self::WBXML_SWITCH_PAGE:
343
+                    $this->tagcp = $this->getByte();
344
+                    break;
345
+
346
+                case self::WBXML_END:
347
+                    $element[EN_TYPE] = EN_TYPE_ENDTAG;
348
+
349
+                    return $element;
350
+
351
+                case self::WBXML_STR_I:
352
+                    $element[EN_TYPE] = EN_TYPE_CONTENT;
353
+                    $element[EN_CONTENT] = $this->getTermStr();
354
+
355
+                    return $element;
356
+
357
+                case self::WBXML_OPAQUE:
358
+                    $length = $this->getMBUInt();
359
+                    $element[EN_TYPE] = EN_TYPE_CONTENT;
360
+                    $element[EN_CONTENT] = $this->getOpaque($length);
361
+
362
+                    return $element;
363
+
364
+                case self::WBXML_ENTITY:
365
+                case self::WBXML_LITERAL:
366
+                case self::WBXML_EXT_I_0:
367
+                case self::WBXML_EXT_I_1:
368
+                case self::WBXML_EXT_I_2:
369
+                case self::WBXML_PI:
370
+                case self::WBXML_LITERAL_C:
371
+                case self::WBXML_EXT_T_0:
372
+                case self::WBXML_EXT_T_1:
373
+                case self::WBXML_EXT_T_2:
374
+                case self::WBXML_STR_T:
375
+                case self::WBXML_LITERAL_A:
376
+                case self::WBXML_EXT_0:
377
+                case self::WBXML_EXT_1:
378
+                case self::WBXML_EXT_2:
379
+                case self::WBXML_LITERAL_AC:
380
+                    throw new WBXMLException("Invalid token :" . $byte);
381
+
382
+                default:
383
+                    if ($byte & self::WBXML_WITH_ATTRIBUTES) {
384
+                        throw new WBXMLException("Attributes are not allowed :" . $byte);
385
+                    }
386
+                    $element[EN_TYPE] = EN_TYPE_STARTTAG;
387
+                    $element[EN_TAG] = $this->getMapping($this->tagcp, $byte & 0x3F);
388
+                    $element[EN_FLAGS] = ($byte & self::WBXML_WITH_CONTENT ? EN_FLAGS_CONTENT : 0);
389
+
390
+                    return $element;
391
+            }
392
+        }
393
+    }
394
+
395
+    /**
396
+     * Reads from the stream until getting a string terminator.
397
+     *
398
+     * @return string
399
+     */
400
+    private function getTermStr() {
401
+        if (defined('WBXML_DEBUGGING') && WBXML_DEBUGGING === true) {
402
+            $str = "";
403
+            while (1) {
404
+                $in = $this->getByte();
405
+                if ($in == 0) {
406
+                    break;
407
+                }
408
+
409
+                $str .= chr($in);
410
+            }
411
+
412
+            return $str;
413
+        }
414
+
415
+        // there is no unlimited "length" for stream_get_line,
416
+        // so we use a huge value for "length" param (1Gb)
417
+        // (0 == PHP_SOCK_CHUNK_SIZE (8192))
418
+        // internally php read at most PHP_SOCK_CHUNK_SIZE at a time,
419
+        // so we can use a huge value for "length" without problem
420
+        return stream_get_line($this->in, 1073741824, "\0");
421
+    }
422
+
423
+    /**
424
+     * Reads $len from the input stream.
425
+     *
426
+     * @param int $len
427
+     *
428
+     * @return string
429
+     */
430
+    private function getOpaque($len) {
431
+        $d = stream_get_contents($this->in, $len);
432
+        if ($d === false) {
433
+            throw new HTTPReturnCodeException("WBXMLDecoder->getOpaque(): stream_get_contents === false", HTTP_CODE_500, null, LOGLEVEL_WARN);
434
+        }
435
+        $l = strlen($d);
436
+        if ($l !== $len) {
437
+            throw new HTTPReturnCodeException("WBXMLDecoder->getOpaque(): only {$l} byte read instead of {$len}", HTTP_CODE_500, null, LOGLEVEL_WARN);
438
+        }
439
+
440
+        return $d;
441
+    }
442
+
443
+    /**
444
+     * Reads one byte from the input stream.
445
+     *
446
+     * @return int
447
+     */
448
+    private function getByte() {
449
+        $ch = fread($this->in, 1);
450
+        if (strlen($ch) > 0) {
451
+            return ord($ch);
452
+        }
453
+    }
454
+
455
+    /**
456
+     * Reads string length from the input stream.
457
+     *
458
+     * @return
459
+     */
460
+    private function getMBUInt() {
461
+        $uint = 0;
462
+
463
+        while (1) {
464
+            $byte = $this->getByte();
465
+
466
+            $uint |= $byte & 0x7F;
467
+
468
+            if ($byte & 0x80) {
469
+                $uint = $uint << 7;
470
+            }
471
+            else {
472
+                break;
473
+            }
474
+        }
475
+
476
+        return $uint;
477
+    }
478
+
479
+    /**
480
+     * Returns the mapping for a specified codepage and id.
481
+     *
482
+     * @param $cp   codepage
483
+     * @param $id
484
+     *
485
+     * @return string
486
+     */
487
+    private function getMapping($cp, $id) {
488
+        if (!isset($this->dtd["codes"][$cp]) || !isset($this->dtd["codes"][$cp][$id])) {
489
+            return false;
490
+        }
491
+
492
+        if (isset($this->dtd["namespaces"][$cp])) {
493
+            return $this->dtd["namespaces"][$cp] . ":" . $this->dtd["codes"][$cp][$id];
494
+        }
495
+
496
+        return $this->dtd["codes"][$cp][$id];
497
+    }
498 498
 }
Please login to merge, or discard this patch.
Spacing   +14 added lines, -15 removed lines patch added patch discarded remove patch
@@ -34,8 +34,7 @@  discard block
 block discarded – undo
34 34
 		if (!isset(self::$loopCounter[$name])) {
35 35
 			self::$loopCounter[$name] = 0;
36 36
 		}
37
-		else {
38
-			++self::$loopCounter[$name];
37
+		else {++self::$loopCounter[$name];
39 38
 		}
40 39
 
41 40
 		if (self::$loopCounter[$name] > self::MAXLOOP) {
@@ -81,17 +80,17 @@  discard block
 block discarded – undo
81 80
 
82 81
 		$publicid = $this->getMBUInt();
83 82
 		if ($publicid !== 1) {
84
-			throw new WBXMLException("Wrong publicid : " . $publicid);
83
+			throw new WBXMLException("Wrong publicid : ".$publicid);
85 84
 		}
86 85
 
87 86
 		$charsetid = $this->getMBUInt();
88 87
 		if ($charsetid !== 106) {
89
-			throw new WBXMLException("Wrong charset : " . $charsetid);
88
+			throw new WBXMLException("Wrong charset : ".$charsetid);
90 89
 		}
91 90
 
92 91
 		$stringtablesize = $this->getMBUInt();
93 92
 		if ($stringtablesize !== 0) {
94
-			throw new WBXMLException("Wrong string table size : " . $stringtablesize);
93
+			throw new WBXMLException("Wrong string table size : ".$stringtablesize);
95 94
 		}
96 95
 	}
97 96
 
@@ -184,7 +183,7 @@  discard block
 block discarded – undo
184 183
 		SLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementEndTag(): unmatched WBXML tag: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG])) ? $element[EN_TAG] : ""), ((isset($element[EN_TYPE])) ? $element[EN_TYPE] : ""), ((isset($element[EN_FLAGS])) ? $element[EN_FLAGS] : "")));
185 184
 
186 185
 		$bt = debug_backtrace();
187
-		SLog::Write(LOGLEVEL_ERROR, sprintf("WBXMLDecoder->getElementEndTag(): could not read end tag in '%s'. Please enable the LOGLEVEL_WBXML and send the log to the grommunio-sync dev team.", $bt[0]["file"] . ":" . $bt[0]["line"]));
186
+		SLog::Write(LOGLEVEL_ERROR, sprintf("WBXMLDecoder->getElementEndTag(): could not read end tag in '%s'. Please enable the LOGLEVEL_WBXML and send the log to the grommunio-sync dev team.", $bt[0]["file"].":".$bt[0]["line"]));
188 187
 
189 188
 		// log the remaining wbxml content
190 189
 		$this->ungetElement($element);
@@ -232,7 +231,7 @@  discard block
 block discarded – undo
232 231
 	 * @return string
233 232
 	 */
234 233
 	public function GetPlainInputStream() {
235
-		return $this->inputBuffer . stream_get_contents($this->in);
234
+		return $this->inputBuffer.stream_get_contents($this->in);
236 235
 	}
237 236
 
238 237
 	/**
@@ -291,17 +290,17 @@  discard block
 block discarded – undo
291 290
 		switch ($el[EN_TYPE]) {
292 291
 			case EN_TYPE_STARTTAG:
293 292
 				if ($el[EN_FLAGS] & EN_FLAGS_CONTENT) {
294
-					SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " <" . $el[EN_TAG] . ">");
293
+					SLog::Write(LOGLEVEL_WBXML, "I ".$spaces." <".$el[EN_TAG].">");
295 294
 					array_push($this->logStack, $el[EN_TAG]);
296 295
 				}
297 296
 				else {
298
-					SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " <" . $el[EN_TAG] . "/>");
297
+					SLog::Write(LOGLEVEL_WBXML, "I ".$spaces." <".$el[EN_TAG]."/>");
299 298
 				}
300 299
 				break;
301 300
 
302 301
 			case EN_TYPE_ENDTAG:
303 302
 				$tag = array_pop($this->logStack);
304
-				SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . "</" . $tag . ">");
303
+				SLog::Write(LOGLEVEL_WBXML, "I ".$spaces."</".$tag.">");
305 304
 				break;
306 305
 
307 306
 			case EN_TYPE_CONTENT:
@@ -310,13 +309,13 @@  discard block
 block discarded – undo
310 309
 				// requires easily additional 20 MB of RAM. See https://jira.z-hub.io/browse/ZP-1159
311 310
 				$messagesize = strlen($el[EN_CONTENT]);
312 311
 				if ($messagesize > 10240 && !defined('WBXML_DEBUGGING')) {
313
-					$content = substr($el[EN_CONTENT], 0, 10240) . sprintf(" <log message with %d bytes truncated>", $messagesize);
312
+					$content = substr($el[EN_CONTENT], 0, 10240).sprintf(" <log message with %d bytes truncated>", $messagesize);
314 313
 				}
315 314
 				else {
316 315
 					$content = $el[EN_CONTENT];
317 316
 				}
318 317
 				// Log but make sure it's not truncated again (will be slightly bigger than 10KB)
319
-				SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " " . $content, false);
318
+				SLog::Write(LOGLEVEL_WBXML, "I ".$spaces." ".$content, false);
320 319
 				break;
321 320
 		}
322 321
 	}
@@ -377,11 +376,11 @@  discard block
 block discarded – undo
377 376
 				case self::WBXML_EXT_1:
378 377
 				case self::WBXML_EXT_2:
379 378
 				case self::WBXML_LITERAL_AC:
380
-					throw new WBXMLException("Invalid token :" . $byte);
379
+					throw new WBXMLException("Invalid token :".$byte);
381 380
 
382 381
 				default:
383 382
 					if ($byte & self::WBXML_WITH_ATTRIBUTES) {
384
-						throw new WBXMLException("Attributes are not allowed :" . $byte);
383
+						throw new WBXMLException("Attributes are not allowed :".$byte);
385 384
 					}
386 385
 					$element[EN_TYPE] = EN_TYPE_STARTTAG;
387 386
 					$element[EN_TAG] = $this->getMapping($this->tagcp, $byte & 0x3F);
@@ -490,7 +489,7 @@  discard block
 block discarded – undo
490 489
 		}
491 490
 
492 491
 		if (isset($this->dtd["namespaces"][$cp])) {
493
-			return $this->dtd["namespaces"][$cp] . ":" . $this->dtd["codes"][$cp][$id];
492
+			return $this->dtd["namespaces"][$cp].":".$this->dtd["codes"][$cp][$id];
494 493
 		}
495 494
 
496 495
 		return $this->dtd["codes"][$cp][$id];
Please login to merge, or discard this patch.
Braces   +5 added lines, -10 removed lines patch added patch discarded remove patch
@@ -33,8 +33,7 @@  discard block
 block discarded – undo
33 33
 	public static function InWhile($name) {
34 34
 		if (!isset(self::$loopCounter[$name])) {
35 35
 			self::$loopCounter[$name] = 0;
36
-		}
37
-		else {
36
+		} else {
38 37
 			++self::$loopCounter[$name];
39 38
 		}
40 39
 
@@ -119,8 +118,7 @@  discard block
 block discarded – undo
119 118
 					}
120 119
 					if ($next[EN_TYPE] == EN_CONTENT) {
121 120
 						$element[EN_CONTENT] .= $next[EN_CONTENT];
122
-					}
123
-					else {
121
+					} else {
124 122
 						$this->ungetElement($next);
125 123
 
126 124
 						break;
@@ -293,8 +291,7 @@  discard block
 block discarded – undo
293 291
 				if ($el[EN_FLAGS] & EN_FLAGS_CONTENT) {
294 292
 					SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " <" . $el[EN_TAG] . ">");
295 293
 					array_push($this->logStack, $el[EN_TAG]);
296
-				}
297
-				else {
294
+				} else {
298 295
 					SLog::Write(LOGLEVEL_WBXML, "I " . $spaces . " <" . $el[EN_TAG] . "/>");
299 296
 				}
300 297
 				break;
@@ -311,8 +308,7 @@  discard block
 block discarded – undo
311 308
 				$messagesize = strlen($el[EN_CONTENT]);
312 309
 				if ($messagesize > 10240 && !defined('WBXML_DEBUGGING')) {
313 310
 					$content = substr($el[EN_CONTENT], 0, 10240) . sprintf(" <log message with %d bytes truncated>", $messagesize);
314
-				}
315
-				else {
311
+				} else {
316 312
 					$content = $el[EN_CONTENT];
317 313
 				}
318 314
 				// Log but make sure it's not truncated again (will be slightly bigger than 10KB)
@@ -467,8 +463,7 @@  discard block
 block discarded – undo
467 463
 
468 464
 			if ($byte & 0x80) {
469 465
 				$uint = $uint << 7;
470
-			}
471
-			else {
466
+			} else {
472 467
 				break;
473 468
 			}
474 469
 		}
Please login to merge, or discard this patch.
lib/wbxml/replacenullcharfilter.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -8,25 +8,25 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 class ReplaceNullcharFilter extends php_user_filter {
11
-	/**
12
-	 * This method is called whenever data is read from or written to the attached stream.
13
-	 *
14
-	 * @see php_user_filter::filter()
15
-	 *
16
-	 * @param resource $in
17
-	 * @param resource $out
18
-	 * @param int      $consumed
19
-	 * @param bool     $closing
20
-	 *
21
-	 * @return int
22
-	 */
23
-	public function filter($in, $out, &$consumed, $closing) {
24
-		while ($bucket = stream_bucket_make_writeable($in)) {
25
-			$bucket->data = str_replace("\0", "", $bucket->data);
26
-			$consumed += $bucket->datalen;
27
-			stream_bucket_append($out, $bucket);
28
-		}
11
+    /**
12
+     * This method is called whenever data is read from or written to the attached stream.
13
+     *
14
+     * @see php_user_filter::filter()
15
+     *
16
+     * @param resource $in
17
+     * @param resource $out
18
+     * @param int      $consumed
19
+     * @param bool     $closing
20
+     *
21
+     * @return int
22
+     */
23
+    public function filter($in, $out, &$consumed, $closing) {
24
+        while ($bucket = stream_bucket_make_writeable($in)) {
25
+            $bucket->data = str_replace("\0", "", $bucket->data);
26
+            $consumed += $bucket->datalen;
27
+            stream_bucket_append($out, $bucket);
28
+        }
29 29
 
30
-		return PSFS_PASS_ON;
31
-	}
30
+        return PSFS_PASS_ON;
31
+    }
32 32
 }
Please login to merge, or discard this patch.
lib/wbxml/wbxmldefs.php 2 patches
Indentation   +709 added lines, -709 removed lines patch added patch discarded remove patch
@@ -21,714 +21,714 @@
 block discarded – undo
21 21
 define('EN_FLAGS_ATTRIBUTES', 2);
22 22
 
23 23
 class WBXMLDefs {
24
-	public const WBXML_SWITCH_PAGE = 0x00;
25
-	public const WBXML_END = 0x01;
26
-	public const WBXML_ENTITY = 0x02; // not used in ActiveSync
27
-	public const WBXML_STR_I = 0x03;
28
-	public const WBXML_LITERAL = 0x04; // not used in ActiveSync
29
-	public const WBXML_EXT_I_0 = 0x40; // not used in ActiveSync
30
-	public const WBXML_EXT_I_1 = 0x41; // not used in ActiveSync
31
-	public const WBXML_EXT_I_2 = 0x42; // not used in ActiveSync
32
-	public const WBXML_PI = 0x43; // not used in ActiveSync
33
-	public const WBXML_LITERAL_C = 0x44; // not used in ActiveSync
34
-	public const WBXML_EXT_T_0 = 0x80; // not used in ActiveSync
35
-	public const WBXML_EXT_T_1 = 0x81; // not used in ActiveSync
36
-	public const WBXML_EXT_T_2 = 0x82; // not used in ActiveSync
37
-	public const WBXML_STR_T = 0x83; // not used in ActiveSync
38
-	public const WBXML_LITERAL_A = 0x84; // not used in ActiveSync
39
-	public const WBXML_EXT_0 = 0xC0; // not used in ActiveSync
40
-	public const WBXML_EXT_1 = 0xC1; // not used in ActiveSync
41
-	public const WBXML_EXT_2 = 0xC2; // not used in ActiveSync
42
-	public const WBXML_OPAQUE = 0xC3;
43
-	public const WBXML_LITERAL_AC = 0xC4; // not used in ActiveSync
44
-	public const WBXML_WITH_ATTRIBUTES = 0x80; // not used in ActiveSync
45
-	public const WBXML_WITH_CONTENT = 0x40;
24
+    public const WBXML_SWITCH_PAGE = 0x00;
25
+    public const WBXML_END = 0x01;
26
+    public const WBXML_ENTITY = 0x02; // not used in ActiveSync
27
+    public const WBXML_STR_I = 0x03;
28
+    public const WBXML_LITERAL = 0x04; // not used in ActiveSync
29
+    public const WBXML_EXT_I_0 = 0x40; // not used in ActiveSync
30
+    public const WBXML_EXT_I_1 = 0x41; // not used in ActiveSync
31
+    public const WBXML_EXT_I_2 = 0x42; // not used in ActiveSync
32
+    public const WBXML_PI = 0x43; // not used in ActiveSync
33
+    public const WBXML_LITERAL_C = 0x44; // not used in ActiveSync
34
+    public const WBXML_EXT_T_0 = 0x80; // not used in ActiveSync
35
+    public const WBXML_EXT_T_1 = 0x81; // not used in ActiveSync
36
+    public const WBXML_EXT_T_2 = 0x82; // not used in ActiveSync
37
+    public const WBXML_STR_T = 0x83; // not used in ActiveSync
38
+    public const WBXML_LITERAL_A = 0x84; // not used in ActiveSync
39
+    public const WBXML_EXT_0 = 0xC0; // not used in ActiveSync
40
+    public const WBXML_EXT_1 = 0xC1; // not used in ActiveSync
41
+    public const WBXML_EXT_2 = 0xC2; // not used in ActiveSync
42
+    public const WBXML_OPAQUE = 0xC3;
43
+    public const WBXML_LITERAL_AC = 0xC4; // not used in ActiveSync
44
+    public const WBXML_WITH_ATTRIBUTES = 0x80; // not used in ActiveSync
45
+    public const WBXML_WITH_CONTENT = 0x40;
46 46
 
47
-	/**
48
-	 * The WBXML DTDs.
49
-	 */
50
-	protected $dtd = [
51
-		"codes" => [
52
-			0 => [
53
-				0x05 => "Synchronize",
54
-				0x06 => "Replies", // Responses
55
-				0x07 => "Add",
56
-				0x08 => "Modify", // Change
57
-				0x09 => "Remove", // Delete
58
-				0x0A => "Fetch",
59
-				0x0B => "SyncKey",
60
-				0x0C => "ClientEntryId", // ClientId
61
-				0x0D => "ServerEntryId", // ServerId
62
-				0x0E => "Status",
63
-				0x0F => "Folder", // collection
64
-				0x10 => "FolderType", // class
65
-				0x11 => "Version", // deprecated
66
-				0x12 => "FolderId", // CollectionId
67
-				0x13 => "GetChanges",
68
-				0x14 => "MoreAvailable",
69
-				0x15 => "WindowSize", // WindowSize - MaxItems before version 2
70
-				0x16 => "Perform", // Commands
71
-				0x17 => "Options",
72
-				0x18 => "FilterType",
73
-				0x19 => "Truncation", // 2.0 and 2.5
74
-				0x1A => "RtfTruncation", // 2.0 and 2.5
75
-				0x1B => "Conflict",
76
-				0x1C => "Folders", // Collections
77
-				0x1D => "Data",
78
-				0x1E => "DeletesAsMoves",
79
-				0x1F => "NotifyGUID", // 2.0 and 2.5
80
-				0x20 => "Supported",
81
-				0x21 => "SoftDelete",
82
-				0x22 => "MIMESupport",
83
-				0x23 => "MIMETruncation",
84
-				0x24 => "Wait", // Since 12.1
85
-				0x25 => "Limit", // Since 12.1
86
-				0x26 => "Partial", // Since 12.1
87
-				0x27 => "ConversationMode", // Since 14.0
88
-				0x28 => "MaxItems", // Since 14.0
89
-				0x29 => "HeartbeatInterval", // Since 14.0 Either this tag or the Wait tag can be present, but not both.
90
-			],
91
-			1 => [
92
-				0x05 => "Anniversary",
93
-				0x06 => "AssistantName",
94
-				0x07 => "AssistnamePhoneNumber", // AssistantTelephoneNumber
95
-				0x08 => "Birthday",
96
-				0x09 => "Body", // 2.5, AirSyncBase Body is used since version 12.0
97
-				0x0A => "BodySize", // 2.0 and 2.5, AirSyncBase is used since version 12.0
98
-				0x0B => "BodyTruncated", // 2.0 and 2.5, AirSyncBase is used since version 12.0
99
-				0x0C => "Business2PhoneNumber",
100
-				0x0D => "BusinessCity",
101
-				0x0E => "BusinessCountry",
102
-				0x0F => "BusinessPostalCode",
103
-				0x10 => "BusinessState",
104
-				0x11 => "BusinessStreet",
105
-				0x12 => "BusinessFaxNumber",
106
-				0x13 => "BusinessPhoneNumber",
107
-				0x14 => "CarPhoneNumber",
108
-				0x15 => "Categories",
109
-				0x16 => "Category",
110
-				0x17 => "Children",
111
-				0x18 => "Child",
112
-				0x19 => "CompanyName",
113
-				0x1A => "Department",
114
-				0x1B => "Email1Address",
115
-				0x1C => "Email2Address",
116
-				0x1D => "Email3Address",
117
-				0x1E => "FileAs",
118
-				0x1F => "FirstName",
119
-				0x20 => "Home2PhoneNumber",
120
-				0x21 => "HomeCity",
121
-				0x22 => "HomeCountry",
122
-				0x23 => "HomePostalCode",
123
-				0x24 => "HomeState",
124
-				0x25 => "HomeStreet",
125
-				0x26 => "HomeFaxNumber",
126
-				0x27 => "HomePhoneNumber",
127
-				0x28 => "JobTitle",
128
-				0x29 => "LastName",
129
-				0x2A => "MiddleName",
130
-				0x2B => "MobilePhoneNumber",
131
-				0x2C => "OfficeLocation",
132
-				0x2D => "OtherCity",
133
-				0x2E => "OtherCountry",
134
-				0x2F => "OtherPostalCode",
135
-				0x30 => "OtherState",
136
-				0x31 => "OtherStreet",
137
-				0x32 => "PagerNumber",
138
-				0x33 => "RadioPhoneNumber",
139
-				0x34 => "Spouse",
140
-				0x35 => "Suffix",
141
-				0x36 => "Title",
142
-				0x37 => "WebPage",
143
-				0x38 => "YomiCompanyName",
144
-				0x39 => "YomiFirstName",
145
-				0x3A => "YomiLastName",
146
-				0x3B => "Rtf", // CompressedRTF - 2.5, deprecated
147
-				0x3C => "Picture",
148
-				0x3D => "Alias", // Since 14.0
149
-				0x3E => "WeightedRank", // Since 14.0
150
-			],
151
-			2 => [
152
-				0x05 => "Attachment", // AirSyncBase Attachments is used since 12.0
153
-				0x06 => "Attachments", // AirSyncBase Attachments is used since 12.0
154
-				0x07 => "AttName", // AirSyncBase Attachments is used since 12.0
155
-				0x08 => "AttSize", // AirSyncBase Attachments is used since 12.0
156
-				0x09 => "AttOid", // AirSyncBase Attachments is used since 12.0
157
-				0x0A => "AttMethod", // AirSyncBase Attachments is used since 12.0
158
-				0x0B => "AttRemoved", // AirSyncBase Attachments is used since 12.0
159
-				0x0C => "Body", // AirSyncBase Body is used since 12.0
160
-				0x0D => "BodySize", // AirSyncBase Body is used since 12.0
161
-				0x0E => "BodyTruncated", // AirSyncBase Body is used since 12.0
162
-				0x0F => "DateReceived",
163
-				0x10 => "DisplayName", // AirSyncBase Attachments is used since 12.0
164
-				0x11 => "DisplayTo",
165
-				0x12 => "Importance",
166
-				0x13 => "MessageClass",
167
-				0x14 => "Subject",
168
-				0x15 => "Read",
169
-				0x16 => "To",
170
-				0x17 => "Cc",
171
-				0x18 => "From",
172
-				0x19 => "Reply-To",
173
-				0x1A => "AllDayEvent",
174
-				0x1B => "Categories", // Since 14.0
175
-				0x1C => "Category", // Since 14.0
176
-				0x1D => "DtStamp",
177
-				0x1E => "EndTime",
178
-				0x1F => "InstanceType",
179
-				0x20 => "BusyStatus",
180
-				0x21 => "Location", // 2.5, 12.0, 12.1, 14.0 and 14.1. Since 16.0 AirSyncBase Location is used.
181
-				0x22 => "MeetingRequest",
182
-				0x23 => "Organizer",
183
-				0x24 => "RecurrenceId",
184
-				0x25 => "Reminder",
185
-				0x26 => "ResponseRequested",
186
-				0x27 => "Recurrences",
187
-				0x28 => "Recurrence",
188
-				0x29 => "Type",
189
-				0x2A => "Until",
190
-				0x2B => "Occurrences",
191
-				0x2C => "Interval",
192
-				0x2D => "DayOfWeek",
193
-				0x2E => "DayOfMonth",
194
-				0x2F => "WeekOfMonth",
195
-				0x30 => "MonthOfYear",
196
-				0x31 => "StartTime",
197
-				0x32 => "Sensitivity",
198
-				0x33 => "TimeZone",
199
-				0x34 => "GlobalObjId", // 2.5, 12.0, 12.1, 14.0 and 14.1. UID of Calendar (Code page 4) is used since 16.0
200
-				0x35 => "ThreadTopic",
201
-				0x36 => "MIMEData", // 2.5
202
-				0x37 => "MIMETruncated", // 2.5
203
-				0x38 => "MIMESize", // 2.5
204
-				0x39 => "InternetCPID",
205
-				0x3A => "Flag", // Since 12.0
206
-				0x3B => "FlagStatus", // Since 12.0
207
-				0x3C => "ContentClass", // Since 12.0
208
-				0x3D => "FlagType", // Since 12.0
209
-				0x3E => "CompleteTime", // Since 12.0
210
-				0x3F => "DisallowNewTimeProposal", // Since 14.0
211
-			],
212
-			3 => [ // Code page 3 is no longer in use, however, tokens 05 through 17 have been defined. 20100501
213
-				0x05 => "Notify",
214
-				0x06 => "Notification",
215
-				0x07 => "Version",
216
-				0x08 => "Lifetime",
217
-				0x09 => "DeviceInfo",
218
-				0x0A => "Enable",
219
-				0x0B => "Folder",
220
-				0x0C => "ServerEntryId",
221
-				0x0D => "DeviceAddress",
222
-				0x0E => "ValidCarrierProfiles",
223
-				0x0F => "CarrierProfile",
224
-				0x10 => "Status",
225
-				0x11 => "Replies",
226
-				//                        0x05 => "Version='1.1'",
227
-				0x12 => "Devices",
228
-				0x13 => "Device",
229
-				0x14 => "Id",
230
-				0x15 => "Expiry",
231
-				0x16 => "NotifyGUID",
232
-			],
233
-			4 => [
234
-				0x05 => "Timezone",
235
-				0x06 => "AllDayEvent",
236
-				0x07 => "Attendees",
237
-				0x08 => "Attendee",
238
-				0x09 => "Email",
239
-				0x0A => "Name",
240
-				0x0B => "Body", // AirSyncBase Body is used since 12.0
241
-				0x0C => "BodyTruncated", // AirSyncBase Body is used since 12.0
242
-				0x0D => "BusyStatus",
243
-				0x0E => "Categories",
244
-				0x0F => "Category",
245
-				0x10 => "Rtf", // 2.5 - deprecated
246
-				0x11 => "DtStamp",
247
-				0x12 => "EndTime",
248
-				0x13 => "Exception",
249
-				0x14 => "Exceptions",
250
-				0x15 => "Deleted",
251
-				0x16 => "ExceptionStartTime", // 2.5, 12.0, 12.1, 14.0 and 14.1.
252
-				0x17 => "Location", // 2.5, 12.0, 12.1, 14.0 and 14.1. Since 16.0 AirSyncBase Location is used.
253
-				0x18 => "MeetingStatus",
254
-				0x19 => "OrganizerEmail",
255
-				0x1A => "OrganizerName",
256
-				0x1B => "Recurrence",
257
-				0x1C => "Type",
258
-				0x1D => "Until",
259
-				0x1E => "Occurrences",
260
-				0x1F => "Interval",
261
-				0x20 => "DayOfWeek",
262
-				0x21 => "DayOfMonth",
263
-				0x22 => "WeekOfMonth",
264
-				0x23 => "MonthOfYear",
265
-				0x24 => "Reminder",
266
-				0x25 => "Sensitivity",
267
-				0x26 => "Subject",
268
-				0x27 => "StartTime",
269
-				0x28 => "UID",
270
-				0x29 => "Attendee_Status", // Since 12.0
271
-				0x2A => "Attendee_Type", // Since 12.0
272
-				0x2B => "Attachment", // Not defined / deprecated
273
-				0x2C => "Attachments", // Not defined / deprecated
274
-				0x2D => "AttName", // Not defined / deprecated
275
-				0x2E => "AttSize", // Not defined / deprecated
276
-				0x2F => "AttOid", // Not defined / deprecated
277
-				0x30 => "AttMethod", // Not defined / deprecated
278
-				0x31 => "AttRemoved", // Not defined / deprecated
279
-				0x32 => "DisplayName", // Not defined / deprecated
280
-				0x33 => "DisallowNewTimeProposal", // Since 14.0
281
-				0x34 => "ResponseRequested", // Since 14.0
282
-				0x35 => "AppointmentReplyTime", // Since 14.0
283
-				0x36 => "ResponseType", // Since 14.0
284
-				0x37 => "CalendarType", // Since 14.0
285
-				0x38 => "IsLeapMonth", // Since 14.0
286
-				0x39 => "FirstDayOfWeek", // Since 14.1
287
-				0x3A => "OnlineMeetingConfLink", // Since 14.1
288
-				0x3B => "OnlineMeetingExternalLink", // Since 14.1
289
-			],
290
-			5 => [
291
-				0x05 => "Moves",
292
-				0x06 => "Move",
293
-				0x07 => "SrcMsgId",
294
-				0x08 => "SrcFldId",
295
-				0x09 => "DstFldId",
296
-				0x0A => "Response",
297
-				0x0B => "Status",
298
-				0x0C => "DstMsgId",
299
-			],
300
-			6 => [
301
-				0x05 => "GetItemEstimate",
302
-				0x06 => "Version", // deprecated
303
-				0x07 => "Folders", // Collections
304
-				0x08 => "Folder", // Collection
305
-				0x09 => "FolderType", // AirSync Class(SYNC_FOLDERTYPE) is used since AS 14.0
306
-				0x0A => "FolderId", // CollectionId
307
-				0x0B => "DateTime", // deprecated
308
-				0x0C => "Estimate",
309
-				0x0D => "Response",
310
-				0x0E => "Status",
311
-			],
312
-			7 => [
313
-				0x05 => "Folders", // 2.5, 12.0 and 12.1
314
-				0x06 => "Folder", // 2.5, 12.0 and 12.1
315
-				0x07 => "DisplayName",
316
-				0x08 => "ServerEntryId", // ServerId
317
-				0x09 => "ParentId",
318
-				0x0A => "Type",
319
-				0x0B => "Response", // deprecated
320
-				0x0C => "Status",
321
-				0x0D => "ContentClass", // deprecated
322
-				0x0E => "Changes",
323
-				0x0F => "Add",
324
-				0x10 => "Remove",
325
-				0x11 => "Update",
326
-				0x12 => "SyncKey",
327
-				0x13 => "FolderCreate",
328
-				0x14 => "FolderDelete",
329
-				0x15 => "FolderUpdate",
330
-				0x16 => "FolderSync",
331
-				0x17 => "Count",
332
-				0x18 => "Version", // 2.0 - not defined in 20100501
333
-			],
334
-			8 => [
335
-				0x05 => "CalendarId",
336
-				0x06 => "FolderId", // CollectionId
337
-				0x07 => "MeetingResponse",
338
-				0x08 => "RequestId",
339
-				0x09 => "Request",
340
-				0x0A => "Result",
341
-				0x0B => "Status",
342
-				0x0C => "UserResponse",
343
-				0x0D => "Version", // 2.0 - not defined in 20100501
344
-				0x0E => "InstanceId", // Since AS 14.1
345
-			],
346
-			9 => [
347
-				0x05 => "Body", // AirSyncBase Body is used since 12.0
348
-				0x06 => "BodySize", // AirSyncBase Body is used since 12.0
349
-				0x07 => "BodyTruncated", // AirSyncBase Body is used since 12.0
350
-				0x08 => "Categories",
351
-				0x09 => "Category",
352
-				0x0A => "Complete",
353
-				0x0B => "DateCompleted",
354
-				0x0C => "DueDate",
355
-				0x0D => "UtcDueDate",
356
-				0x0E => "Importance",
357
-				0x0F => "Recurrence",
358
-				0x10 => "Type",
359
-				0x11 => "Start",
360
-				0x12 => "Until",
361
-				0x13 => "Occurrences",
362
-				0x14 => "Interval",
363
-				0x15 => "DayOfMonth",
364
-				0x16 => "DayOfWeek",
365
-				0x17 => "WeekOfMonth",
366
-				0x18 => "MonthOfYear",
367
-				0x19 => "Regenerate",
368
-				0x1A => "DeadOccur",
369
-				0x1B => "ReminderSet",
370
-				0x1C => "ReminderTime",
371
-				0x1D => "Sensitivity",
372
-				0x1E => "StartDate",
373
-				0x1F => "UtcStartDate",
374
-				0x20 => "Subject",
375
-				0x21 => "Rtf", // deprecated
376
-				0x22 => "OrdinalDate", // Since 12.0
377
-				0x23 => "SubOrdinalDate", // Since 12.0
378
-				0x24 => "CalendarType", // Since 14.0
379
-				0x25 => "IsLeapMonth", // Since 14.0
380
-				0x26 => "FirstDayOfWeek", // Since 14.1
381
-			],
382
-			0xA => [
383
-				0x05 => "ResolveRecipients",
384
-				0x06 => "Response",
385
-				0x07 => "Status",
386
-				0x08 => "Type",
387
-				0x09 => "Recipient",
388
-				0x0A => "DisplayName",
389
-				0x0B => "EmailAddress",
390
-				0x0C => "Certificates",
391
-				0x0D => "Certificate",
392
-				0x0E => "MiniCertificate",
393
-				0x0F => "Options",
394
-				0x10 => "To",
395
-				0x11 => "CertificateRetrieval",
396
-				0x12 => "RecipientCount",
397
-				0x13 => "MaxCertificates",
398
-				0x14 => "MaxAmbiguousRecipients",
399
-				0x15 => "CertificateCount",
400
-				0x16 => "Availability", // Since 14.0
401
-				0x17 => "StartTime", // Since 14.0
402
-				0x18 => "EndTime", // Since 14.0
403
-				0x19 => "MergedFreeBusy", // Since 14.0
404
-				0x1A => "Picture", // Since 14.1
405
-				0x1B => "MaxSize", // Since 14.1
406
-				0x1C => "Data", // Since 14.1
407
-				0x1D => "MaxPictures", // Since 14.1
408
-			],
409
-			0xB => [
410
-				0x05 => "ValidateCert",
411
-				0x06 => "Certificates",
412
-				0x07 => "Certificate",
413
-				0x08 => "CertificateChain",
414
-				0x09 => "CheckCRL",
415
-				0x0A => "Status",
416
-			],
417
-			0xC => [
418
-				0x05 => "CustomerId",
419
-				0x06 => "GovernmentId",
420
-				0x07 => "IMAddress",
421
-				0x08 => "IMAddress2",
422
-				0x09 => "IMAddress3",
423
-				0x0A => "ManagerName",
424
-				0x0B => "CompanyMainPhone",
425
-				0x0C => "AccountName",
426
-				0x0D => "NickName",
427
-				0x0E => "MMS",
428
-			],
429
-			0xD => [
430
-				0x05 => "Ping",
431
-				0x06 => "AutdState", // (Not used by protocol)
432
-				0x07 => "Status",
433
-				0x08 => "LifeTime", // HeartbeatInterval
434
-				0x09 => "Folders",
435
-				0x0A => "Folder",
436
-				0x0B => "ServerEntryId", // Id
437
-				0x0C => "FolderType", // Class
438
-				0x0D => "MaxFolders",
439
-				0x0E => "Version", // not defined / deprecated
440
-			],
441
-			0xE => [
442
-				0x05 => "Provision",
443
-				0x06 => "Policies",
444
-				0x07 => "Policy",
445
-				0x08 => "PolicyType",
446
-				0x09 => "PolicyKey",
447
-				0x0A => "Data",
448
-				0x0B => "Status",
449
-				0x0C => "RemoteWipe",
450
-				0x0D => "EASProvisionDoc", // Since 12.0
451
-				0x0E => "DevicePasswordEnabled", // Since 12.0
452
-				0x0F => "AlphanumericDevicePasswordRequired", // Since 12.0
453
-				0x10 => "DeviceEncryptionEnabled", // Since 12.1
454
-				// 0x10 => "RequireStorageCardEncryption", //Since 12.1
455
-				0x11 => "PasswordRecoveryEnabled", // Since 12.0
456
-				0x12 => "DocumentBrowseEnabled", // not defined / deprecated
457
-				0x13 => "AttachmentsEnabled", // Since 12.0
458
-				0x14 => "MinDevicePasswordLength", // Since 12.0
459
-				0x15 => "MaxInactivityTimeDeviceLock", // Since 12.0
460
-				0x16 => "MaxDevicePasswordFailedAttempts", // Since 12.0
461
-				0x17 => "MaxAttachmentSize", // Since 12.0
462
-				0x18 => "AllowSimpleDevicePassword", // Since 12.0
463
-				0x19 => "DevicePasswordExpiration", // Since 12.0
464
-				0x1A => "DevicePasswordHistory", // Since 12.0
465
-				0x1B => "AllowStorageCard", // Since 12.1
466
-				0x1C => "AllowCamera", // Since 12.1
467
-				0x1D => "RequireDeviceEncryption", // Since 12.1
468
-				0x1E => "AllowUnsignedApplications", // Since 12.1
469
-				0x1F => "AllowUnsignedInstallationPackages", // Since 12.1
470
-				0x20 => "MinDevicePasswordComplexCharacters", // Since 12.1
471
-				0x21 => "AllowWiFi", // Since 12.1
472
-				0x22 => "AllowTextMessaging", // Since 12.1
473
-				0x23 => "AllowPOPIMAPEmail", // Since 12.1
474
-				0x24 => "AllowBluetooth", // Since 12.1
475
-				0x25 => "AllowIrDA", // Since 12.1
476
-				0x26 => "RequireManualSyncWhenRoaming", // Since 12.1
477
-				0x27 => "AllowDesktopSync", // Since 12.1
478
-				0x28 => "MaxCalendarAgeFilter", // Since 12.1
479
-				0x29 => "AllowHTMLEmail", // Since 12.1
480
-				0x2A => "MaxEmailAgeFilter", // Since 12.1
481
-				0x2B => "MaxEmailBodyTruncationSize", // Since 12.1
482
-				0x2C => "MaxEmailHTMLBodyTruncationSize", // Since 12.1
483
-				0x2D => "RequireSignedSMIMEMessages", // Since 12.1
484
-				0x2E => "RequireEncryptedSMIMEMessages", // Since 12.1
485
-				0x2F => "RequireSignedSMIMEAlgorithm", // Since 12.1
486
-				0x30 => "RequireEncryptionSMIMEAlgorithm", // Since 12.1
487
-				0x31 => "AllowSMIMEEncryptionAlgorithmNegotiation", // Since 12.1
488
-				0x32 => "AllowSMIMESoftCerts", // Since 12.1
489
-				0x33 => "AllowBrowser", // Since 12.1
490
-				0x34 => "AllowConsumerEmail", // Since 12.1
491
-				0x35 => "AllowRemoteDesktop", // Since 12.1
492
-				0x36 => "AllowInternetSharing", // Since 12.1
493
-				0x37 => "UnapprovedInROMApplicationList", // Since 12.1
494
-				0x38 => "ApplicationName", // Since 12.1
495
-				0x39 => "ApprovedApplicationList", // Since 12.1
496
-				0x3A => "Hash", // Since 12.1
497
-			],
498
-			0xF => [
499
-				0x05 => "Search",
500
-				0x07 => "Store",
501
-				0x08 => "Name",
502
-				0x09 => "Query",
503
-				0x0A => "Options",
504
-				0x0B => "Range",
505
-				0x0C => "Status",
506
-				0x0D => "Response",
507
-				0x0E => "Result",
508
-				0x0F => "Properties",
509
-				0x10 => "Total",
510
-				0x11 => "EqualTo",
511
-				0x12 => "Value",
512
-				0x13 => "And",
513
-				0x14 => "Or",
514
-				0x15 => "FreeText",
515
-				0x17 => "DeepTraversal",
516
-				0x18 => "LongId",
517
-				0x19 => "RebuildResults",
518
-				0x1A => "LessThan",
519
-				0x1B => "GreaterThan",
520
-				0x1C => "Schema",
521
-				0x1D => "Supported",
522
-				0x1E => "UserName", // Since 12.1
523
-				0x1F => "Password", // Since 12.1
524
-				0x20 => "ConversationId", // Since 14.0
525
-				0x21 => "Picture", // Since 14.1
526
-				0x22 => "MaxSize", // Since 14.1
527
-				0x23 => "MaxPictures", // Since 14.1
528
-			],
529
-			0x10 => [
530
-				0x05 => "DisplayName",
531
-				0x06 => "Phone",
532
-				0x07 => "Office",
533
-				0x08 => "Title",
534
-				0x09 => "Company",
535
-				0x0A => "Alias",
536
-				0x0B => "FirstName",
537
-				0x0C => "LastName",
538
-				0x0D => "HomePhone",
539
-				0x0E => "MobilePhone",
540
-				0x0F => "EmailAddress",
541
-				0x10 => "Picture", // Since 14.1
542
-				0x11 => "Status", // Since 14.1
543
-				0x12 => "Data", // Since 14.1
544
-			],
545
-			0x11 => [ // Since 12.0
546
-				0x05 => "BodyPreference",
547
-				0x06 => "Type",
548
-				0x07 => "TruncationSize",
549
-				0x08 => "AllOrNone",
550
-				0x0A => "Body",
551
-				0x0B => "Data",
552
-				0x0C => "EstimatedDataSize",
553
-				0x0D => "Truncated",
554
-				0x0E => "Attachments",
555
-				0x0F => "Attachment",
556
-				0x10 => "DisplayName",
557
-				0x11 => "FileReference",
558
-				0x12 => "Method",
559
-				0x13 => "ContentId",
560
-				0x14 => "ContentLocation",
561
-				0x15 => "IsInline",
562
-				0x16 => "NativeBodyType",
563
-				0x17 => "ContentType",
564
-				0x18 => "Preview", // Since 14.0
565
-				0x19 => "BodyPartPreference", // Since 14.1
566
-				0x1A => "BodyPart", // Since 14.1
567
-				0x1B => "Status", // Since 14.1
568
-			],
569
-			0x12 => [ // Since 12.0
570
-				0x05 => "Settings",
571
-				0x06 => "Status",
572
-				0x07 => "Get",
573
-				0x08 => "Set",
574
-				0x09 => "Oof",
575
-				0x0A => "OofState",
576
-				0x0B => "StartTime",
577
-				0x0C => "EndTime",
578
-				0x0D => "OofMessage",
579
-				0x0E => "AppliesToInternal",
580
-				0x0F => "AppliesToExternalKnown",
581
-				0x10 => "AppliesToExternalUnknown",
582
-				0x11 => "Enabled",
583
-				0x12 => "ReplyMessage",
584
-				0x13 => "BodyType",
585
-				0x14 => "DevicePassword",
586
-				0x15 => "Password",
587
-				0x16 => "DeviceInformation",
588
-				0x17 => "Model",
589
-				0x18 => "IMEI",
590
-				0x19 => "FriendlyName",
591
-				0x1A => "OS",
592
-				0x1B => "OSLanguage",
593
-				0x1C => "PhoneNumber",
594
-				0x1D => "UserInformation",
595
-				0x1E => "EmailAddresses",
596
-				0x1F => "SmtpAddress",
597
-				0x20 => "UserAgent", // Since 12.1
598
-				0x21 => "EnableOutboundSMS", // Since 14.0
599
-				0x22 => "MobileOperator", // Since 14.0
600
-				0x23 => "PrimarySmtpAddress", // Since 14.1
601
-				0x24 => "Accounts", // Since 14.1
602
-				0x25 => "Account", // Since 14.1
603
-				0x26 => "AccountId", // Since 14.1
604
-				0x27 => "AccountName", // Since 14.1
605
-				0x28 => "UserDisplayName", // Since 14.1
606
-				0x29 => "SendDisabled", // Since 14.1
607
-				0x2B => "RightsManagementInformation", // Since 14.1
608
-			],
609
-			0x13 => [ // Since 12.0
610
-				0x05 => "LinkId",
611
-				0x06 => "DisplayName",
612
-				0x07 => "IsFolder",
613
-				0x08 => "CreationDate",
614
-				0x09 => "LastModifiedDate",
615
-				0x0A => "IsHidden",
616
-				0x0B => "ContentLength",
617
-				0x0C => "ContentType",
618
-			],
619
-			0x14 => [ // Since 12.0
620
-				0x05 => "ItemOperations",
621
-				0x06 => "Fetch",
622
-				0x07 => "Store",
623
-				0x08 => "Options",
624
-				0x09 => "Range",
625
-				0x0A => "Total",
626
-				0x0B => "Properties",
627
-				0x0C => "Data",
628
-				0x0D => "Status",
629
-				0x0E => "Response",
630
-				0x0F => "Version",
631
-				0x10 => "Schema",
632
-				0x11 => "Part",
633
-				0x12 => "EmptyFolderContents",
634
-				0x13 => "DeleteSubFolders",
635
-				0x14 => "UserName", // Since 12.1
636
-				0x15 => "Password", // Since 12.1
637
-				0x16 => "Move", // Since 14.0
638
-				0x17 => "DstFldId", // Since 14.0
639
-				0x18 => "ConversationId", // Since 14.0
640
-				0x19 => "MoveAlways", // Since 14.0
641
-			],
642
-			0x15 => [ // Since 14.0
643
-				0x05 => "SendMail",
644
-				0x06 => "SmartForward",
645
-				0x07 => "SmartReply",
646
-				0x08 => "SaveInSentItems",
647
-				0x09 => "ReplaceMime",
648
-				0x0A => "Type", // not used
649
-				0x0B => "Source",
650
-				0x0C => "FolderId",
651
-				0x0D => "ItemId",
652
-				0x0E => "LongId",
653
-				0x0F => "InstanceId",
654
-				0x10 => "MIME",
655
-				0x11 => "ClientId",
656
-				0x12 => "Status",
657
-				0x13 => "AccountId", // Since 14.1
658
-			],
659
-			0x16 => [ // Since 14.0
660
-				0x05 => "UmCallerId",
661
-				0x06 => "UmUserNotes",
662
-				0x07 => "UmAttDuration",
663
-				0x08 => "UmAttOrder",
664
-				0x09 => "ConversationId",
665
-				0x0A => "ConversationIndex",
666
-				0x0B => "LastVerbExecuted",
667
-				0x0C => "LastVerbExecutionTime",
668
-				0x0D => "ReceivedAsBcc",
669
-				0x0E => "Sender",
670
-				0x0F => "CalendarType",
671
-				0x10 => "IsLeapMonth",
672
-				0x11 => "AccountId", // Since 14.1
673
-				0x12 => "FirstDayOfWeek", // Since 14.1
674
-				0x13 => "MeetingMessageType", // Since 14.1
675
-			],
676
-			0x17 => [ // Since 14.0
677
-				0x05 => "Subject",
678
-				0x06 => "MessageClass",
679
-				0x07 => "LastModifiedDate",
680
-				0x08 => "Categories",
681
-				0x09 => "Category",
682
-			],
683
-			0x18 => [ // Since 14.1
684
-				0x05 => "RightsManagementSupport",
685
-				0x06 => "RightsManagementTemplates",
686
-				0x07 => "RightsManagementTemplate",
687
-				0x08 => "RightsManagementLicense",
688
-				0x09 => "EditAllowed",
689
-				0x0A => "ReplyAllowed",
690
-				0x0B => "ReplyAllAllowed",
691
-				0x0C => "ForwardAllowed",
692
-				0x0D => "ModifyRecipientsAllowed",
693
-				0x0E => "ExtractAllowed",
694
-				0x0F => "PrintAllowed",
695
-				0x10 => "ExportAllowed",
696
-				0x11 => "ProgrammaticAccessAllowed",
697
-				0x12 => "Owner",
698
-				0x13 => "ContentExpiryDate",
699
-				0x14 => "TemplateID",
700
-				0x15 => "TemplateName",
701
-				0x16 => "TemplateDescription",
702
-				0x17 => "ContentOwner",
703
-				0x18 => "RemoveRightsManagementProtection",
704
-			],
705
-		],
706
-		"namespaces" => [
707
-			// 0 => "AirSync", //
708
-			1 => "POOMCONTACTS",
709
-			2 => "POOMMAIL",
710
-			3 => "AirNotify", // no longer used
711
-			4 => "POOMCAL",
712
-			5 => "Move",
713
-			6 => "GetItemEstimate",
714
-			7 => "FolderHierarchy",
715
-			8 => "MeetingResponse",
716
-			9 => "POOMTASKS",
717
-			0xA => "ResolveRecipients",
718
-			0xB => "ValidateCert",
719
-			0xC => "POOMCONTACTS2",
720
-			0xD => "Ping",
721
-			0xE => "Provision",
722
-			0xF => "Search",
723
-			0x10 => "GAL",
724
-			0x11 => "AirSyncBase", // 12.0, 12.1 and 14.0
725
-			0x12 => "Settings", // 12.0, 12.1 and 14.0.
726
-			0x13 => "DocumentLibrary", // 12.0, 12.1 and 14.0
727
-			0x14 => "ItemOperations", // 12.0, 12.1 and 14.0
728
-			0x15 => "ComposeMail", // 14.0
729
-			0x16 => "POOMMAIL2", // 14.0
730
-			0x17 => "Notes", // 14.0
731
-			0x18 => "RightsManagement",
732
-		],
733
-	];
47
+    /**
48
+     * The WBXML DTDs.
49
+     */
50
+    protected $dtd = [
51
+        "codes" => [
52
+            0 => [
53
+                0x05 => "Synchronize",
54
+                0x06 => "Replies", // Responses
55
+                0x07 => "Add",
56
+                0x08 => "Modify", // Change
57
+                0x09 => "Remove", // Delete
58
+                0x0A => "Fetch",
59
+                0x0B => "SyncKey",
60
+                0x0C => "ClientEntryId", // ClientId
61
+                0x0D => "ServerEntryId", // ServerId
62
+                0x0E => "Status",
63
+                0x0F => "Folder", // collection
64
+                0x10 => "FolderType", // class
65
+                0x11 => "Version", // deprecated
66
+                0x12 => "FolderId", // CollectionId
67
+                0x13 => "GetChanges",
68
+                0x14 => "MoreAvailable",
69
+                0x15 => "WindowSize", // WindowSize - MaxItems before version 2
70
+                0x16 => "Perform", // Commands
71
+                0x17 => "Options",
72
+                0x18 => "FilterType",
73
+                0x19 => "Truncation", // 2.0 and 2.5
74
+                0x1A => "RtfTruncation", // 2.0 and 2.5
75
+                0x1B => "Conflict",
76
+                0x1C => "Folders", // Collections
77
+                0x1D => "Data",
78
+                0x1E => "DeletesAsMoves",
79
+                0x1F => "NotifyGUID", // 2.0 and 2.5
80
+                0x20 => "Supported",
81
+                0x21 => "SoftDelete",
82
+                0x22 => "MIMESupport",
83
+                0x23 => "MIMETruncation",
84
+                0x24 => "Wait", // Since 12.1
85
+                0x25 => "Limit", // Since 12.1
86
+                0x26 => "Partial", // Since 12.1
87
+                0x27 => "ConversationMode", // Since 14.0
88
+                0x28 => "MaxItems", // Since 14.0
89
+                0x29 => "HeartbeatInterval", // Since 14.0 Either this tag or the Wait tag can be present, but not both.
90
+            ],
91
+            1 => [
92
+                0x05 => "Anniversary",
93
+                0x06 => "AssistantName",
94
+                0x07 => "AssistnamePhoneNumber", // AssistantTelephoneNumber
95
+                0x08 => "Birthday",
96
+                0x09 => "Body", // 2.5, AirSyncBase Body is used since version 12.0
97
+                0x0A => "BodySize", // 2.0 and 2.5, AirSyncBase is used since version 12.0
98
+                0x0B => "BodyTruncated", // 2.0 and 2.5, AirSyncBase is used since version 12.0
99
+                0x0C => "Business2PhoneNumber",
100
+                0x0D => "BusinessCity",
101
+                0x0E => "BusinessCountry",
102
+                0x0F => "BusinessPostalCode",
103
+                0x10 => "BusinessState",
104
+                0x11 => "BusinessStreet",
105
+                0x12 => "BusinessFaxNumber",
106
+                0x13 => "BusinessPhoneNumber",
107
+                0x14 => "CarPhoneNumber",
108
+                0x15 => "Categories",
109
+                0x16 => "Category",
110
+                0x17 => "Children",
111
+                0x18 => "Child",
112
+                0x19 => "CompanyName",
113
+                0x1A => "Department",
114
+                0x1B => "Email1Address",
115
+                0x1C => "Email2Address",
116
+                0x1D => "Email3Address",
117
+                0x1E => "FileAs",
118
+                0x1F => "FirstName",
119
+                0x20 => "Home2PhoneNumber",
120
+                0x21 => "HomeCity",
121
+                0x22 => "HomeCountry",
122
+                0x23 => "HomePostalCode",
123
+                0x24 => "HomeState",
124
+                0x25 => "HomeStreet",
125
+                0x26 => "HomeFaxNumber",
126
+                0x27 => "HomePhoneNumber",
127
+                0x28 => "JobTitle",
128
+                0x29 => "LastName",
129
+                0x2A => "MiddleName",
130
+                0x2B => "MobilePhoneNumber",
131
+                0x2C => "OfficeLocation",
132
+                0x2D => "OtherCity",
133
+                0x2E => "OtherCountry",
134
+                0x2F => "OtherPostalCode",
135
+                0x30 => "OtherState",
136
+                0x31 => "OtherStreet",
137
+                0x32 => "PagerNumber",
138
+                0x33 => "RadioPhoneNumber",
139
+                0x34 => "Spouse",
140
+                0x35 => "Suffix",
141
+                0x36 => "Title",
142
+                0x37 => "WebPage",
143
+                0x38 => "YomiCompanyName",
144
+                0x39 => "YomiFirstName",
145
+                0x3A => "YomiLastName",
146
+                0x3B => "Rtf", // CompressedRTF - 2.5, deprecated
147
+                0x3C => "Picture",
148
+                0x3D => "Alias", // Since 14.0
149
+                0x3E => "WeightedRank", // Since 14.0
150
+            ],
151
+            2 => [
152
+                0x05 => "Attachment", // AirSyncBase Attachments is used since 12.0
153
+                0x06 => "Attachments", // AirSyncBase Attachments is used since 12.0
154
+                0x07 => "AttName", // AirSyncBase Attachments is used since 12.0
155
+                0x08 => "AttSize", // AirSyncBase Attachments is used since 12.0
156
+                0x09 => "AttOid", // AirSyncBase Attachments is used since 12.0
157
+                0x0A => "AttMethod", // AirSyncBase Attachments is used since 12.0
158
+                0x0B => "AttRemoved", // AirSyncBase Attachments is used since 12.0
159
+                0x0C => "Body", // AirSyncBase Body is used since 12.0
160
+                0x0D => "BodySize", // AirSyncBase Body is used since 12.0
161
+                0x0E => "BodyTruncated", // AirSyncBase Body is used since 12.0
162
+                0x0F => "DateReceived",
163
+                0x10 => "DisplayName", // AirSyncBase Attachments is used since 12.0
164
+                0x11 => "DisplayTo",
165
+                0x12 => "Importance",
166
+                0x13 => "MessageClass",
167
+                0x14 => "Subject",
168
+                0x15 => "Read",
169
+                0x16 => "To",
170
+                0x17 => "Cc",
171
+                0x18 => "From",
172
+                0x19 => "Reply-To",
173
+                0x1A => "AllDayEvent",
174
+                0x1B => "Categories", // Since 14.0
175
+                0x1C => "Category", // Since 14.0
176
+                0x1D => "DtStamp",
177
+                0x1E => "EndTime",
178
+                0x1F => "InstanceType",
179
+                0x20 => "BusyStatus",
180
+                0x21 => "Location", // 2.5, 12.0, 12.1, 14.0 and 14.1. Since 16.0 AirSyncBase Location is used.
181
+                0x22 => "MeetingRequest",
182
+                0x23 => "Organizer",
183
+                0x24 => "RecurrenceId",
184
+                0x25 => "Reminder",
185
+                0x26 => "ResponseRequested",
186
+                0x27 => "Recurrences",
187
+                0x28 => "Recurrence",
188
+                0x29 => "Type",
189
+                0x2A => "Until",
190
+                0x2B => "Occurrences",
191
+                0x2C => "Interval",
192
+                0x2D => "DayOfWeek",
193
+                0x2E => "DayOfMonth",
194
+                0x2F => "WeekOfMonth",
195
+                0x30 => "MonthOfYear",
196
+                0x31 => "StartTime",
197
+                0x32 => "Sensitivity",
198
+                0x33 => "TimeZone",
199
+                0x34 => "GlobalObjId", // 2.5, 12.0, 12.1, 14.0 and 14.1. UID of Calendar (Code page 4) is used since 16.0
200
+                0x35 => "ThreadTopic",
201
+                0x36 => "MIMEData", // 2.5
202
+                0x37 => "MIMETruncated", // 2.5
203
+                0x38 => "MIMESize", // 2.5
204
+                0x39 => "InternetCPID",
205
+                0x3A => "Flag", // Since 12.0
206
+                0x3B => "FlagStatus", // Since 12.0
207
+                0x3C => "ContentClass", // Since 12.0
208
+                0x3D => "FlagType", // Since 12.0
209
+                0x3E => "CompleteTime", // Since 12.0
210
+                0x3F => "DisallowNewTimeProposal", // Since 14.0
211
+            ],
212
+            3 => [ // Code page 3 is no longer in use, however, tokens 05 through 17 have been defined. 20100501
213
+                0x05 => "Notify",
214
+                0x06 => "Notification",
215
+                0x07 => "Version",
216
+                0x08 => "Lifetime",
217
+                0x09 => "DeviceInfo",
218
+                0x0A => "Enable",
219
+                0x0B => "Folder",
220
+                0x0C => "ServerEntryId",
221
+                0x0D => "DeviceAddress",
222
+                0x0E => "ValidCarrierProfiles",
223
+                0x0F => "CarrierProfile",
224
+                0x10 => "Status",
225
+                0x11 => "Replies",
226
+                //                        0x05 => "Version='1.1'",
227
+                0x12 => "Devices",
228
+                0x13 => "Device",
229
+                0x14 => "Id",
230
+                0x15 => "Expiry",
231
+                0x16 => "NotifyGUID",
232
+            ],
233
+            4 => [
234
+                0x05 => "Timezone",
235
+                0x06 => "AllDayEvent",
236
+                0x07 => "Attendees",
237
+                0x08 => "Attendee",
238
+                0x09 => "Email",
239
+                0x0A => "Name",
240
+                0x0B => "Body", // AirSyncBase Body is used since 12.0
241
+                0x0C => "BodyTruncated", // AirSyncBase Body is used since 12.0
242
+                0x0D => "BusyStatus",
243
+                0x0E => "Categories",
244
+                0x0F => "Category",
245
+                0x10 => "Rtf", // 2.5 - deprecated
246
+                0x11 => "DtStamp",
247
+                0x12 => "EndTime",
248
+                0x13 => "Exception",
249
+                0x14 => "Exceptions",
250
+                0x15 => "Deleted",
251
+                0x16 => "ExceptionStartTime", // 2.5, 12.0, 12.1, 14.0 and 14.1.
252
+                0x17 => "Location", // 2.5, 12.0, 12.1, 14.0 and 14.1. Since 16.0 AirSyncBase Location is used.
253
+                0x18 => "MeetingStatus",
254
+                0x19 => "OrganizerEmail",
255
+                0x1A => "OrganizerName",
256
+                0x1B => "Recurrence",
257
+                0x1C => "Type",
258
+                0x1D => "Until",
259
+                0x1E => "Occurrences",
260
+                0x1F => "Interval",
261
+                0x20 => "DayOfWeek",
262
+                0x21 => "DayOfMonth",
263
+                0x22 => "WeekOfMonth",
264
+                0x23 => "MonthOfYear",
265
+                0x24 => "Reminder",
266
+                0x25 => "Sensitivity",
267
+                0x26 => "Subject",
268
+                0x27 => "StartTime",
269
+                0x28 => "UID",
270
+                0x29 => "Attendee_Status", // Since 12.0
271
+                0x2A => "Attendee_Type", // Since 12.0
272
+                0x2B => "Attachment", // Not defined / deprecated
273
+                0x2C => "Attachments", // Not defined / deprecated
274
+                0x2D => "AttName", // Not defined / deprecated
275
+                0x2E => "AttSize", // Not defined / deprecated
276
+                0x2F => "AttOid", // Not defined / deprecated
277
+                0x30 => "AttMethod", // Not defined / deprecated
278
+                0x31 => "AttRemoved", // Not defined / deprecated
279
+                0x32 => "DisplayName", // Not defined / deprecated
280
+                0x33 => "DisallowNewTimeProposal", // Since 14.0
281
+                0x34 => "ResponseRequested", // Since 14.0
282
+                0x35 => "AppointmentReplyTime", // Since 14.0
283
+                0x36 => "ResponseType", // Since 14.0
284
+                0x37 => "CalendarType", // Since 14.0
285
+                0x38 => "IsLeapMonth", // Since 14.0
286
+                0x39 => "FirstDayOfWeek", // Since 14.1
287
+                0x3A => "OnlineMeetingConfLink", // Since 14.1
288
+                0x3B => "OnlineMeetingExternalLink", // Since 14.1
289
+            ],
290
+            5 => [
291
+                0x05 => "Moves",
292
+                0x06 => "Move",
293
+                0x07 => "SrcMsgId",
294
+                0x08 => "SrcFldId",
295
+                0x09 => "DstFldId",
296
+                0x0A => "Response",
297
+                0x0B => "Status",
298
+                0x0C => "DstMsgId",
299
+            ],
300
+            6 => [
301
+                0x05 => "GetItemEstimate",
302
+                0x06 => "Version", // deprecated
303
+                0x07 => "Folders", // Collections
304
+                0x08 => "Folder", // Collection
305
+                0x09 => "FolderType", // AirSync Class(SYNC_FOLDERTYPE) is used since AS 14.0
306
+                0x0A => "FolderId", // CollectionId
307
+                0x0B => "DateTime", // deprecated
308
+                0x0C => "Estimate",
309
+                0x0D => "Response",
310
+                0x0E => "Status",
311
+            ],
312
+            7 => [
313
+                0x05 => "Folders", // 2.5, 12.0 and 12.1
314
+                0x06 => "Folder", // 2.5, 12.0 and 12.1
315
+                0x07 => "DisplayName",
316
+                0x08 => "ServerEntryId", // ServerId
317
+                0x09 => "ParentId",
318
+                0x0A => "Type",
319
+                0x0B => "Response", // deprecated
320
+                0x0C => "Status",
321
+                0x0D => "ContentClass", // deprecated
322
+                0x0E => "Changes",
323
+                0x0F => "Add",
324
+                0x10 => "Remove",
325
+                0x11 => "Update",
326
+                0x12 => "SyncKey",
327
+                0x13 => "FolderCreate",
328
+                0x14 => "FolderDelete",
329
+                0x15 => "FolderUpdate",
330
+                0x16 => "FolderSync",
331
+                0x17 => "Count",
332
+                0x18 => "Version", // 2.0 - not defined in 20100501
333
+            ],
334
+            8 => [
335
+                0x05 => "CalendarId",
336
+                0x06 => "FolderId", // CollectionId
337
+                0x07 => "MeetingResponse",
338
+                0x08 => "RequestId",
339
+                0x09 => "Request",
340
+                0x0A => "Result",
341
+                0x0B => "Status",
342
+                0x0C => "UserResponse",
343
+                0x0D => "Version", // 2.0 - not defined in 20100501
344
+                0x0E => "InstanceId", // Since AS 14.1
345
+            ],
346
+            9 => [
347
+                0x05 => "Body", // AirSyncBase Body is used since 12.0
348
+                0x06 => "BodySize", // AirSyncBase Body is used since 12.0
349
+                0x07 => "BodyTruncated", // AirSyncBase Body is used since 12.0
350
+                0x08 => "Categories",
351
+                0x09 => "Category",
352
+                0x0A => "Complete",
353
+                0x0B => "DateCompleted",
354
+                0x0C => "DueDate",
355
+                0x0D => "UtcDueDate",
356
+                0x0E => "Importance",
357
+                0x0F => "Recurrence",
358
+                0x10 => "Type",
359
+                0x11 => "Start",
360
+                0x12 => "Until",
361
+                0x13 => "Occurrences",
362
+                0x14 => "Interval",
363
+                0x15 => "DayOfMonth",
364
+                0x16 => "DayOfWeek",
365
+                0x17 => "WeekOfMonth",
366
+                0x18 => "MonthOfYear",
367
+                0x19 => "Regenerate",
368
+                0x1A => "DeadOccur",
369
+                0x1B => "ReminderSet",
370
+                0x1C => "ReminderTime",
371
+                0x1D => "Sensitivity",
372
+                0x1E => "StartDate",
373
+                0x1F => "UtcStartDate",
374
+                0x20 => "Subject",
375
+                0x21 => "Rtf", // deprecated
376
+                0x22 => "OrdinalDate", // Since 12.0
377
+                0x23 => "SubOrdinalDate", // Since 12.0
378
+                0x24 => "CalendarType", // Since 14.0
379
+                0x25 => "IsLeapMonth", // Since 14.0
380
+                0x26 => "FirstDayOfWeek", // Since 14.1
381
+            ],
382
+            0xA => [
383
+                0x05 => "ResolveRecipients",
384
+                0x06 => "Response",
385
+                0x07 => "Status",
386
+                0x08 => "Type",
387
+                0x09 => "Recipient",
388
+                0x0A => "DisplayName",
389
+                0x0B => "EmailAddress",
390
+                0x0C => "Certificates",
391
+                0x0D => "Certificate",
392
+                0x0E => "MiniCertificate",
393
+                0x0F => "Options",
394
+                0x10 => "To",
395
+                0x11 => "CertificateRetrieval",
396
+                0x12 => "RecipientCount",
397
+                0x13 => "MaxCertificates",
398
+                0x14 => "MaxAmbiguousRecipients",
399
+                0x15 => "CertificateCount",
400
+                0x16 => "Availability", // Since 14.0
401
+                0x17 => "StartTime", // Since 14.0
402
+                0x18 => "EndTime", // Since 14.0
403
+                0x19 => "MergedFreeBusy", // Since 14.0
404
+                0x1A => "Picture", // Since 14.1
405
+                0x1B => "MaxSize", // Since 14.1
406
+                0x1C => "Data", // Since 14.1
407
+                0x1D => "MaxPictures", // Since 14.1
408
+            ],
409
+            0xB => [
410
+                0x05 => "ValidateCert",
411
+                0x06 => "Certificates",
412
+                0x07 => "Certificate",
413
+                0x08 => "CertificateChain",
414
+                0x09 => "CheckCRL",
415
+                0x0A => "Status",
416
+            ],
417
+            0xC => [
418
+                0x05 => "CustomerId",
419
+                0x06 => "GovernmentId",
420
+                0x07 => "IMAddress",
421
+                0x08 => "IMAddress2",
422
+                0x09 => "IMAddress3",
423
+                0x0A => "ManagerName",
424
+                0x0B => "CompanyMainPhone",
425
+                0x0C => "AccountName",
426
+                0x0D => "NickName",
427
+                0x0E => "MMS",
428
+            ],
429
+            0xD => [
430
+                0x05 => "Ping",
431
+                0x06 => "AutdState", // (Not used by protocol)
432
+                0x07 => "Status",
433
+                0x08 => "LifeTime", // HeartbeatInterval
434
+                0x09 => "Folders",
435
+                0x0A => "Folder",
436
+                0x0B => "ServerEntryId", // Id
437
+                0x0C => "FolderType", // Class
438
+                0x0D => "MaxFolders",
439
+                0x0E => "Version", // not defined / deprecated
440
+            ],
441
+            0xE => [
442
+                0x05 => "Provision",
443
+                0x06 => "Policies",
444
+                0x07 => "Policy",
445
+                0x08 => "PolicyType",
446
+                0x09 => "PolicyKey",
447
+                0x0A => "Data",
448
+                0x0B => "Status",
449
+                0x0C => "RemoteWipe",
450
+                0x0D => "EASProvisionDoc", // Since 12.0
451
+                0x0E => "DevicePasswordEnabled", // Since 12.0
452
+                0x0F => "AlphanumericDevicePasswordRequired", // Since 12.0
453
+                0x10 => "DeviceEncryptionEnabled", // Since 12.1
454
+                // 0x10 => "RequireStorageCardEncryption", //Since 12.1
455
+                0x11 => "PasswordRecoveryEnabled", // Since 12.0
456
+                0x12 => "DocumentBrowseEnabled", // not defined / deprecated
457
+                0x13 => "AttachmentsEnabled", // Since 12.0
458
+                0x14 => "MinDevicePasswordLength", // Since 12.0
459
+                0x15 => "MaxInactivityTimeDeviceLock", // Since 12.0
460
+                0x16 => "MaxDevicePasswordFailedAttempts", // Since 12.0
461
+                0x17 => "MaxAttachmentSize", // Since 12.0
462
+                0x18 => "AllowSimpleDevicePassword", // Since 12.0
463
+                0x19 => "DevicePasswordExpiration", // Since 12.0
464
+                0x1A => "DevicePasswordHistory", // Since 12.0
465
+                0x1B => "AllowStorageCard", // Since 12.1
466
+                0x1C => "AllowCamera", // Since 12.1
467
+                0x1D => "RequireDeviceEncryption", // Since 12.1
468
+                0x1E => "AllowUnsignedApplications", // Since 12.1
469
+                0x1F => "AllowUnsignedInstallationPackages", // Since 12.1
470
+                0x20 => "MinDevicePasswordComplexCharacters", // Since 12.1
471
+                0x21 => "AllowWiFi", // Since 12.1
472
+                0x22 => "AllowTextMessaging", // Since 12.1
473
+                0x23 => "AllowPOPIMAPEmail", // Since 12.1
474
+                0x24 => "AllowBluetooth", // Since 12.1
475
+                0x25 => "AllowIrDA", // Since 12.1
476
+                0x26 => "RequireManualSyncWhenRoaming", // Since 12.1
477
+                0x27 => "AllowDesktopSync", // Since 12.1
478
+                0x28 => "MaxCalendarAgeFilter", // Since 12.1
479
+                0x29 => "AllowHTMLEmail", // Since 12.1
480
+                0x2A => "MaxEmailAgeFilter", // Since 12.1
481
+                0x2B => "MaxEmailBodyTruncationSize", // Since 12.1
482
+                0x2C => "MaxEmailHTMLBodyTruncationSize", // Since 12.1
483
+                0x2D => "RequireSignedSMIMEMessages", // Since 12.1
484
+                0x2E => "RequireEncryptedSMIMEMessages", // Since 12.1
485
+                0x2F => "RequireSignedSMIMEAlgorithm", // Since 12.1
486
+                0x30 => "RequireEncryptionSMIMEAlgorithm", // Since 12.1
487
+                0x31 => "AllowSMIMEEncryptionAlgorithmNegotiation", // Since 12.1
488
+                0x32 => "AllowSMIMESoftCerts", // Since 12.1
489
+                0x33 => "AllowBrowser", // Since 12.1
490
+                0x34 => "AllowConsumerEmail", // Since 12.1
491
+                0x35 => "AllowRemoteDesktop", // Since 12.1
492
+                0x36 => "AllowInternetSharing", // Since 12.1
493
+                0x37 => "UnapprovedInROMApplicationList", // Since 12.1
494
+                0x38 => "ApplicationName", // Since 12.1
495
+                0x39 => "ApprovedApplicationList", // Since 12.1
496
+                0x3A => "Hash", // Since 12.1
497
+            ],
498
+            0xF => [
499
+                0x05 => "Search",
500
+                0x07 => "Store",
501
+                0x08 => "Name",
502
+                0x09 => "Query",
503
+                0x0A => "Options",
504
+                0x0B => "Range",
505
+                0x0C => "Status",
506
+                0x0D => "Response",
507
+                0x0E => "Result",
508
+                0x0F => "Properties",
509
+                0x10 => "Total",
510
+                0x11 => "EqualTo",
511
+                0x12 => "Value",
512
+                0x13 => "And",
513
+                0x14 => "Or",
514
+                0x15 => "FreeText",
515
+                0x17 => "DeepTraversal",
516
+                0x18 => "LongId",
517
+                0x19 => "RebuildResults",
518
+                0x1A => "LessThan",
519
+                0x1B => "GreaterThan",
520
+                0x1C => "Schema",
521
+                0x1D => "Supported",
522
+                0x1E => "UserName", // Since 12.1
523
+                0x1F => "Password", // Since 12.1
524
+                0x20 => "ConversationId", // Since 14.0
525
+                0x21 => "Picture", // Since 14.1
526
+                0x22 => "MaxSize", // Since 14.1
527
+                0x23 => "MaxPictures", // Since 14.1
528
+            ],
529
+            0x10 => [
530
+                0x05 => "DisplayName",
531
+                0x06 => "Phone",
532
+                0x07 => "Office",
533
+                0x08 => "Title",
534
+                0x09 => "Company",
535
+                0x0A => "Alias",
536
+                0x0B => "FirstName",
537
+                0x0C => "LastName",
538
+                0x0D => "HomePhone",
539
+                0x0E => "MobilePhone",
540
+                0x0F => "EmailAddress",
541
+                0x10 => "Picture", // Since 14.1
542
+                0x11 => "Status", // Since 14.1
543
+                0x12 => "Data", // Since 14.1
544
+            ],
545
+            0x11 => [ // Since 12.0
546
+                0x05 => "BodyPreference",
547
+                0x06 => "Type",
548
+                0x07 => "TruncationSize",
549
+                0x08 => "AllOrNone",
550
+                0x0A => "Body",
551
+                0x0B => "Data",
552
+                0x0C => "EstimatedDataSize",
553
+                0x0D => "Truncated",
554
+                0x0E => "Attachments",
555
+                0x0F => "Attachment",
556
+                0x10 => "DisplayName",
557
+                0x11 => "FileReference",
558
+                0x12 => "Method",
559
+                0x13 => "ContentId",
560
+                0x14 => "ContentLocation",
561
+                0x15 => "IsInline",
562
+                0x16 => "NativeBodyType",
563
+                0x17 => "ContentType",
564
+                0x18 => "Preview", // Since 14.0
565
+                0x19 => "BodyPartPreference", // Since 14.1
566
+                0x1A => "BodyPart", // Since 14.1
567
+                0x1B => "Status", // Since 14.1
568
+            ],
569
+            0x12 => [ // Since 12.0
570
+                0x05 => "Settings",
571
+                0x06 => "Status",
572
+                0x07 => "Get",
573
+                0x08 => "Set",
574
+                0x09 => "Oof",
575
+                0x0A => "OofState",
576
+                0x0B => "StartTime",
577
+                0x0C => "EndTime",
578
+                0x0D => "OofMessage",
579
+                0x0E => "AppliesToInternal",
580
+                0x0F => "AppliesToExternalKnown",
581
+                0x10 => "AppliesToExternalUnknown",
582
+                0x11 => "Enabled",
583
+                0x12 => "ReplyMessage",
584
+                0x13 => "BodyType",
585
+                0x14 => "DevicePassword",
586
+                0x15 => "Password",
587
+                0x16 => "DeviceInformation",
588
+                0x17 => "Model",
589
+                0x18 => "IMEI",
590
+                0x19 => "FriendlyName",
591
+                0x1A => "OS",
592
+                0x1B => "OSLanguage",
593
+                0x1C => "PhoneNumber",
594
+                0x1D => "UserInformation",
595
+                0x1E => "EmailAddresses",
596
+                0x1F => "SmtpAddress",
597
+                0x20 => "UserAgent", // Since 12.1
598
+                0x21 => "EnableOutboundSMS", // Since 14.0
599
+                0x22 => "MobileOperator", // Since 14.0
600
+                0x23 => "PrimarySmtpAddress", // Since 14.1
601
+                0x24 => "Accounts", // Since 14.1
602
+                0x25 => "Account", // Since 14.1
603
+                0x26 => "AccountId", // Since 14.1
604
+                0x27 => "AccountName", // Since 14.1
605
+                0x28 => "UserDisplayName", // Since 14.1
606
+                0x29 => "SendDisabled", // Since 14.1
607
+                0x2B => "RightsManagementInformation", // Since 14.1
608
+            ],
609
+            0x13 => [ // Since 12.0
610
+                0x05 => "LinkId",
611
+                0x06 => "DisplayName",
612
+                0x07 => "IsFolder",
613
+                0x08 => "CreationDate",
614
+                0x09 => "LastModifiedDate",
615
+                0x0A => "IsHidden",
616
+                0x0B => "ContentLength",
617
+                0x0C => "ContentType",
618
+            ],
619
+            0x14 => [ // Since 12.0
620
+                0x05 => "ItemOperations",
621
+                0x06 => "Fetch",
622
+                0x07 => "Store",
623
+                0x08 => "Options",
624
+                0x09 => "Range",
625
+                0x0A => "Total",
626
+                0x0B => "Properties",
627
+                0x0C => "Data",
628
+                0x0D => "Status",
629
+                0x0E => "Response",
630
+                0x0F => "Version",
631
+                0x10 => "Schema",
632
+                0x11 => "Part",
633
+                0x12 => "EmptyFolderContents",
634
+                0x13 => "DeleteSubFolders",
635
+                0x14 => "UserName", // Since 12.1
636
+                0x15 => "Password", // Since 12.1
637
+                0x16 => "Move", // Since 14.0
638
+                0x17 => "DstFldId", // Since 14.0
639
+                0x18 => "ConversationId", // Since 14.0
640
+                0x19 => "MoveAlways", // Since 14.0
641
+            ],
642
+            0x15 => [ // Since 14.0
643
+                0x05 => "SendMail",
644
+                0x06 => "SmartForward",
645
+                0x07 => "SmartReply",
646
+                0x08 => "SaveInSentItems",
647
+                0x09 => "ReplaceMime",
648
+                0x0A => "Type", // not used
649
+                0x0B => "Source",
650
+                0x0C => "FolderId",
651
+                0x0D => "ItemId",
652
+                0x0E => "LongId",
653
+                0x0F => "InstanceId",
654
+                0x10 => "MIME",
655
+                0x11 => "ClientId",
656
+                0x12 => "Status",
657
+                0x13 => "AccountId", // Since 14.1
658
+            ],
659
+            0x16 => [ // Since 14.0
660
+                0x05 => "UmCallerId",
661
+                0x06 => "UmUserNotes",
662
+                0x07 => "UmAttDuration",
663
+                0x08 => "UmAttOrder",
664
+                0x09 => "ConversationId",
665
+                0x0A => "ConversationIndex",
666
+                0x0B => "LastVerbExecuted",
667
+                0x0C => "LastVerbExecutionTime",
668
+                0x0D => "ReceivedAsBcc",
669
+                0x0E => "Sender",
670
+                0x0F => "CalendarType",
671
+                0x10 => "IsLeapMonth",
672
+                0x11 => "AccountId", // Since 14.1
673
+                0x12 => "FirstDayOfWeek", // Since 14.1
674
+                0x13 => "MeetingMessageType", // Since 14.1
675
+            ],
676
+            0x17 => [ // Since 14.0
677
+                0x05 => "Subject",
678
+                0x06 => "MessageClass",
679
+                0x07 => "LastModifiedDate",
680
+                0x08 => "Categories",
681
+                0x09 => "Category",
682
+            ],
683
+            0x18 => [ // Since 14.1
684
+                0x05 => "RightsManagementSupport",
685
+                0x06 => "RightsManagementTemplates",
686
+                0x07 => "RightsManagementTemplate",
687
+                0x08 => "RightsManagementLicense",
688
+                0x09 => "EditAllowed",
689
+                0x0A => "ReplyAllowed",
690
+                0x0B => "ReplyAllAllowed",
691
+                0x0C => "ForwardAllowed",
692
+                0x0D => "ModifyRecipientsAllowed",
693
+                0x0E => "ExtractAllowed",
694
+                0x0F => "PrintAllowed",
695
+                0x10 => "ExportAllowed",
696
+                0x11 => "ProgrammaticAccessAllowed",
697
+                0x12 => "Owner",
698
+                0x13 => "ContentExpiryDate",
699
+                0x14 => "TemplateID",
700
+                0x15 => "TemplateName",
701
+                0x16 => "TemplateDescription",
702
+                0x17 => "ContentOwner",
703
+                0x18 => "RemoveRightsManagementProtection",
704
+            ],
705
+        ],
706
+        "namespaces" => [
707
+            // 0 => "AirSync", //
708
+            1 => "POOMCONTACTS",
709
+            2 => "POOMMAIL",
710
+            3 => "AirNotify", // no longer used
711
+            4 => "POOMCAL",
712
+            5 => "Move",
713
+            6 => "GetItemEstimate",
714
+            7 => "FolderHierarchy",
715
+            8 => "MeetingResponse",
716
+            9 => "POOMTASKS",
717
+            0xA => "ResolveRecipients",
718
+            0xB => "ValidateCert",
719
+            0xC => "POOMCONTACTS2",
720
+            0xD => "Ping",
721
+            0xE => "Provision",
722
+            0xF => "Search",
723
+            0x10 => "GAL",
724
+            0x11 => "AirSyncBase", // 12.0, 12.1 and 14.0
725
+            0x12 => "Settings", // 12.0, 12.1 and 14.0.
726
+            0x13 => "DocumentLibrary", // 12.0, 12.1 and 14.0
727
+            0x14 => "ItemOperations", // 12.0, 12.1 and 14.0
728
+            0x15 => "ComposeMail", // 14.0
729
+            0x16 => "POOMMAIL2", // 14.0
730
+            0x17 => "Notes", // 14.0
731
+            0x18 => "RightsManagement",
732
+        ],
733
+    ];
734 734
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -209,7 +209,7 @@  discard block
 block discarded – undo
209 209
 				0x3E => "CompleteTime", // Since 12.0
210 210
 				0x3F => "DisallowNewTimeProposal", // Since 14.0
211 211
 			],
212
-			3 => [ // Code page 3 is no longer in use, however, tokens 05 through 17 have been defined. 20100501
212
+			3 => [// Code page 3 is no longer in use, however, tokens 05 through 17 have been defined. 20100501
213 213
 				0x05 => "Notify",
214 214
 				0x06 => "Notification",
215 215
 				0x07 => "Version",
@@ -542,7 +542,7 @@  discard block
 block discarded – undo
542 542
 				0x11 => "Status", // Since 14.1
543 543
 				0x12 => "Data", // Since 14.1
544 544
 			],
545
-			0x11 => [ // Since 12.0
545
+			0x11 => [// Since 12.0
546 546
 				0x05 => "BodyPreference",
547 547
 				0x06 => "Type",
548 548
 				0x07 => "TruncationSize",
@@ -566,7 +566,7 @@  discard block
 block discarded – undo
566 566
 				0x1A => "BodyPart", // Since 14.1
567 567
 				0x1B => "Status", // Since 14.1
568 568
 			],
569
-			0x12 => [ // Since 12.0
569
+			0x12 => [// Since 12.0
570 570
 				0x05 => "Settings",
571 571
 				0x06 => "Status",
572 572
 				0x07 => "Get",
@@ -606,7 +606,7 @@  discard block
 block discarded – undo
606 606
 				0x29 => "SendDisabled", // Since 14.1
607 607
 				0x2B => "RightsManagementInformation", // Since 14.1
608 608
 			],
609
-			0x13 => [ // Since 12.0
609
+			0x13 => [// Since 12.0
610 610
 				0x05 => "LinkId",
611 611
 				0x06 => "DisplayName",
612 612
 				0x07 => "IsFolder",
@@ -616,7 +616,7 @@  discard block
 block discarded – undo
616 616
 				0x0B => "ContentLength",
617 617
 				0x0C => "ContentType",
618 618
 			],
619
-			0x14 => [ // Since 12.0
619
+			0x14 => [// Since 12.0
620 620
 				0x05 => "ItemOperations",
621 621
 				0x06 => "Fetch",
622 622
 				0x07 => "Store",
@@ -639,7 +639,7 @@  discard block
 block discarded – undo
639 639
 				0x18 => "ConversationId", // Since 14.0
640 640
 				0x19 => "MoveAlways", // Since 14.0
641 641
 			],
642
-			0x15 => [ // Since 14.0
642
+			0x15 => [// Since 14.0
643 643
 				0x05 => "SendMail",
644 644
 				0x06 => "SmartForward",
645 645
 				0x07 => "SmartReply",
@@ -656,7 +656,7 @@  discard block
 block discarded – undo
656 656
 				0x12 => "Status",
657 657
 				0x13 => "AccountId", // Since 14.1
658 658
 			],
659
-			0x16 => [ // Since 14.0
659
+			0x16 => [// Since 14.0
660 660
 				0x05 => "UmCallerId",
661 661
 				0x06 => "UmUserNotes",
662 662
 				0x07 => "UmAttDuration",
@@ -673,14 +673,14 @@  discard block
 block discarded – undo
673 673
 				0x12 => "FirstDayOfWeek", // Since 14.1
674 674
 				0x13 => "MeetingMessageType", // Since 14.1
675 675
 			],
676
-			0x17 => [ // Since 14.0
676
+			0x17 => [// Since 14.0
677 677
 				0x05 => "Subject",
678 678
 				0x06 => "MessageClass",
679 679
 				0x07 => "LastModifiedDate",
680 680
 				0x08 => "Categories",
681 681
 				0x09 => "Category",
682 682
 			],
683
-			0x18 => [ // Since 14.1
683
+			0x18 => [// Since 14.1
684 684
 				0x05 => "RightsManagementSupport",
685 685
 				0x06 => "RightsManagementTemplates",
686 686
 				0x07 => "RightsManagementTemplate",
Please login to merge, or discard this patch.
lib/wbxml/wbxmlencoder.php 3 patches
Indentation   +507 added lines, -507 removed lines patch added patch discarded remove patch
@@ -8,514 +8,514 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 class WBXMLEncoder extends WBXMLDefs {
11
-	private $_dtd;
12
-	private $_out;
13
-	private $_tagcp = 0;
14
-	private $log = false;
15
-	private $logStack = [];
16
-
17
-	// We use a delayed output mechanism in which we only output a tag when it actually has something
18
-	// in it. This can cause entire XML trees to disappear if they don't have output data in them; Ie
19
-	// calling 'startTag' 10 times, and then 'endTag' will cause 0 bytes of output apart from the header.
20
-
21
-	// Only when content() is called do we output the current stack of tags
22
-
23
-	private $_stack;
24
-	private $multipart; // the content is multipart
25
-	private $bodyparts;
26
-
27
-	public function __construct($output, $multipart = false) {
28
-		$this->log = SLog::IsWbxmlDebugEnabled();
29
-		$this->_out = $output;
30
-
31
-		// reverse-map the DTD
32
-		foreach ($this->dtd["namespaces"] as $nsid => $nsname) {
33
-			$this->_dtd["namespaces"][$nsname] = $nsid;
34
-		}
35
-
36
-		foreach ($this->dtd["codes"] as $cp => $value) {
37
-			$this->_dtd["codes"][$cp] = [];
38
-			foreach ($this->dtd["codes"][$cp] as $tagid => $tagname) {
39
-				$this->_dtd["codes"][$cp][$tagname] = $tagid;
40
-			}
41
-		}
42
-		$this->_stack = [];
43
-		$this->multipart = $multipart;
44
-		$this->bodyparts = [];
45
-	}
46
-
47
-	/**
48
-	 * Puts the WBXML header on the stream.
49
-	 *
50
-	 * @return
51
-	 */
52
-	public function startWBXML() {
53
-		if ($this->multipart) {
54
-			header("Content-Type: application/vnd.ms-sync.multipart");
55
-			SLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->startWBXML() type: vnd.ms-sync.multipart");
56
-		}
57
-		else {
58
-			header("Content-Type: application/vnd.ms-sync.wbxml");
59
-			SLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->startWBXML() type: vnd.ms-sync.wbxml");
60
-		}
61
-
62
-		$this->outByte(0x03); // WBXML 1.3
63
-		$this->outMBUInt(0x01); // Public ID 1
64
-		$this->outMBUInt(106); // UTF-8
65
-		$this->outMBUInt(0x00); // string table length (0)
66
-	}
67
-
68
-	/**
69
-	 * Puts a StartTag on the output stack.
70
-	 *
71
-	 * @param $tag
72
-	 * @param $attributes
73
-	 * @param $nocontent
74
-	 *
75
-	 * @return
76
-	 */
77
-	public function startTag($tag, $attributes = false, $nocontent = false) {
78
-		$stackelem = [];
79
-
80
-		if (!$nocontent) {
81
-			$stackelem['tag'] = $tag;
82
-			$stackelem['nocontent'] = $nocontent;
83
-			$stackelem['sent'] = false;
84
-
85
-			array_push($this->_stack, $stackelem);
86
-
87
-		// If 'nocontent' is specified, then apparently the user wants to force
88
-			// output of an empty tag, and we therefore output the stack here
89
-		}
90
-		else {
91
-			$this->_outputStack();
92
-			$this->_startTag($tag, $nocontent);
93
-		}
94
-	}
95
-
96
-	/**
97
-	 * Puts an EndTag on the stack.
98
-	 *
99
-	 * @return
100
-	 */
101
-	public function endTag() {
102
-		$stackelem = array_pop($this->_stack);
103
-
104
-		// Only output end tags for items that have had a start tag sent
105
-		if ($stackelem['sent']) {
106
-			$this->_endTag();
107
-
108
-			if (count($this->_stack) == 0) {
109
-				SLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->endTag() WBXML output completed");
110
-			}
111
-			if (count($this->_stack) == 0 && $this->multipart == true) {
112
-				$this->processMultipart();
113
-			}
114
-			if (count($this->_stack) == 0) {
115
-				$this->writeLog();
116
-			}
117
-		}
118
-	}
119
-
120
-	/**
121
-	 * Puts content on the output stack.
122
-	 *
123
-	 * @param string $content
124
-	 *
125
-	 * @return
126
-	 */
127
-	public function content($content) {
128
-		// We need to filter out any \0 chars because it's the string terminator in WBXML. We currently
129
-		// cannot send \0 characters within the XML content anywhere.
130
-		$content = str_replace("\0", "", $content);
131
-		if ("x" . $content == "x") {
132
-			return;
133
-		}
134
-		$this->_outputStack();
135
-		$this->_content($content);
136
-	}
137
-
138
-	/**
139
-	 * Puts content of a stream on the output stack AND closes it.
140
-	 *
141
-	 * @param resource $stream
142
-	 * @param bool     $asBase64 if true, the data will be encoded as base64, default: false
143
-	 * @param bool     $opaque   if true, output the opaque data, default: false
144
-	 *
145
-	 * @return
146
-	 */
147
-	public function contentStream($stream, $asBase64 = false, $opaque = false) {
148
-		// Do not append filters to opaque data as it might contain null char
149
-		if (!$asBase64 && !$opaque) {
150
-			stream_filter_register('replacenullchar', 'ReplaceNullcharFilter');
151
-			$rnc_filter = stream_filter_append($stream, 'replacenullchar');
152
-		}
153
-
154
-		$this->_outputStack();
155
-		$this->_contentStream($stream, $asBase64, $opaque);
156
-
157
-		if (!$asBase64 && !$opaque) {
158
-			stream_filter_remove($rnc_filter);
159
-		}
160
-
161
-		fclose($stream);
162
-	}
163
-
164
-	/**
165
-	 * Gets the value of multipart.
166
-	 *
167
-	 * @return bool
168
-	 */
169
-	public function getMultipart() {
170
-		return $this->multipart;
171
-	}
172
-
173
-	/**
174
-	 * Adds a bodypart.
175
-	 *
176
-	 * @param Stream $bp
177
-	 */
178
-	public function addBodypartStream($bp) {
179
-		if (!is_resource($bp)) {
180
-			throw new WBXMLException("WBXMLEncoder->addBodypartStream(): trying to add a " . gettype($bp) . " instead of a stream");
181
-		}
182
-		if ($this->multipart) {
183
-			$this->bodyparts[] = $bp;
184
-		}
185
-	}
186
-
187
-	/**
188
-	 * Gets the number of bodyparts.
189
-	 *
190
-	 * @return int
191
-	 */
192
-	public function getBodypartsCount() {
193
-		return count($this->bodyparts);
194
-	}
195
-
196
-	/*----------------------------------------------------------------------------------------------------------
11
+    private $_dtd;
12
+    private $_out;
13
+    private $_tagcp = 0;
14
+    private $log = false;
15
+    private $logStack = [];
16
+
17
+    // We use a delayed output mechanism in which we only output a tag when it actually has something
18
+    // in it. This can cause entire XML trees to disappear if they don't have output data in them; Ie
19
+    // calling 'startTag' 10 times, and then 'endTag' will cause 0 bytes of output apart from the header.
20
+
21
+    // Only when content() is called do we output the current stack of tags
22
+
23
+    private $_stack;
24
+    private $multipart; // the content is multipart
25
+    private $bodyparts;
26
+
27
+    public function __construct($output, $multipart = false) {
28
+        $this->log = SLog::IsWbxmlDebugEnabled();
29
+        $this->_out = $output;
30
+
31
+        // reverse-map the DTD
32
+        foreach ($this->dtd["namespaces"] as $nsid => $nsname) {
33
+            $this->_dtd["namespaces"][$nsname] = $nsid;
34
+        }
35
+
36
+        foreach ($this->dtd["codes"] as $cp => $value) {
37
+            $this->_dtd["codes"][$cp] = [];
38
+            foreach ($this->dtd["codes"][$cp] as $tagid => $tagname) {
39
+                $this->_dtd["codes"][$cp][$tagname] = $tagid;
40
+            }
41
+        }
42
+        $this->_stack = [];
43
+        $this->multipart = $multipart;
44
+        $this->bodyparts = [];
45
+    }
46
+
47
+    /**
48
+     * Puts the WBXML header on the stream.
49
+     *
50
+     * @return
51
+     */
52
+    public function startWBXML() {
53
+        if ($this->multipart) {
54
+            header("Content-Type: application/vnd.ms-sync.multipart");
55
+            SLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->startWBXML() type: vnd.ms-sync.multipart");
56
+        }
57
+        else {
58
+            header("Content-Type: application/vnd.ms-sync.wbxml");
59
+            SLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->startWBXML() type: vnd.ms-sync.wbxml");
60
+        }
61
+
62
+        $this->outByte(0x03); // WBXML 1.3
63
+        $this->outMBUInt(0x01); // Public ID 1
64
+        $this->outMBUInt(106); // UTF-8
65
+        $this->outMBUInt(0x00); // string table length (0)
66
+    }
67
+
68
+    /**
69
+     * Puts a StartTag on the output stack.
70
+     *
71
+     * @param $tag
72
+     * @param $attributes
73
+     * @param $nocontent
74
+     *
75
+     * @return
76
+     */
77
+    public function startTag($tag, $attributes = false, $nocontent = false) {
78
+        $stackelem = [];
79
+
80
+        if (!$nocontent) {
81
+            $stackelem['tag'] = $tag;
82
+            $stackelem['nocontent'] = $nocontent;
83
+            $stackelem['sent'] = false;
84
+
85
+            array_push($this->_stack, $stackelem);
86
+
87
+        // If 'nocontent' is specified, then apparently the user wants to force
88
+            // output of an empty tag, and we therefore output the stack here
89
+        }
90
+        else {
91
+            $this->_outputStack();
92
+            $this->_startTag($tag, $nocontent);
93
+        }
94
+    }
95
+
96
+    /**
97
+     * Puts an EndTag on the stack.
98
+     *
99
+     * @return
100
+     */
101
+    public function endTag() {
102
+        $stackelem = array_pop($this->_stack);
103
+
104
+        // Only output end tags for items that have had a start tag sent
105
+        if ($stackelem['sent']) {
106
+            $this->_endTag();
107
+
108
+            if (count($this->_stack) == 0) {
109
+                SLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->endTag() WBXML output completed");
110
+            }
111
+            if (count($this->_stack) == 0 && $this->multipart == true) {
112
+                $this->processMultipart();
113
+            }
114
+            if (count($this->_stack) == 0) {
115
+                $this->writeLog();
116
+            }
117
+        }
118
+    }
119
+
120
+    /**
121
+     * Puts content on the output stack.
122
+     *
123
+     * @param string $content
124
+     *
125
+     * @return
126
+     */
127
+    public function content($content) {
128
+        // We need to filter out any \0 chars because it's the string terminator in WBXML. We currently
129
+        // cannot send \0 characters within the XML content anywhere.
130
+        $content = str_replace("\0", "", $content);
131
+        if ("x" . $content == "x") {
132
+            return;
133
+        }
134
+        $this->_outputStack();
135
+        $this->_content($content);
136
+    }
137
+
138
+    /**
139
+     * Puts content of a stream on the output stack AND closes it.
140
+     *
141
+     * @param resource $stream
142
+     * @param bool     $asBase64 if true, the data will be encoded as base64, default: false
143
+     * @param bool     $opaque   if true, output the opaque data, default: false
144
+     *
145
+     * @return
146
+     */
147
+    public function contentStream($stream, $asBase64 = false, $opaque = false) {
148
+        // Do not append filters to opaque data as it might contain null char
149
+        if (!$asBase64 && !$opaque) {
150
+            stream_filter_register('replacenullchar', 'ReplaceNullcharFilter');
151
+            $rnc_filter = stream_filter_append($stream, 'replacenullchar');
152
+        }
153
+
154
+        $this->_outputStack();
155
+        $this->_contentStream($stream, $asBase64, $opaque);
156
+
157
+        if (!$asBase64 && !$opaque) {
158
+            stream_filter_remove($rnc_filter);
159
+        }
160
+
161
+        fclose($stream);
162
+    }
163
+
164
+    /**
165
+     * Gets the value of multipart.
166
+     *
167
+     * @return bool
168
+     */
169
+    public function getMultipart() {
170
+        return $this->multipart;
171
+    }
172
+
173
+    /**
174
+     * Adds a bodypart.
175
+     *
176
+     * @param Stream $bp
177
+     */
178
+    public function addBodypartStream($bp) {
179
+        if (!is_resource($bp)) {
180
+            throw new WBXMLException("WBXMLEncoder->addBodypartStream(): trying to add a " . gettype($bp) . " instead of a stream");
181
+        }
182
+        if ($this->multipart) {
183
+            $this->bodyparts[] = $bp;
184
+        }
185
+    }
186
+
187
+    /**
188
+     * Gets the number of bodyparts.
189
+     *
190
+     * @return int
191
+     */
192
+    public function getBodypartsCount() {
193
+        return count($this->bodyparts);
194
+    }
195
+
196
+    /*----------------------------------------------------------------------------------------------------------
197 197
 	 * Private WBXMLEncoder stuff
198 198
 	 */
199 199
 
200
-	/**
201
-	 * Output any tags on the stack that haven't been output yet.
202
-	 *
203
-	 * @return
204
-	 */
205
-	private function _outputStack() {
206
-		for ($i = 0; $i < count($this->_stack); ++$i) {
207
-			if (!$this->_stack[$i]['sent']) {
208
-				$this->_startTag($this->_stack[$i]['tag'], $this->_stack[$i]['nocontent']);
209
-				$this->_stack[$i]['sent'] = true;
210
-			}
211
-		}
212
-	}
213
-
214
-	/**
215
-	 * Outputs an actual start tag.
216
-	 *
217
-	 * @param mixed $tag
218
-	 * @param mixed $nocontent
219
-	 *
220
-	 * @return
221
-	 */
222
-	private function _startTag($tag, $nocontent = false) {
223
-		if ($this->log) {
224
-			$this->logStartTag($tag, $nocontent);
225
-		}
226
-
227
-		$mapping = $this->getMapping($tag);
228
-
229
-		if (!$mapping) {
230
-			return false;
231
-		}
232
-
233
-		if ($this->_tagcp != $mapping["cp"]) {
234
-			$this->outSwitchPage($mapping["cp"]);
235
-			$this->_tagcp = $mapping["cp"];
236
-		}
237
-
238
-		$code = $mapping["code"];
239
-
240
-		if (!isset($nocontent) || !$nocontent) {
241
-			$code |= 0x40;
242
-		}
243
-
244
-		$this->outByte($code);
245
-	}
246
-
247
-	/**
248
-	 * Outputs actual data.
249
-	 *
250
-	 * @param string $content
251
-	 *
252
-	 * @return
253
-	 */
254
-	private function _content($content) {
255
-		if ($this->log) {
256
-			$this->logContent($content);
257
-		}
258
-		$this->outByte(self::WBXML_STR_I);
259
-		$this->outTermStr($content);
260
-	}
261
-
262
-	/**
263
-	 * Outputs actual data coming from a stream, optionally encoded as base64.
264
-	 *
265
-	 * @param resource $stream
266
-	 * @param bool     $asBase64
267
-	 * @param mixed    $opaque
268
-	 *
269
-	 * @return
270
-	 */
271
-	private function _contentStream($stream, $asBase64, $opaque) {
272
-		$stat = fstat($stream);
273
-		// write full stream, including the finalizing terminator to the output stream (stuff outTermStr() would do)
274
-		if ($opaque) {
275
-			$this->outByte(self::WBXML_OPAQUE);
276
-			$this->outMBUInt($stat['size']);
277
-		}
278
-		else {
279
-			$this->outByte(self::WBXML_STR_I);
280
-		}
281
-
282
-		if ($asBase64) {
283
-			$out_filter = stream_filter_append($this->_out, 'convert.base64-encode');
284
-		}
285
-		$written = stream_copy_to_stream($stream, $this->_out);
286
-		if ($asBase64) {
287
-			stream_filter_remove($out_filter);
288
-		}
289
-		if (!$opaque) {
290
-			fwrite($this->_out, chr(0));
291
-		}
292
-
293
-		if ($this->log) {
294
-			// data is out, do some logging
295
-			$this->logContent(sprintf("<<< written %d of %d bytes of %s data >>>", $written, $stat['size'], $asBase64 ? "base64 encoded" : "plain"));
296
-		}
297
-	}
298
-
299
-	/**
300
-	 * Outputs an actual end tag.
301
-	 *
302
-	 * @return
303
-	 */
304
-	private function _endTag() {
305
-		if ($this->log) {
306
-			$this->logEndTag();
307
-		}
308
-		$this->outByte(self::WBXML_END);
309
-	}
310
-
311
-	/**
312
-	 * Outputs a byte.
313
-	 *
314
-	 * @param $byte
315
-	 *
316
-	 * @return
317
-	 */
318
-	private function outByte($byte) {
319
-		fwrite($this->_out, chr($byte));
320
-	}
321
-
322
-	/**
323
-	 * Output the multibyte integers to the stream.
324
-	 *
325
-	 * A multi-byte integer consists of a series of octets,
326
-	 * where the most significant bit is the continuation flag
327
-	 * and the remaining seven bits are a scalar value.
328
-	 * The octets are arranged in a big-endian order,
329
-	 * eg, the most significant seven bits are transmitted first.
330
-	 *
331
-	 * @see https://www.w3.org/1999/06/NOTE-wbxml-19990624/#_Toc443384895
332
-	 *
333
-	 * @param int $uint
334
-	 */
335
-	private function outMBUInt($uint) {
336
-		if ($uint == 0x0) {
337
-			return $this->outByte($uint);
338
-		}
339
-
340
-		$out = '';
341
-
342
-		for ($i = 0; $uint != 0; ++$i) {
343
-			$byte = $uint & 0x7F;
344
-			$uint = $uint >> 7;
345
-			if ($i == 0) {
346
-				$out = chr($byte) . $out;
347
-			}
348
-			else {
349
-				$out = chr($byte | 0x80) . $out;
350
-			}
351
-		}
352
-		fwrite($this->_out, $out);
353
-	}
354
-
355
-	/**
356
-	 * Outputs content with string terminator.
357
-	 *
358
-	 * @param $content
359
-	 *
360
-	 * @return
361
-	 */
362
-	private function outTermStr($content) {
363
-		fwrite($this->_out, $content);
364
-		fwrite($this->_out, chr(0));
365
-	}
366
-
367
-	/**
368
-	 * Switches the codepage.
369
-	 *
370
-	 * @param $page
371
-	 *
372
-	 * @return
373
-	 */
374
-	private function outSwitchPage($page) {
375
-		$this->outByte(self::WBXML_SWITCH_PAGE);
376
-		$this->outByte($page);
377
-	}
378
-
379
-	/**
380
-	 * Get the mapping for a tag.
381
-	 *
382
-	 * @param $tag
383
-	 *
384
-	 * @return array
385
-	 */
386
-	private function getMapping($tag) {
387
-		$mapping = [];
388
-
389
-		$split = $this->splitTag($tag);
390
-
391
-		if (isset($split["ns"])) {
392
-			$cp = $this->_dtd["namespaces"][$split["ns"]];
393
-		}
394
-		else {
395
-			$cp = 0;
396
-		}
397
-
398
-		$code = $this->_dtd["codes"][$cp][$split["tag"]];
399
-
400
-		$mapping["cp"] = $cp;
401
-		$mapping["code"] = $code;
402
-
403
-		return $mapping;
404
-	}
405
-
406
-	/**
407
-	 * Split a tag from a the fulltag (namespace + tag).
408
-	 *
409
-	 * @param $fulltag
410
-	 *
411
-	 * @return array keys: 'ns' (namespace), 'tag' (tag)
412
-	 */
413
-	private function splitTag($fulltag) {
414
-		$ns = false;
415
-		$pos = strpos($fulltag, chr(58)); // chr(58) == ':'
416
-
417
-		if ($pos) {
418
-			$ns = substr($fulltag, 0, $pos);
419
-			$tag = substr($fulltag, $pos + 1);
420
-		}
421
-		else {
422
-			$tag = $fulltag;
423
-		}
424
-
425
-		$ret = [];
426
-		if ($ns) {
427
-			$ret["ns"] = $ns;
428
-		}
429
-		$ret["tag"] = $tag;
430
-
431
-		return $ret;
432
-	}
433
-
434
-	/**
435
-	 * Logs a StartTag to SLog.
436
-	 *
437
-	 * @param $tag
438
-	 * @param $nocontent
439
-	 *
440
-	 * @return
441
-	 */
442
-	private function logStartTag($tag, $nocontent) {
443
-		$spaces = str_repeat(" ", count($this->logStack));
444
-		if ($nocontent) {
445
-			SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . " <{$tag}/>");
446
-		}
447
-		else {
448
-			array_push($this->logStack, $tag);
449
-			SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . " <{$tag}>");
450
-		}
451
-	}
452
-
453
-	/**
454
-	 * Logs a EndTag to SLog.
455
-	 *
456
-	 * @return
457
-	 */
458
-	private function logEndTag() {
459
-		$spaces = str_repeat(" ", count($this->logStack));
460
-		$tag = array_pop($this->logStack);
461
-		SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . "</{$tag}>");
462
-	}
463
-
464
-	/**
465
-	 * Logs content to SLog.
466
-	 *
467
-	 * @param string $content
468
-	 *
469
-	 * @return
470
-	 */
471
-	private function logContent($content) {
472
-		$spaces = str_repeat(" ", count($this->logStack));
473
-		SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . $content);
474
-	}
475
-
476
-	/**
477
-	 * Processes the multipart response.
478
-	 */
479
-	private function processMultipart() {
480
-		SLog::Write(LOGLEVEL_DEBUG, sprintf("WBXMLEncoder->processMultipart() with %d parts to be processed", $this->getBodypartsCount()));
481
-		$len = ob_get_length();
482
-		$buffer = ob_get_clean();
483
-		$nrBodyparts = $this->getBodypartsCount();
484
-		$blockstart = (($nrBodyparts + 1) * 2) * 4 + 4;
485
-
486
-		fwrite($this->_out, pack("iii", ($nrBodyparts + 1), $blockstart, $len));
487
-
488
-		foreach ($this->bodyparts as $i => $bp) {
489
-			$blockstart = $blockstart + $len;
490
-			$len = fstat($bp);
491
-			$len = (isset($len['size'])) ? $len['size'] : 0;
492
-			if ($len == 0) {
493
-				SLog::Write(LOGLEVEL_WARN, sprintf("WBXMLEncoder->processMultipart(): the length of the body part at position %d is 0", $i));
494
-			}
495
-			fwrite($this->_out, pack("ii", $blockstart, $len));
496
-		}
497
-
498
-		fwrite($this->_out, $buffer);
499
-
500
-		foreach ($this->bodyparts as $bp) {
501
-			stream_copy_to_stream($bp, $this->_out);
502
-			fclose($bp);
503
-		}
504
-	}
505
-
506
-	/**
507
-	 * Writes the sent WBXML data to the log if it is not bigger than 512K.
508
-	 */
509
-	private function writeLog() {
510
-		if (ob_get_length() === false) {
511
-			$data = "output buffer disabled";
512
-		}
513
-		elseif (ob_get_length() < 524288) {
514
-			$data = base64_encode(ob_get_contents());
515
-		}
516
-		else {
517
-			$data = "more than 512K of data";
518
-		}
519
-		SLog::Write(LOGLEVEL_WBXML, "WBXML-OUT: " . $data, false);
520
-	}
200
+    /**
201
+     * Output any tags on the stack that haven't been output yet.
202
+     *
203
+     * @return
204
+     */
205
+    private function _outputStack() {
206
+        for ($i = 0; $i < count($this->_stack); ++$i) {
207
+            if (!$this->_stack[$i]['sent']) {
208
+                $this->_startTag($this->_stack[$i]['tag'], $this->_stack[$i]['nocontent']);
209
+                $this->_stack[$i]['sent'] = true;
210
+            }
211
+        }
212
+    }
213
+
214
+    /**
215
+     * Outputs an actual start tag.
216
+     *
217
+     * @param mixed $tag
218
+     * @param mixed $nocontent
219
+     *
220
+     * @return
221
+     */
222
+    private function _startTag($tag, $nocontent = false) {
223
+        if ($this->log) {
224
+            $this->logStartTag($tag, $nocontent);
225
+        }
226
+
227
+        $mapping = $this->getMapping($tag);
228
+
229
+        if (!$mapping) {
230
+            return false;
231
+        }
232
+
233
+        if ($this->_tagcp != $mapping["cp"]) {
234
+            $this->outSwitchPage($mapping["cp"]);
235
+            $this->_tagcp = $mapping["cp"];
236
+        }
237
+
238
+        $code = $mapping["code"];
239
+
240
+        if (!isset($nocontent) || !$nocontent) {
241
+            $code |= 0x40;
242
+        }
243
+
244
+        $this->outByte($code);
245
+    }
246
+
247
+    /**
248
+     * Outputs actual data.
249
+     *
250
+     * @param string $content
251
+     *
252
+     * @return
253
+     */
254
+    private function _content($content) {
255
+        if ($this->log) {
256
+            $this->logContent($content);
257
+        }
258
+        $this->outByte(self::WBXML_STR_I);
259
+        $this->outTermStr($content);
260
+    }
261
+
262
+    /**
263
+     * Outputs actual data coming from a stream, optionally encoded as base64.
264
+     *
265
+     * @param resource $stream
266
+     * @param bool     $asBase64
267
+     * @param mixed    $opaque
268
+     *
269
+     * @return
270
+     */
271
+    private function _contentStream($stream, $asBase64, $opaque) {
272
+        $stat = fstat($stream);
273
+        // write full stream, including the finalizing terminator to the output stream (stuff outTermStr() would do)
274
+        if ($opaque) {
275
+            $this->outByte(self::WBXML_OPAQUE);
276
+            $this->outMBUInt($stat['size']);
277
+        }
278
+        else {
279
+            $this->outByte(self::WBXML_STR_I);
280
+        }
281
+
282
+        if ($asBase64) {
283
+            $out_filter = stream_filter_append($this->_out, 'convert.base64-encode');
284
+        }
285
+        $written = stream_copy_to_stream($stream, $this->_out);
286
+        if ($asBase64) {
287
+            stream_filter_remove($out_filter);
288
+        }
289
+        if (!$opaque) {
290
+            fwrite($this->_out, chr(0));
291
+        }
292
+
293
+        if ($this->log) {
294
+            // data is out, do some logging
295
+            $this->logContent(sprintf("<<< written %d of %d bytes of %s data >>>", $written, $stat['size'], $asBase64 ? "base64 encoded" : "plain"));
296
+        }
297
+    }
298
+
299
+    /**
300
+     * Outputs an actual end tag.
301
+     *
302
+     * @return
303
+     */
304
+    private function _endTag() {
305
+        if ($this->log) {
306
+            $this->logEndTag();
307
+        }
308
+        $this->outByte(self::WBXML_END);
309
+    }
310
+
311
+    /**
312
+     * Outputs a byte.
313
+     *
314
+     * @param $byte
315
+     *
316
+     * @return
317
+     */
318
+    private function outByte($byte) {
319
+        fwrite($this->_out, chr($byte));
320
+    }
321
+
322
+    /**
323
+     * Output the multibyte integers to the stream.
324
+     *
325
+     * A multi-byte integer consists of a series of octets,
326
+     * where the most significant bit is the continuation flag
327
+     * and the remaining seven bits are a scalar value.
328
+     * The octets are arranged in a big-endian order,
329
+     * eg, the most significant seven bits are transmitted first.
330
+     *
331
+     * @see https://www.w3.org/1999/06/NOTE-wbxml-19990624/#_Toc443384895
332
+     *
333
+     * @param int $uint
334
+     */
335
+    private function outMBUInt($uint) {
336
+        if ($uint == 0x0) {
337
+            return $this->outByte($uint);
338
+        }
339
+
340
+        $out = '';
341
+
342
+        for ($i = 0; $uint != 0; ++$i) {
343
+            $byte = $uint & 0x7F;
344
+            $uint = $uint >> 7;
345
+            if ($i == 0) {
346
+                $out = chr($byte) . $out;
347
+            }
348
+            else {
349
+                $out = chr($byte | 0x80) . $out;
350
+            }
351
+        }
352
+        fwrite($this->_out, $out);
353
+    }
354
+
355
+    /**
356
+     * Outputs content with string terminator.
357
+     *
358
+     * @param $content
359
+     *
360
+     * @return
361
+     */
362
+    private function outTermStr($content) {
363
+        fwrite($this->_out, $content);
364
+        fwrite($this->_out, chr(0));
365
+    }
366
+
367
+    /**
368
+     * Switches the codepage.
369
+     *
370
+     * @param $page
371
+     *
372
+     * @return
373
+     */
374
+    private function outSwitchPage($page) {
375
+        $this->outByte(self::WBXML_SWITCH_PAGE);
376
+        $this->outByte($page);
377
+    }
378
+
379
+    /**
380
+     * Get the mapping for a tag.
381
+     *
382
+     * @param $tag
383
+     *
384
+     * @return array
385
+     */
386
+    private function getMapping($tag) {
387
+        $mapping = [];
388
+
389
+        $split = $this->splitTag($tag);
390
+
391
+        if (isset($split["ns"])) {
392
+            $cp = $this->_dtd["namespaces"][$split["ns"]];
393
+        }
394
+        else {
395
+            $cp = 0;
396
+        }
397
+
398
+        $code = $this->_dtd["codes"][$cp][$split["tag"]];
399
+
400
+        $mapping["cp"] = $cp;
401
+        $mapping["code"] = $code;
402
+
403
+        return $mapping;
404
+    }
405
+
406
+    /**
407
+     * Split a tag from a the fulltag (namespace + tag).
408
+     *
409
+     * @param $fulltag
410
+     *
411
+     * @return array keys: 'ns' (namespace), 'tag' (tag)
412
+     */
413
+    private function splitTag($fulltag) {
414
+        $ns = false;
415
+        $pos = strpos($fulltag, chr(58)); // chr(58) == ':'
416
+
417
+        if ($pos) {
418
+            $ns = substr($fulltag, 0, $pos);
419
+            $tag = substr($fulltag, $pos + 1);
420
+        }
421
+        else {
422
+            $tag = $fulltag;
423
+        }
424
+
425
+        $ret = [];
426
+        if ($ns) {
427
+            $ret["ns"] = $ns;
428
+        }
429
+        $ret["tag"] = $tag;
430
+
431
+        return $ret;
432
+    }
433
+
434
+    /**
435
+     * Logs a StartTag to SLog.
436
+     *
437
+     * @param $tag
438
+     * @param $nocontent
439
+     *
440
+     * @return
441
+     */
442
+    private function logStartTag($tag, $nocontent) {
443
+        $spaces = str_repeat(" ", count($this->logStack));
444
+        if ($nocontent) {
445
+            SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . " <{$tag}/>");
446
+        }
447
+        else {
448
+            array_push($this->logStack, $tag);
449
+            SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . " <{$tag}>");
450
+        }
451
+    }
452
+
453
+    /**
454
+     * Logs a EndTag to SLog.
455
+     *
456
+     * @return
457
+     */
458
+    private function logEndTag() {
459
+        $spaces = str_repeat(" ", count($this->logStack));
460
+        $tag = array_pop($this->logStack);
461
+        SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . "</{$tag}>");
462
+    }
463
+
464
+    /**
465
+     * Logs content to SLog.
466
+     *
467
+     * @param string $content
468
+     *
469
+     * @return
470
+     */
471
+    private function logContent($content) {
472
+        $spaces = str_repeat(" ", count($this->logStack));
473
+        SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . $content);
474
+    }
475
+
476
+    /**
477
+     * Processes the multipart response.
478
+     */
479
+    private function processMultipart() {
480
+        SLog::Write(LOGLEVEL_DEBUG, sprintf("WBXMLEncoder->processMultipart() with %d parts to be processed", $this->getBodypartsCount()));
481
+        $len = ob_get_length();
482
+        $buffer = ob_get_clean();
483
+        $nrBodyparts = $this->getBodypartsCount();
484
+        $blockstart = (($nrBodyparts + 1) * 2) * 4 + 4;
485
+
486
+        fwrite($this->_out, pack("iii", ($nrBodyparts + 1), $blockstart, $len));
487
+
488
+        foreach ($this->bodyparts as $i => $bp) {
489
+            $blockstart = $blockstart + $len;
490
+            $len = fstat($bp);
491
+            $len = (isset($len['size'])) ? $len['size'] : 0;
492
+            if ($len == 0) {
493
+                SLog::Write(LOGLEVEL_WARN, sprintf("WBXMLEncoder->processMultipart(): the length of the body part at position %d is 0", $i));
494
+            }
495
+            fwrite($this->_out, pack("ii", $blockstart, $len));
496
+        }
497
+
498
+        fwrite($this->_out, $buffer);
499
+
500
+        foreach ($this->bodyparts as $bp) {
501
+            stream_copy_to_stream($bp, $this->_out);
502
+            fclose($bp);
503
+        }
504
+    }
505
+
506
+    /**
507
+     * Writes the sent WBXML data to the log if it is not bigger than 512K.
508
+     */
509
+    private function writeLog() {
510
+        if (ob_get_length() === false) {
511
+            $data = "output buffer disabled";
512
+        }
513
+        elseif (ob_get_length() < 524288) {
514
+            $data = base64_encode(ob_get_contents());
515
+        }
516
+        else {
517
+            $data = "more than 512K of data";
518
+        }
519
+        SLog::Write(LOGLEVEL_WBXML, "WBXML-OUT: " . $data, false);
520
+    }
521 521
 }
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -128,7 +128,7 @@  discard block
 block discarded – undo
128 128
 		// We need to filter out any \0 chars because it's the string terminator in WBXML. We currently
129 129
 		// cannot send \0 characters within the XML content anywhere.
130 130
 		$content = str_replace("\0", "", $content);
131
-		if ("x" . $content == "x") {
131
+		if ("x".$content == "x") {
132 132
 			return;
133 133
 		}
134 134
 		$this->_outputStack();
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 	 */
178 178
 	public function addBodypartStream($bp) {
179 179
 		if (!is_resource($bp)) {
180
-			throw new WBXMLException("WBXMLEncoder->addBodypartStream(): trying to add a " . gettype($bp) . " instead of a stream");
180
+			throw new WBXMLException("WBXMLEncoder->addBodypartStream(): trying to add a ".gettype($bp)." instead of a stream");
181 181
 		}
182 182
 		if ($this->multipart) {
183 183
 			$this->bodyparts[] = $bp;
@@ -343,10 +343,10 @@  discard block
 block discarded – undo
343 343
 			$byte = $uint & 0x7F;
344 344
 			$uint = $uint >> 7;
345 345
 			if ($i == 0) {
346
-				$out = chr($byte) . $out;
346
+				$out = chr($byte).$out;
347 347
 			}
348 348
 			else {
349
-				$out = chr($byte | 0x80) . $out;
349
+				$out = chr($byte|0x80).$out;
350 350
 			}
351 351
 		}
352 352
 		fwrite($this->_out, $out);
@@ -442,11 +442,11 @@  discard block
 block discarded – undo
442 442
 	private function logStartTag($tag, $nocontent) {
443 443
 		$spaces = str_repeat(" ", count($this->logStack));
444 444
 		if ($nocontent) {
445
-			SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . " <{$tag}/>");
445
+			SLog::Write(LOGLEVEL_WBXML, "O ".$spaces." <{$tag}/>");
446 446
 		}
447 447
 		else {
448 448
 			array_push($this->logStack, $tag);
449
-			SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . " <{$tag}>");
449
+			SLog::Write(LOGLEVEL_WBXML, "O ".$spaces." <{$tag}>");
450 450
 		}
451 451
 	}
452 452
 
@@ -458,7 +458,7 @@  discard block
 block discarded – undo
458 458
 	private function logEndTag() {
459 459
 		$spaces = str_repeat(" ", count($this->logStack));
460 460
 		$tag = array_pop($this->logStack);
461
-		SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . "</{$tag}>");
461
+		SLog::Write(LOGLEVEL_WBXML, "O ".$spaces."</{$tag}>");
462 462
 	}
463 463
 
464 464
 	/**
@@ -470,7 +470,7 @@  discard block
 block discarded – undo
470 470
 	 */
471 471
 	private function logContent($content) {
472 472
 		$spaces = str_repeat(" ", count($this->logStack));
473
-		SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . $content);
473
+		SLog::Write(LOGLEVEL_WBXML, "O ".$spaces.$content);
474 474
 	}
475 475
 
476 476
 	/**
@@ -516,6 +516,6 @@  discard block
 block discarded – undo
516 516
 		else {
517 517
 			$data = "more than 512K of data";
518 518
 		}
519
-		SLog::Write(LOGLEVEL_WBXML, "WBXML-OUT: " . $data, false);
519
+		SLog::Write(LOGLEVEL_WBXML, "WBXML-OUT: ".$data, false);
520 520
 	}
521 521
 }
Please login to merge, or discard this patch.
Braces   +9 added lines, -18 removed lines patch added patch discarded remove patch
@@ -53,8 +53,7 @@  discard block
 block discarded – undo
53 53
 		if ($this->multipart) {
54 54
 			header("Content-Type: application/vnd.ms-sync.multipart");
55 55
 			SLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->startWBXML() type: vnd.ms-sync.multipart");
56
-		}
57
-		else {
56
+		} else {
58 57
 			header("Content-Type: application/vnd.ms-sync.wbxml");
59 58
 			SLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->startWBXML() type: vnd.ms-sync.wbxml");
60 59
 		}
@@ -86,8 +85,7 @@  discard block
 block discarded – undo
86 85
 
87 86
 		// If 'nocontent' is specified, then apparently the user wants to force
88 87
 			// output of an empty tag, and we therefore output the stack here
89
-		}
90
-		else {
88
+		} else {
91 89
 			$this->_outputStack();
92 90
 			$this->_startTag($tag, $nocontent);
93 91
 		}
@@ -274,8 +272,7 @@  discard block
 block discarded – undo
274 272
 		if ($opaque) {
275 273
 			$this->outByte(self::WBXML_OPAQUE);
276 274
 			$this->outMBUInt($stat['size']);
277
-		}
278
-		else {
275
+		} else {
279 276
 			$this->outByte(self::WBXML_STR_I);
280 277
 		}
281 278
 
@@ -344,8 +341,7 @@  discard block
 block discarded – undo
344 341
 			$uint = $uint >> 7;
345 342
 			if ($i == 0) {
346 343
 				$out = chr($byte) . $out;
347
-			}
348
-			else {
344
+			} else {
349 345
 				$out = chr($byte | 0x80) . $out;
350 346
 			}
351 347
 		}
@@ -390,8 +386,7 @@  discard block
 block discarded – undo
390 386
 
391 387
 		if (isset($split["ns"])) {
392 388
 			$cp = $this->_dtd["namespaces"][$split["ns"]];
393
-		}
394
-		else {
389
+		} else {
395 390
 			$cp = 0;
396 391
 		}
397 392
 
@@ -417,8 +412,7 @@  discard block
 block discarded – undo
417 412
 		if ($pos) {
418 413
 			$ns = substr($fulltag, 0, $pos);
419 414
 			$tag = substr($fulltag, $pos + 1);
420
-		}
421
-		else {
415
+		} else {
422 416
 			$tag = $fulltag;
423 417
 		}
424 418
 
@@ -443,8 +437,7 @@  discard block
 block discarded – undo
443 437
 		$spaces = str_repeat(" ", count($this->logStack));
444 438
 		if ($nocontent) {
445 439
 			SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . " <{$tag}/>");
446
-		}
447
-		else {
440
+		} else {
448 441
 			array_push($this->logStack, $tag);
449 442
 			SLog::Write(LOGLEVEL_WBXML, "O " . $spaces . " <{$tag}>");
450 443
 		}
@@ -509,11 +502,9 @@  discard block
 block discarded – undo
509 502
 	private function writeLog() {
510 503
 		if (ob_get_length() === false) {
511 504
 			$data = "output buffer disabled";
512
-		}
513
-		elseif (ob_get_length() < 524288) {
505
+		} elseif (ob_get_length() < 524288) {
514 506
 			$data = base64_encode(ob_get_contents());
515
-		}
516
-		else {
507
+		} else {
517 508
 			$data = "more than 512K of data";
518 509
 		}
519 510
 		SLog::Write(LOGLEVEL_WBXML, "WBXML-OUT: " . $data, false);
Please login to merge, or discard this patch.
lib/utils/compat.php 3 patches
Indentation   +200 added lines, -200 removed lines patch added patch discarded remove patch
@@ -8,227 +8,227 @@
 block discarded – undo
8 8
  */
9 9
 
10 10
 if (!function_exists('apache_request_headers')) {
11
-	/**
12
-	 * When using other webservers or using php as cgi in apache
13
-	 * the function apache_request_headers() is not available.
14
-	 * This function parses the environment variables to extract
15
-	 * the necessary headers for grommunio-sync.
16
-	 */
17
-	function apache_request_headers() {
18
-		$headers = [];
19
-		foreach ($_SERVER as $key => $value) {
20
-			if (substr($key, 0, 5) == 'HTTP_') {
21
-				$headers[strtr(substr($key, 5), '_', '-')] = $value;
22
-			}
23
-		}
24
-
25
-		return $headers;
26
-	}
11
+    /**
12
+     * When using other webservers or using php as cgi in apache
13
+     * the function apache_request_headers() is not available.
14
+     * This function parses the environment variables to extract
15
+     * the necessary headers for grommunio-sync.
16
+     */
17
+    function apache_request_headers() {
18
+        $headers = [];
19
+        foreach ($_SERVER as $key => $value) {
20
+            if (substr($key, 0, 5) == 'HTTP_') {
21
+                $headers[strtr(substr($key, 5), '_', '-')] = $value;
22
+            }
23
+        }
24
+
25
+        return $headers;
26
+    }
27 27
 }
28 28
 
29 29
 if (!function_exists('hex2bin')) {
30
-	/**
31
-	 * Complementary function to bin2hex() which converts a hex entryid to a binary entryid.
32
-	 * Since PHP 5.4 an internal hex2bin() implementation is available.
33
-	 *
34
-	 * @param string $data the hexadecimal string
35
-	 *
36
-	 * @returns string
37
-	 */
38
-	function hex2bin($data) {
39
-		return pack("H*", $data);
40
-	}
30
+    /**
31
+     * Complementary function to bin2hex() which converts a hex entryid to a binary entryid.
32
+     * Since PHP 5.4 an internal hex2bin() implementation is available.
33
+     *
34
+     * @param string $data the hexadecimal string
35
+     *
36
+     * @returns string
37
+     */
38
+    function hex2bin($data) {
39
+        return pack("H*", $data);
40
+    }
41 41
 }
42 42
 
43 43
 if (!function_exists('http_response_code')) {
44
-	/**
45
-	 * http_response_code does not exists in PHP < 5.4
46
-	 * http://php.net/manual/en/function.http-response-code.php.
47
-	 *
48
-	 * @param null|mixed $code
49
-	 */
50
-	function http_response_code($code = null) {
51
-		if ($code !== null) {
52
-			switch ($code) {
53
-				case 100:
54
-					$text = 'Continue';
55
-					break;
56
-
57
-				case 101:
58
-					$text = 'Switching Protocols';
59
-					break;
60
-
61
-				case 200:
62
-					$text = 'OK';
63
-					break;
64
-
65
-				case 201:
66
-					$text = 'Created';
67
-					break;
68
-
69
-				case 202:
70
-					$text = 'Accepted';
71
-					break;
72
-
73
-				case 203:
74
-					$text = 'Non-Authoritative Information';
75
-					break;
76
-
77
-				case 204:
78
-					$text = 'No Content';
79
-					break;
80
-
81
-				case 205:
82
-					$text = 'Reset Content';
83
-					break;
84
-
85
-				case 206:
86
-					$text = 'Partial Content';
87
-					break;
88
-
89
-				case 300:
90
-					$text = 'Multiple Choices';
91
-					break;
92
-
93
-				case 301:
94
-					$text = 'Moved Permanently';
95
-					break;
96
-
97
-				case 302:
98
-					$text = 'Moved Temporarily';
99
-					break;
100
-
101
-				case 303:
102
-					$text = 'See Other';
103
-					break;
104
-
105
-				case 304:
106
-					$text = 'Not Modified';
107
-					break;
108
-
109
-				case 305:
110
-					$text = 'Use Proxy';
111
-					break;
112
-
113
-				case 400:
114
-					$text = 'Bad Request';
115
-					break;
116
-
117
-				case 401:
118
-					$text = 'Unauthorized';
119
-					break;
120
-
121
-				case 402:
122
-					$text = 'Payment Required';
123
-					break;
124
-
125
-				case 403:
126
-					$text = 'Forbidden';
127
-					break;
128
-
129
-				case 404:
130
-					$text = 'Not Found';
131
-					break;
132
-
133
-				case 405:
134
-					$text = 'Method Not Allowed';
135
-					break;
136
-
137
-				case 406:
138
-					$text = 'Not Acceptable';
139
-					break;
140
-
141
-				case 407:
142
-					$text = 'Proxy Authentication Required';
143
-					break;
144
-
145
-				case 408:
146
-					$text = 'Request Time-out';
147
-					break;
148
-
149
-				case 409:
150
-					$text = 'Conflict';
151
-					break;
152
-
153
-				case 410:
154
-					$text = 'Gone';
155
-					break;
44
+    /**
45
+     * http_response_code does not exists in PHP < 5.4
46
+     * http://php.net/manual/en/function.http-response-code.php.
47
+     *
48
+     * @param null|mixed $code
49
+     */
50
+    function http_response_code($code = null) {
51
+        if ($code !== null) {
52
+            switch ($code) {
53
+                case 100:
54
+                    $text = 'Continue';
55
+                    break;
56
+
57
+                case 101:
58
+                    $text = 'Switching Protocols';
59
+                    break;
60
+
61
+                case 200:
62
+                    $text = 'OK';
63
+                    break;
64
+
65
+                case 201:
66
+                    $text = 'Created';
67
+                    break;
68
+
69
+                case 202:
70
+                    $text = 'Accepted';
71
+                    break;
72
+
73
+                case 203:
74
+                    $text = 'Non-Authoritative Information';
75
+                    break;
76
+
77
+                case 204:
78
+                    $text = 'No Content';
79
+                    break;
80
+
81
+                case 205:
82
+                    $text = 'Reset Content';
83
+                    break;
84
+
85
+                case 206:
86
+                    $text = 'Partial Content';
87
+                    break;
88
+
89
+                case 300:
90
+                    $text = 'Multiple Choices';
91
+                    break;
92
+
93
+                case 301:
94
+                    $text = 'Moved Permanently';
95
+                    break;
96
+
97
+                case 302:
98
+                    $text = 'Moved Temporarily';
99
+                    break;
100
+
101
+                case 303:
102
+                    $text = 'See Other';
103
+                    break;
104
+
105
+                case 304:
106
+                    $text = 'Not Modified';
107
+                    break;
108
+
109
+                case 305:
110
+                    $text = 'Use Proxy';
111
+                    break;
112
+
113
+                case 400:
114
+                    $text = 'Bad Request';
115
+                    break;
116
+
117
+                case 401:
118
+                    $text = 'Unauthorized';
119
+                    break;
120
+
121
+                case 402:
122
+                    $text = 'Payment Required';
123
+                    break;
124
+
125
+                case 403:
126
+                    $text = 'Forbidden';
127
+                    break;
128
+
129
+                case 404:
130
+                    $text = 'Not Found';
131
+                    break;
132
+
133
+                case 405:
134
+                    $text = 'Method Not Allowed';
135
+                    break;
136
+
137
+                case 406:
138
+                    $text = 'Not Acceptable';
139
+                    break;
140
+
141
+                case 407:
142
+                    $text = 'Proxy Authentication Required';
143
+                    break;
144
+
145
+                case 408:
146
+                    $text = 'Request Time-out';
147
+                    break;
148
+
149
+                case 409:
150
+                    $text = 'Conflict';
151
+                    break;
152
+
153
+                case 410:
154
+                    $text = 'Gone';
155
+                    break;
156 156
 
157
-				case 411:
158
-					$text = 'Length Required';
159
-					break;
157
+                case 411:
158
+                    $text = 'Length Required';
159
+                    break;
160 160
 
161
-				case 412:
162
-					$text = 'Precondition Failed';
163
-					break;
161
+                case 412:
162
+                    $text = 'Precondition Failed';
163
+                    break;
164 164
 
165
-				case 413:
166
-					$text = 'Request Entity Too Large';
167
-					break;
165
+                case 413:
166
+                    $text = 'Request Entity Too Large';
167
+                    break;
168 168
 
169
-				case 414:
170
-					$text = 'Request-URI Too Large';
171
-					break;
169
+                case 414:
170
+                    $text = 'Request-URI Too Large';
171
+                    break;
172 172
 
173
-				case 415:
174
-					$text = 'Unsupported Media Type';
175
-					break;
173
+                case 415:
174
+                    $text = 'Unsupported Media Type';
175
+                    break;
176 176
 
177
-				case 500:
178
-					$text = 'Internal Server Error';
179
-					break;
177
+                case 500:
178
+                    $text = 'Internal Server Error';
179
+                    break;
180 180
 
181
-				case 501:
182
-					$text = 'Not Implemented';
183
-					break;
181
+                case 501:
182
+                    $text = 'Not Implemented';
183
+                    break;
184 184
 
185
-				case 502:
186
-					$text = 'Bad Gateway';
187
-					break;
185
+                case 502:
186
+                    $text = 'Bad Gateway';
187
+                    break;
188 188
 
189
-				case 503:
190
-					$text = 'Service Unavailable';
191
-					break;
189
+                case 503:
190
+                    $text = 'Service Unavailable';
191
+                    break;
192 192
 
193
-				case 504:
194
-					$text = 'Gateway Time-out';
195
-					break;
193
+                case 504:
194
+                    $text = 'Gateway Time-out';
195
+                    break;
196 196
 
197
-				case 505:
198
-					$text = 'HTTP Version not supported';
199
-					break;
197
+                case 505:
198
+                    $text = 'HTTP Version not supported';
199
+                    break;
200 200
 
201
-				default:
202
-					exit('Unknown http status code "' . htmlentities($code) . '"');
203
-					break;
204
-			}
205
-
206
-			$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
207
-			header($protocol . ' ' . $code . ' ' . $text);
201
+                default:
202
+                    exit('Unknown http status code "' . htmlentities($code) . '"');
203
+                    break;
204
+            }
205
+
206
+            $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
207
+            header($protocol . ' ' . $code . ' ' . $text);
208 208
 
209
-			$GLOBALS['http_response_code'] = $code;
210
-		}
211
-		else {
212
-			$code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
213
-		}
209
+            $GLOBALS['http_response_code'] = $code;
210
+        }
211
+        else {
212
+            $code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
213
+        }
214 214
 
215
-		return $code;
216
-	}
215
+        return $code;
216
+    }
217 217
 }
218 218
 
219 219
 if (!function_exists('memory_get_peak_usage')) {
220
-	/**
221
-	 * memory_get_peak_usage is not available prior to PHP 5.2.
222
-	 * This complementary function will return the value of memory_get_usage();.
223
-	 *
224
-	 * @see http://php.net/manual/en/function.memory-get-usage.php
225
-	 * @see http://php.net/manual/en/function.memory-get-peak-usage.php
226
-	 *
227
-	 * @param bool $real_usage
228
-	 */
229
-	function memory_get_peak_usage($real_usage = false) {
230
-		SLog::Write(LOGLEVEL_DEBUG, "memory_get_peak_usage() is not available on this system. The value of memory_get_usage() will be used.");
231
-
232
-		return memory_get_usage();
233
-	}
220
+    /**
221
+     * memory_get_peak_usage is not available prior to PHP 5.2.
222
+     * This complementary function will return the value of memory_get_usage();.
223
+     *
224
+     * @see http://php.net/manual/en/function.memory-get-usage.php
225
+     * @see http://php.net/manual/en/function.memory-get-peak-usage.php
226
+     *
227
+     * @param bool $real_usage
228
+     */
229
+    function memory_get_peak_usage($real_usage = false) {
230
+        SLog::Write(LOGLEVEL_DEBUG, "memory_get_peak_usage() is not available on this system. The value of memory_get_usage() will be used.");
231
+
232
+        return memory_get_usage();
233
+    }
234 234
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -199,12 +199,12 @@
 block discarded – undo
199 199
 					break;
200 200
 
201 201
 				default:
202
-					exit('Unknown http status code "' . htmlentities($code) . '"');
202
+					exit('Unknown http status code "'.htmlentities($code).'"');
203 203
 					break;
204 204
 			}
205 205
 
206 206
 			$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
207
-			header($protocol . ' ' . $code . ' ' . $text);
207
+			header($protocol.' '.$code.' '.$text);
208 208
 
209 209
 			$GLOBALS['http_response_code'] = $code;
210 210
 		}
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -207,8 +207,7 @@
 block discarded – undo
207 207
 			header($protocol . ' ' . $code . ' ' . $text);
208 208
 
209 209
 			$GLOBALS['http_response_code'] = $code;
210
-		}
211
-		else {
210
+		} else {
212 211
 			$code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
213 212
 		}
214 213
 
Please login to merge, or discard this patch.