source: trunk/zpush/lib/request/foldersync.php @ 7589

Revision 7589, 10.0 KB checked in by douglas, 11 years ago (diff)

Ticket #3209 - Integrar módulo de sincronização Z-push ao Expresso

Line 
1<?php
2/***********************************************
3* File      :   foldersync.php
4* Project   :   Z-Push
5* Descr     :   Provides the FOLDERSYNC command
6*
7* Created   :   16.02.2012
8*
9* Copyright 2007 - 2012 Zarafa Deutschland GmbH
10*
11* This program is free software: you can redistribute it and/or modify
12* it under the terms of the GNU Affero General Public License, version 3,
13* as published by the Free Software Foundation with the following additional
14* term according to sec. 7:
15*
16* According to sec. 7 of the GNU Affero General Public License, version 3,
17* the terms of the AGPL are supplemented with the following terms:
18*
19* "Zarafa" is a registered trademark of Zarafa B.V.
20* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
21* The licensing of the Program under the AGPL does not imply a trademark license.
22* Therefore any rights, title and interest in our trademarks remain entirely with us.
23*
24* However, if you propagate an unmodified version of the Program you are
25* allowed to use the term "Z-Push" to indicate that you distribute the Program.
26* Furthermore you may use our trademarks where it is necessary to indicate
27* the intended purpose of a product or service provided you use it in accordance
28* with honest practices in industrial or commercial matters.
29* If you want to propagate modified versions of the Program under the name "Z-Push",
30* you may only do so if you have a written permission by Zarafa Deutschland GmbH
31* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
32*
33* This program is distributed in the hope that it will be useful,
34* but WITHOUT ANY WARRANTY; without even the implied warranty of
35* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36* GNU Affero General Public License for more details.
37*
38* You should have received a copy of the GNU Affero General Public License
39* along with this program.  If not, see <http://www.gnu.org/licenses/>.
40*
41* Consult LICENSE file for details
42************************************************/
43
44class FolderSync extends RequestProcessor {
45
46    /**
47     * Handles the FolderSync command
48     *
49     * @param int       $commandCode
50     *
51     * @access public
52     * @return boolean
53     */
54    public function Handle ($commandCode) {
55        // Maps serverid -> clientid for items that are received from the PIM
56        $map = array();
57
58        // Parse input
59        if(!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC))
60            return false;
61
62        if(!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY))
63            return false;
64
65        $synckey = self::$decoder->getElementContent();
66
67        if(!self::$decoder->getElementEndTag())
68            return false;
69
70        // every FolderSync with SyncKey 0 should return the supported AS version & command headers
71        if($synckey == "0") {
72            self::$specialHeaders = array();
73            self::$specialHeaders[] = ZPush::GetSupportedProtocolVersions();
74            self::$specialHeaders[] = ZPush::GetSupportedCommands();
75        }
76
77        $status = SYNC_FSSTATUS_SUCCESS;
78        $newsynckey = $synckey;
79        try {
80            $syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey);
81
82            // We will be saving the sync state under 'newsynckey'
83            $newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey);
84        }
85        catch (StateNotFoundException $snfex) {
86                $status = SYNC_FSSTATUS_SYNCKEYERROR;
87        }
88        catch (StateInvalidException $sive) {
89                $status = SYNC_FSSTATUS_SYNCKEYERROR;
90        }
91
92        // The ChangesWrapper caches all imports in-memory, so we can send a change count
93        // before sending the actual data.
94        // the HierarchyCache is notified and the changes from the PIM are transmitted to the actual backend
95        $changesMem = self::$deviceManager->GetHierarchyChangesWrapper();
96
97        // the hierarchyCache should now fully be initialized - check for changes in the additional folders
98        $changesMem->Config(ZPush::GetAdditionalSyncFolders());
99
100        // process incoming changes
101        if(self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) {
102            // Ignore <Count> if present
103            if(self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) {
104                self::$decoder->getElementContent();
105                if(!self::$decoder->getElementEndTag())
106                    return false;
107            }
108
109            // Process the changes (either <Add>, <Modify>, or <Remove>)
110            $element = self::$decoder->getElement();
111
112            if($element[EN_TYPE] != EN_TYPE_STARTTAG)
113                return false;
114
115            $importer = false;
116            while(1) {
117                $folder = new SyncFolder();
118                if(!$folder->Decode(self::$decoder))
119                    break;
120
121                try {
122                    if ($status == SYNC_FSSTATUS_SUCCESS && !$importer) {
123                        // Configure the backends importer with last state
124                        $importer = self::$backend->GetImporter();
125                        $importer->Config($syncstate);
126                        // the messages from the PIM will be forwarded to the backend
127                        $changesMem->forwardImporter($importer);
128                    }
129
130                    if ($status == SYNC_FSSTATUS_SUCCESS) {
131                        switch($element[EN_TAG]) {
132                            case SYNC_ADD:
133                            case SYNC_MODIFY:
134                                $serverid = $changesMem->ImportFolderChange($folder);
135                                break;
136                            case SYNC_REMOVE:
137                                $serverid = $changesMem->ImportFolderDeletion($folder);
138                                break;
139                        }
140
141                        // TODO what does $map??
142                        if($serverid)
143                            $map[$serverid] = $folder->clientid;
144                    }
145                    else {
146                        ZLog::Write(LOGLEVEL_WARN, sprintf("Request->HandleFolderSync(): ignoring incoming folderchange for folder '%s' as status indicates problem.", $folder->displayname));
147                        self::$topCollector->AnnounceInformation("Incoming change ignored", true);
148                    }
149                }
150                catch (StatusException $stex) {
151                   $status = $stex->getCode();
152                }
153            }
154
155            if(!self::$decoder->getElementEndTag())
156                return false;
157        }
158        // no incoming changes
159        else {
160            // check for a potential process loop like described in Issue ZP-5
161            if ($synckey != "0" && self::$deviceManager->IsHierarchyFullResyncRequired())
162                $status = SYNC_FSSTATUS_SYNCKEYERROR;
163                self::$deviceManager->AnnounceProcessStatus(false, $status);
164        }
165
166        if(!self::$decoder->getElementEndTag())
167            return false;
168
169        // We have processed incoming foldersync requests, now send the PIM
170        // our changes
171
172        // Output our WBXML reply now
173        self::$encoder->StartWBXML();
174
175        self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC);
176        {
177            if ($status == SYNC_FSSTATUS_SUCCESS) {
178                try {
179                    // do nothing if this is an invalid device id (like the 'validate' Androids internal client sends)
180                    if (!Request::IsValidDeviceID())
181                        throw new StatusException(sprintf("Request::IsValidDeviceID() indicated that '%s' is not a valid device id", Request::GetDeviceID()), SYNC_FSSTATUS_SERVERERROR);
182
183                    // Changes from backend are sent to the MemImporter and processed for the HierarchyCache.
184                    // The state which is saved is from the backend, as the MemImporter is only a proxy.
185                    $exporter = self::$backend->GetExporter();
186
187                    $exporter->Config($syncstate);
188                    $exporter->InitializeExporter($changesMem);
189
190                    // Stream all changes to the ImportExportChangesMem
191                    while(is_array($exporter->Synchronize()));
192
193                    // get the new state from the backend
194                    $newsyncstate = (isset($exporter))?$exporter->GetState():"";
195                }
196                catch (StatusException $stex) {
197                    if ($stex->getCode() == SYNC_FSSTATUS_CODEUNKNOWN)
198                        $status = SYNC_FSSTATUS_SYNCKEYERROR;
199                    else
200                        $status = $stex->getCode();
201                }
202            }
203
204            self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
205            self::$encoder->content($status);
206            self::$encoder->endTag();
207
208            if ($status == SYNC_FSSTATUS_SUCCESS) {
209                self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
210                $synckey = ($changesMem->IsStateChanged()) ? $newsynckey : $synckey;
211                self::$encoder->content($synckey);
212                self::$encoder->endTag();
213
214                // Stream folders directly to the PDA
215                $streamimporter = new ImportChangesStream(self::$encoder, false);
216                $changesMem->InitializeExporter($streamimporter);
217                $changeCount = $changesMem->GetChangeCount();
218
219                self::$encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES);
220                {
221                    self::$encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT);
222                    self::$encoder->content($changeCount);
223                    self::$encoder->endTag();
224                    while($changesMem->Synchronize());
225                }
226                self::$encoder->endTag();
227                self::$topCollector->AnnounceInformation(sprintf("Outgoing %d folders",$changeCount), true);
228
229                // everything fine, save the sync state for the next time
230                if ($synckey == $newsynckey)
231                    self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $newsyncstate);
232            }
233        }
234        self::$encoder->endTag();
235
236        return true;
237    }
238}
239?>
Note: See TracBrowser for help on using the repository browser.