. * * Consult LICENSE file for details ************************************************/ class DiffState implements IChanges { protected $syncstate; protected $backend; protected $flags; protected $contentparameters; protected $cutoffdate; /** * Initializes the state * * @param string $state * @param int $flags * * @access public * @return boolean status flag * @throws StatusException */ public function Config($state, $flags = 0) { if ($state == "") $state = array(); if (!is_array($state)) throw new StatusException("Invalid state", SYNC_FSSTATUS_CODEUNKNOWN); $this->syncstate = $state; $this->flags = $flags; return true; } /** * Configures additional parameters used for content synchronization * * @param ContentParameters $contentparameters * * @access public * @return boolean * @throws StatusException */ public function ConfigContentParameters($contentparameters) { $this->contentparameters = $contentparameters; $filtertype = $contentparameters->GetFilterType(); switch($contentparameters->GetContentClass()) { case "Email": case "Calendar": $this->cutoffdate = ($filtertype === false) ? 0 : Utils::GetCutOffDate($filtertype); break; case "Contacts": case "Tasks": default: $this->cutoffdate = 0; break; } } /** * Returns state * * @access public * @return string * @throws StatusException */ public function GetState() { if (!isset($this->syncstate) || !is_array($this->syncstate)) throw new StatusException("DiffState->GetState(): Error, state not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN); return $this->syncstate; } /**---------------------------------------------------------------------------------------------------------- * DiffState specific stuff */ /** * Comparing function used for sorting of the differential engine * * @param array $a * @param array $b * * @access public * @return boolean */ static public function RowCmp($a, $b) { return strcmp($b['id'], $a['id']); } /** * Differential mechanism * Compares the current syncstate to the sent $new * * @param array $new * * @access protected * @return array */ protected function getDiffTo($new) { $changes = array(); // Sort both arrays in the same way by ID usort($this->syncstate, array("DiffState", "RowCmp")); usort($new, array("DiffState", "RowCmp")); $inew = 0; $iold = 0; $cntstate = count($this->syncstate); $cntnew = count($new); // Get changes by comparing our list of messages with // our previous state while(true) { if($iold >= $cntstate || $inew >= $cntnew) break; $cmp = strcmp($this->syncstate[$iold]["id"], $new[$inew]["id"]); if ($cmp == 0) { // Both messages are still available, compare flags and mod if(isset($this->syncstate[$iold]["flags"]) && isset($new[$inew]["flags"]) && $this->syncstate[$iold]["flags"] != $new[$inew]["flags"]) { // Flags changed $change = array(); $change["type"] = "flags"; $change["id"] = $new[$inew]["id"]; $change["flags"] = $new[$inew]["flags"]; $changes[] = $change; } if(isset($this->syncstate[$iold]["mod"]) && isset($new[$inew]["mod"]) && $this->syncstate[$iold]["mod"] != $new[$inew]["mod"]) { $change = array(); $change["type"] = "change"; $change["id"] = $new[$inew]["id"]; $changes[] = $change; } $inew++; $iold++; } elseif ($cmp > 0) { // Message in state seems to have disappeared (delete) $change = array(); $change["type"] = "delete"; $change["id"] = $this->syncstate[$iold]["id"]; $changes[] = $change; $iold++; } else { // Message in new seems to be new (add) $change = array(); $change["type"] = "change"; $change["flags"] = SYNC_NEWMESSAGE; $change["id"] = $new[$inew]["id"]; $changes[] = $change; $inew++; } } while($iold < $cntstate) { // All data left in 'syncstate' have been deleted $change = array(); $change["type"] = "delete"; $change["id"] = $this->syncstate[$iold]["id"]; $changes[] = $change; $iold++; } while($inew < $cntnew) { // All data left in new have been added $change = array(); $change["type"] = "change"; $change["flags"] = SYNC_NEWMESSAGE; $change["id"] = $new[$inew]["id"]; $changes[] = $change; $inew++; } return $changes; } /** * Update the state to reflect changes * * @param string $type of change * @param array $change * * * @access protected * @return */ protected function updateState($type, $change) { // Change can be a change or an add $change_id = $change['id']; foreach ($this->syncstate as $i => &$state) { if($this->syncstate[$i]["id"] == $change["id"]) { if ($state['id'] == $change_id) { $this->syncstate[$i] = $change; switch ($type) { case 'change': $state = $change; return; case 'flags': $state['flags'] = $change['flags']; return; case 'delete': array_splice($this->syncstate, $i, 1); return; default: throw new Exception(sprintf("updateState: type '%s' is not supported", $type)); } } } } if($type == "change") { $this->syncstate[] = $change; } else { $flags = empty($change['flags'])?"":$change['flags']; $mod = empty($change['mod'])?"":$change['mod']; ZLog::Write(LOGLEVEL_WARN, sprintf("updateState: no state modification!!! %s|%s|%s|%s", $type, $change_id, $flags, $mod)); } } /** * Returns TRUE if the given ID conflicts with the given operation. This is only true in the following situations: * - Changed here and changed there * - Changed here and deleted there * - Deleted here and changed there * Any other combination of operations can be done (e.g. change flags & move or move & delete) * * @param string $type of change * @param string $folderid * @param string $id * * @access protected * @return */ protected function isConflict($type, $folderid, $id) { $stat = $this->backend->StatMessage($folderid, $id); if(!$stat) { // Message is gone if($type == "change") return true; // deleted here, but changed there else return false; // all other remote changes still result in a delete (no conflict) } foreach($this->syncstate as $state) { if($state["id"] == $id) { $oldstat = $state; break; } } if(!isset($oldstat)) { // New message, can never conflict return false; } if($stat["mod"] != $oldstat["mod"]) { // Changed here if($type == "delete" || $type == "change") return true; // changed here, but deleted there -> conflict, or changed here and changed there -> conflict else return false; // changed here, and other remote changes (move or flags) } } }