Back to index

d-push  2.0
foldersync.php
Go to the documentation of this file.
00001 <?php
00002 /***********************************************
00003 * File      :   foldersync.php
00004 * Project   :   Z-Push
00005 * Descr     :   Provides the FOLDERSYNC command
00006 *
00007 * Created   :   16.02.2012
00008 *
00009 * Copyright 2007 - 2012 Zarafa Deutschland GmbH
00010 *
00011 * This program is free software: you can redistribute it and/or modify
00012 * it under the terms of the GNU Affero General Public License, version 3,
00013 * as published by the Free Software Foundation with the following additional
00014 * term according to sec. 7:
00015 *
00016 * According to sec. 7 of the GNU Affero General Public License, version 3,
00017 * the terms of the AGPL are supplemented with the following terms:
00018 *
00019 * "Zarafa" is a registered trademark of Zarafa B.V.
00020 * "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
00021 * The licensing of the Program under the AGPL does not imply a trademark license.
00022 * Therefore any rights, title and interest in our trademarks remain entirely with us.
00023 *
00024 * However, if you propagate an unmodified version of the Program you are
00025 * allowed to use the term "Z-Push" to indicate that you distribute the Program.
00026 * Furthermore you may use our trademarks where it is necessary to indicate
00027 * the intended purpose of a product or service provided you use it in accordance
00028 * with honest practices in industrial or commercial matters.
00029 * If you want to propagate modified versions of the Program under the name "Z-Push",
00030 * you may only do so if you have a written permission by Zarafa Deutschland GmbH
00031 * (to acquire a permission please contact Zarafa at trademark@zarafa.com).
00032 *
00033 * This program is distributed in the hope that it will be useful,
00034 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00035 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00036 * GNU Affero General Public License for more details.
00037 *
00038 * You should have received a copy of the GNU Affero General Public License
00039 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00040 *
00041 * Consult LICENSE file for details
00042 ************************************************/
00043 
00044 class FolderSync extends RequestProcessor {
00045 
00054     public function Handle ($commandCode) {
00055         // Maps serverid -> clientid for items that are received from the PIM
00056         $map = array();
00057 
00058         // Parse input
00059         if(!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC))
00060             return false;
00061 
00062         if(!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY))
00063             return false;
00064 
00065         $synckey = self::$decoder->getElementContent();
00066 
00067         if(!self::$decoder->getElementEndTag())
00068             return false;
00069 
00070         $status = SYNC_FSSTATUS_SUCCESS;
00071         $newsynckey = $synckey;
00072         try {
00073             $syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey);
00074 
00075             // We will be saving the sync state under 'newsynckey'
00076             $newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey);
00077         }
00078         catch (StateNotFoundException $snfex) {
00079                 $status = SYNC_FSSTATUS_SYNCKEYERROR;
00080         }
00081         catch (StateInvalidException $sive) {
00082                 $status = SYNC_FSSTATUS_SYNCKEYERROR;
00083         }
00084 
00085         // The ChangesWrapper caches all imports in-memory, so we can send a change count
00086         // before sending the actual data.
00087         // the HierarchyCache is notified and the changes from the PIM are transmitted to the actual backend
00088         $changesMem = self::$deviceManager->GetHierarchyChangesWrapper();
00089 
00090         // the hierarchyCache should now fully be initialized - check for changes in the additional folders
00091         $changesMem->Config(ZPush::GetAdditionalSyncFolders());
00092 
00093         // process incoming changes
00094         if(self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) {
00095             // Ignore <Count> if present
00096             if(self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) {
00097                 self::$decoder->getElementContent();
00098                 if(!self::$decoder->getElementEndTag())
00099                     return false;
00100             }
00101 
00102             // Process the changes (either <Add>, <Modify>, or <Remove>)
00103             $element = self::$decoder->getElement();
00104 
00105             if($element[EN_TYPE] != EN_TYPE_STARTTAG)
00106                 return false;
00107 
00108             $importer = false;
00109             while(1) {
00110                 $folder = new SyncFolder();
00111                 if(!$folder->Decode(self::$decoder))
00112                     break;
00113 
00114                 try {
00115                     if ($status == SYNC_FSSTATUS_SUCCESS && !$importer) {
00116                         // Configure the backends importer with last state
00117                         $importer = self::$backend->GetImporter();
00118                         $importer->Config($syncstate);
00119                         // the messages from the PIM will be forwarded to the backend
00120                         $changesMem->forwardImporter($importer);
00121                     }
00122 
00123                     if ($status == SYNC_FSSTATUS_SUCCESS) {
00124                         switch($element[EN_TAG]) {
00125                             case SYNC_ADD:
00126                             case SYNC_MODIFY:
00127                                 $serverid = $changesMem->ImportFolderChange($folder);
00128                                 break;
00129                             case SYNC_REMOVE:
00130                                 $serverid = $changesMem->ImportFolderDeletion($folder);
00131                                 break;
00132                         }
00133 
00134                         // TODO what does $map??
00135                         if($serverid)
00136                             $map[$serverid] = $folder->clientid;
00137                     }
00138                     else {
00139                         ZLog::Write(LOGLEVEL_WARN, sprintf("Request->HandleFolderSync(): ignoring incoming folderchange for folder '%s' as status indicates problem.", $folder->displayname));
00140                         self::$topCollector->AnnounceInformation("Incoming change ignored", true);
00141                     }
00142                 }
00143                 catch (StatusException $stex) {
00144                    $status = $stex->getCode();
00145                 }
00146             }
00147 
00148             if(!self::$decoder->getElementEndTag())
00149                 return false;
00150         }
00151         // no incoming changes
00152         else {
00153             // check for a potential process loop like described in Issue ZP-5
00154             if ($synckey != "0" && self::$deviceManager->IsHierarchyFullResyncRequired())
00155                 $status = SYNC_FSSTATUS_SYNCKEYERROR;
00156                 self::$deviceManager->AnnounceProcessStatus(false, $status);
00157         }
00158 
00159         if(!self::$decoder->getElementEndTag())
00160             return false;
00161 
00162         // We have processed incoming foldersync requests, now send the PIM
00163         // our changes
00164 
00165         // Output our WBXML reply now
00166         self::$encoder->StartWBXML();
00167 
00168         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC);
00169         {
00170             if ($status == SYNC_FSSTATUS_SUCCESS) {
00171                 try {
00172                     // do nothing if this is an invalid device id (like the 'validate' Androids internal client sends)
00173                     if (!Request::IsValidDeviceID())
00174                         throw new StatusException(sprintf("Request::IsValidDeviceID() indicated that '%s' is not a valid device id", Request::GetDeviceID()), SYNC_FSSTATUS_SERVERERROR);
00175 
00176                     // Changes from backend are sent to the MemImporter and processed for the HierarchyCache.
00177                     // The state which is saved is from the backend, as the MemImporter is only a proxy.
00178                     $exporter = self::$backend->GetExporter();
00179 
00180                     $exporter->Config($syncstate);
00181                     $exporter->InitializeExporter($changesMem);
00182 
00183                     // Stream all changes to the ImportExportChangesMem
00184                     while(is_array($exporter->Synchronize()));
00185 
00186                     // get the new state from the backend
00187                     $newsyncstate = (isset($exporter))?$exporter->GetState():"";
00188                 }
00189                 catch (StatusException $stex) {
00190                     if ($stex->getCode() == SYNC_FSSTATUS_CODEUNKNOWN)
00191                         $status = SYNC_FSSTATUS_SYNCKEYERROR;
00192                     else
00193                         $status = $stex->getCode();
00194                 }
00195             }
00196 
00197             self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
00198             self::$encoder->content($status);
00199             self::$encoder->endTag();
00200 
00201             if ($status == SYNC_FSSTATUS_SUCCESS) {
00202                 self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
00203                 $synckey = ($changesMem->IsStateChanged()) ? $newsynckey : $synckey;
00204                 self::$encoder->content($synckey);
00205                 self::$encoder->endTag();
00206 
00207                 // Stream folders directly to the PDA
00208                 $streamimporter = new ImportChangesStream(self::$encoder, false);
00209                 $changesMem->InitializeExporter($streamimporter);
00210                 $changeCount = $changesMem->GetChangeCount();
00211 
00212                 self::$encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES);
00213                 {
00214                     self::$encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT);
00215                     self::$encoder->content($changeCount);
00216                     self::$encoder->endTag();
00217                     while($changesMem->Synchronize());
00218                 }
00219                 self::$encoder->endTag();
00220                 self::$topCollector->AnnounceInformation(sprintf("Outgoing %d folders",$changeCount), true);
00221 
00222                 // everything fine, save the sync state for the next time
00223                 if ($synckey == $newsynckey)
00224                     self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $newsyncstate);
00225             }
00226         }
00227         self::$encoder->endTag();
00228 
00229         return true;
00230     }
00231 }
00232 ?>