1<?php
2/***********************************************
3* File      :   moveitems.php
4* Project   :   Z-Push
5* Descr     :   Provides the MOVEITEMS command
6*
7* Created   :   16.02.2012
8*
9* Copyright 2007 - 2016 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.
14*
15* This program is distributed in the hope that it will be useful,
16* but WITHOUT ANY WARRANTY; without even the implied warranty of
17* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18* GNU Affero General Public License for more details.
19*
20* You should have received a copy of the GNU Affero General Public License
21* along with this program.  If not, see <http://www.gnu.org/licenses/>.
22*
23* Consult LICENSE file for details
24************************************************/
25
26class MoveItems extends RequestProcessor {
27
28    /**
29     * Handles the MoveItems command
30     *
31     * @param int       $commandCode
32     *
33     * @access public
34     * @return boolean
35     */
36    public function Handle($commandCode) {
37        if(!self::$decoder->getElementStartTag(SYNC_MOVE_MOVES))
38            return false;
39
40        $moves = array();
41        while(self::$decoder->getElementStartTag(SYNC_MOVE_MOVE)) {
42            $move = array();
43            if(self::$decoder->getElementStartTag(SYNC_MOVE_SRCMSGID)) {
44                $move["srcmsgid"] = self::$decoder->getElementContent();
45                if(!self::$decoder->getElementEndTag())
46                    break;
47            }
48            if(self::$decoder->getElementStartTag(SYNC_MOVE_SRCFLDID)) {
49                $move["srcfldid"] = self::$decoder->getElementContent();
50                if(!self::$decoder->getElementEndTag())
51                    break;
52            }
53            if(self::$decoder->getElementStartTag(SYNC_MOVE_DSTFLDID)) {
54                $move["dstfldid"] = self::$decoder->getElementContent();
55                if(!self::$decoder->getElementEndTag())
56                    break;
57            }
58            array_push($moves, $move);
59
60            if(!self::$decoder->getElementEndTag())
61                return false;
62        }
63
64        if(!self::$decoder->getElementEndTag())
65            return false;
66
67        self::$encoder->StartWBXML();
68
69        self::$encoder->startTag(SYNC_MOVE_MOVES);
70
71        $operationResults = array();
72        $operationCounter = 0;
73        $operationTotal = count($moves);
74        foreach($moves as $move) {
75            $operationCounter++;
76            self::$encoder->startTag(SYNC_MOVE_RESPONSE);
77            self::$encoder->startTag(SYNC_MOVE_SRCMSGID);
78            self::$encoder->content($move["srcmsgid"]);
79            self::$encoder->endTag();
80
81            $status = SYNC_MOVEITEMSSTATUS_SUCCESS;
82            $result = false;
83            try {
84                $sourceBackendFolderId = self::$deviceManager->GetBackendIdForFolderId($move["srcfldid"]);
85
86                // if the source folder is an additional folder the backend has to be setup correctly
87                if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($sourceBackendFolderId)))
88                    throw new StatusException(sprintf("HandleMoveItems() could not Setup() the backend for folder id %s/%s", $move["srcfldid"], $sourceBackendFolderId), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
89
90                $importer = self::$backend->GetImporter($sourceBackendFolderId);
91                if ($importer === false)
92                    throw new StatusException(sprintf("HandleMoveItems() could not get an importer for folder id %s/%s", $move["srcfldid"], $sourceBackendFolderId), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
93
94                // get saved SyncParameters of the source folder
95                $spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($move["srcfldid"]);
96                if (!$spa->HasSyncKey())
97                    throw new StatusException(sprintf("MoveItems(): Source folder id '%s' is not fully synchronized. Unable to perform operation.", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
98
99                // get saved SyncParameters of the destination folder
100                $destSpa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($move["dstfldid"]);
101                if (!$destSpa->HasSyncKey()) {
102                    $destSpa->SetFolderId($move["dstfldid"]);
103                    $destSpa->SetSyncKey(self::$deviceManager->GetStateManager()->GetZeroSyncKey());
104                }
105
106                $importer->SetMoveStates($spa->GetMoveState(), $destSpa->GetMoveState());
107                $importer->ConfigContentParameters($spa->GetCPO());
108
109                $result = $importer->ImportMessageMove($move["srcmsgid"], self::$deviceManager->GetBackendIdForFolderId($move["dstfldid"]));
110                // We discard the standard importer state for now.
111
112                // Get the move states and save them in the SyncParameters of the src and dst folder
113                list($srcMoveState, $dstMoveState) = $importer->GetMoveStates();
114                if ($spa->GetMoveState() !== $srcMoveState) {
115                    $spa->SetMoveState($srcMoveState);
116                    self::$deviceManager->GetStateManager()->SetSynchedFolderState($spa);
117                }
118                if ($destSpa->GetMoveState() !== $dstMoveState) {
119                    $destSpa->SetMoveState($dstMoveState);
120                    self::$deviceManager->GetStateManager()->SetSynchedFolderState($destSpa);
121                }
122            }
123            catch (StatusException $stex) {
124                if ($stex->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) // same as SYNC_FSSTATUS_CODEUNKNOWN
125                    $status = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID;
126                else
127                    $status = $stex->getCode();
128            }
129
130            if ($operationCounter % 10 == 0) {
131                self::$topCollector->AnnounceInformation(sprintf("Moved %d objects out of %d", $operationCounter, $operationTotal));
132            }
133
134            // save the operation result
135            if (!isset($operationResults[$status])) {
136                $operationResults[$status] = 0;
137            }
138            $operationResults[$status]++;
139
140            self::$encoder->startTag(SYNC_MOVE_STATUS);
141            self::$encoder->content($status);
142            self::$encoder->endTag();
143
144            self::$encoder->startTag(SYNC_MOVE_DSTMSGID);
145            self::$encoder->content( (($result !== false ) ? $result : $move["srcmsgid"]));
146            self::$encoder->endTag();
147            self::$encoder->endTag();
148        }
149
150        self::$topCollector->AnnounceInformation(sprintf("Moved %d - Codes", $operationTotal), true);
151        foreach ($operationResults as $status => $occurences) {
152            self::$topCollector->AnnounceInformation(sprintf("%dx%d", $occurences, $status), true);
153        }
154
155
156        self::$encoder->endTag();
157        return true;
158    }
159}
160