1<?php 2/*********************************************** 3* File : exportchangesdiff.php 4* Project : Z-Push 5* Descr : IExportChanges implementation using 6* the differential engine 7* 8* Created : 02.01.2012 9* 10* Copyright 2007 - 2016 Zarafa Deutschland GmbH 11* 12* This program is free software: you can redistribute it and/or modify 13* it under the terms of the GNU Affero General Public License, version 3, 14* as published by the Free Software Foundation. 15* 16* This program is distributed in the hope that it will be useful, 17* but WITHOUT ANY WARRANTY; without even the implied warranty of 18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19* GNU Affero General Public License for more details. 20* 21* You should have received a copy of the GNU Affero General Public License 22* along with this program. If not, see <http://www.gnu.org/licenses/>. 23* 24* Consult LICENSE file for details 25************************************************/ 26 27class ExportChangesDiff extends DiffState implements IExportChanges{ 28 private $importer; 29 private $folderid; 30 private $changes; 31 private $step; 32 33 /** 34 * Constructor 35 * 36 * @param object $backend 37 * @param string $folderid 38 * 39 * @access public 40 * @throws StatusException 41 */ 42 public function __construct($backend, $folderid) { 43 $this->backend = $backend; 44 $this->folderid = $folderid; 45 } 46 47 /** 48 * Sets the importer the exporter will sent it's changes to 49 * and initializes the Exporter 50 * 51 * @param object &$importer Implementation of IImportChanges 52 * 53 * @access public 54 * @return boolean 55 * @throws StatusException 56 */ 57 public function InitializeExporter(&$importer) { 58 $this->changes = array(); 59 $this->step = 0; 60 $this->importer = $importer; 61 62 if($this->folderid) { 63 // Get the changes since the last sync 64 if(!isset($this->syncstate) || !$this->syncstate) 65 $this->syncstate = array(); 66 67 ZLog::Write(LOGLEVEL_DEBUG,sprintf("ExportChangesDiff->InitializeExporter(): Initializing message diff engine. '%d' messages in state", count($this->syncstate))); 68 69 //do nothing if it is a dummy folder 70 if ($this->folderid != SYNC_FOLDER_TYPE_DUMMY) { 71 // Get our lists - syncstate (old) and msglist (new) 72 $msglist = $this->backend->GetMessageList($this->folderid, $this->cutoffdate); 73 // if the folder was deleted, no information is available anymore. A hierarchysync should be executed 74 if($msglist === false) 75 throw new StatusException("ExportChangesDiff->InitializeExporter(): Error, no message list available from the backend", SYNC_STATUS_FOLDERHIERARCHYCHANGED, null, LOGLEVEL_INFO); 76 77 $this->changes = $this->getDiffTo($msglist); 78 } 79 } 80 else { 81 ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesDiff->InitializeExporter(): Initializing folder diff engine"); 82 83 $folderlist = $this->backend->GetFolderList(); 84 if($folderlist === false) 85 throw new StatusException("ExportChangesDiff->InitializeExporter(): error, no folders available from the backend", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN); 86 87 if(!isset($this->syncstate) || !$this->syncstate) 88 $this->syncstate = array(); 89 90 $this->changes = $this->getDiffTo($folderlist); 91 } 92 93 ZLog::Write(LOGLEVEL_INFO, sprintf("ExportChangesDiff->InitializeExporter(): Found '%d' changes for '%s'", count($this->changes), ($this->folderid)?$this->folderid : 'hierarchy' )); 94 } 95 96 /** 97 * Returns the amount of changes to be exported 98 * 99 * @access public 100 * @return int 101 */ 102 public function GetChangeCount() { 103 return count($this->changes); 104 } 105 106 /** 107 * Synchronizes a change 108 * 109 * @access public 110 * @return array 111 */ 112 public function Synchronize() { 113 $progress = array(); 114 115 // Get one of our stored changes and send it to the importer, store the new state if 116 // it succeeds 117 if($this->folderid == false) { 118 if($this->step < count($this->changes)) { 119 $change = $this->changes[$this->step]; 120 121 switch($change["type"]) { 122 case "change": 123 $folder = $this->backend->GetFolder($change["id"]); 124 $stat = $this->backend->StatFolder($change["id"]); 125 126 if(!$folder) 127 return; 128 129 if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportFolderChange($folder)) 130 $this->updateState("change", $stat); 131 break; 132 case "delete": 133 if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportFolderDeletion(SyncFolder::GetObject($change["id"]))) 134 $this->updateState("delete", $change); 135 break; 136 } 137 138 $this->step++; 139 140 $progress = array(); 141 $progress["steps"] = count($this->changes); 142 $progress["progress"] = $this->step; 143 144 return $progress; 145 } else { 146 return false; 147 } 148 } 149 else { 150 if($this->step < count($this->changes)) { 151 $change = $this->changes[$this->step]; 152 153 switch($change["type"]) { 154 case "change": 155 // Note: because 'parseMessage' and 'statMessage' are two seperate 156 // calls, we have a chance that the message has changed between both 157 // calls. This may cause our algorithm to 'double see' changes. 158 159 $stat = $this->backend->StatMessage($this->folderid, $change["id"]); 160 $message = $this->backend->GetMessage($this->folderid, $change["id"], $this->contentparameters); 161 162 // copy the flag to the message 163 $message->flags = (isset($change["flags"])) ? $change["flags"] : 0; 164 165 if($stat && $message) { 166 if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportMessageChange($change["id"], $message) == true) 167 $this->updateState("change", $stat); 168 } 169 break; 170 case "delete": 171 if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportMessageDeletion($change["id"]) == true) 172 $this->updateState("delete", $change); 173 break; 174 case "flags": 175 if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportMessageReadFlag($change["id"], $change["flags"]) == true) 176 $this->updateState("flags", $change); 177 break; 178 case "move": 179 if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportMessageMove($change["id"], $change["parent"]) == true) 180 $this->updateState("move", $change); 181 break; 182 } 183 184 $this->step++; 185 186 $progress = array(); 187 $progress["steps"] = count($this->changes); 188 $progress["progress"] = $this->step; 189 190 return $progress; 191 } else { 192 return false; 193 } 194 } 195 } 196} 197