diff --git a/lib/ext/Syncroton/Command/ItemOperations.php b/lib/ext/Syncroton/Command/ItemOperations.php index 279bf2b..4f5c648 100644 --- a/lib/ext/Syncroton/Command/ItemOperations.php +++ b/lib/ext/Syncroton/Command/ItemOperations.php @@ -1,309 +1,306 @@ */ /** * class to handle ActiveSync ItemOperations command * * @package Syncroton * @subpackage Command */ class Syncroton_Command_ItemOperations extends Syncroton_Command_Wbxml { const STATUS_SUCCESS = 1; const STATUS_PROTOCOL_ERROR = 2; const STATUS_SERVER_ERROR = 3; const STATUS_ITEM_FAILED_CONVERSION = 14; protected $_defaultNameSpace = 'uri:ItemOperations'; protected $_documentElement = 'ItemOperations'; /** * list of items to move * * @var array */ protected $_fetches = array(); /** * list of folder to empty * * @var array */ protected $_emptyFolderContents = array(); /** * parse MoveItems request * */ public function handle() { $xml = simplexml_import_dom($this->_requestBody); if (isset($xml->Fetch)) { foreach ($xml->Fetch as $fetch) { $this->_fetches[] = $this->_handleFetch($fetch); } } if (isset($xml->EmptyFolderContents)) { foreach ($xml->EmptyFolderContents as $emptyFolderContents) { $this->_emptyFolderContents[] = $this->_handleEmptyFolderContents($emptyFolderContents); } } - - if ($this->_logger instanceof Zend_Log) - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " fetches: " . print_r($this->_fetches, true)); } /** * generate ItemOperations response * * @todo add multipart support to all types of fetches */ public function getResponse() { // add aditional namespaces $this->_outputDom->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:AirSyncBase' , 'uri:AirSyncBase'); $this->_outputDom->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:AirSync' , 'uri:AirSync'); $this->_outputDom->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:Search' , 'uri:Search'); $itemOperations = $this->_outputDom->documentElement; $itemOperations->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_SUCCESS)); $response = $itemOperations->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Response')); foreach ($this->_fetches as $fetch) { $fetchTag = $response->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Fetch')); try { $dataController = Syncroton_Data_Factory::factory($fetch['store'], $this->_device, $this->_syncTimeStamp); if (isset($fetch['collectionId'])) { $fetchTag->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_SUCCESS)); $fetchTag->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $fetch['collectionId'])); $fetchTag->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $fetch['serverId'])); $properties = $this->_outputDom->createElementNS('uri:ItemOperations', 'Properties'); $dataController ->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $fetch['collectionId'], 'options' => $fetch['options'])), $fetch['serverId']) ->appendXML($properties, $this->_device); $fetchTag->appendChild($properties); } elseif (isset($fetch['longId'])) { $fetchTag->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_SUCCESS)); $fetchTag->appendChild($this->_outputDom->createElementNS('uri:Search', 'LongId', $fetch['longId'])); $properties = $this->_outputDom->createElementNS('uri:ItemOperations', 'Properties'); $dataController ->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $fetch['longId'], 'options' => $fetch['options'])), $fetch['longId']) ->appendXML($properties, $this->_device); $fetchTag->appendChild($properties); } elseif (isset($fetch['fileReference'])) { $fetchTag->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_SUCCESS)); $fetchTag->appendChild($this->_outputDom->createElementNS('uri:AirSyncBase', 'FileReference', $fetch['fileReference'])); $properties = $this->_outputDom->createElementNS('uri:ItemOperations', 'Properties'); $fileReference = $dataController->getFileReference($fetch['fileReference']); // unset data field and move content to stream if ($this->_requestParameters['acceptMultipart'] == true) { $this->_headers['Content-Type'] = 'application/vnd.ms-sync.multipart'; $partStream = fopen("php://temp", 'r+'); if (is_resource($fileReference->data)) { stream_copy_to_stream($fileReference->data, $partStream); } else { fwrite($partStream, $fileReference->data); } unset($fileReference->data); $this->_parts[] = $partStream; $fileReference->part = count($this->_parts); } /** * the client requested a range. But we return the whole file. * * That's not correct, but allowed. The server is allowed to overwrite the range. * * @todo implement cutting $fileReference->data into pieces */ if (isset($fetch['options']['range'])) { $dataSize = $this->_getDataSize($fileReference->data); $total = $this->_outputDom->createElementNS('uri:ItemOperations', 'Total', $dataSize); $properties->appendChild($total); $rangeEnd = $dataSize > 0 ? $dataSize - 1 : 0; $range = $this->_outputDom->createElementNS('uri:ItemOperations', 'Range', '0-' . $rangeEnd); $properties->appendChild($range); } $fileReference->appendXML($properties, $this->_device); $fetchTag->appendChild($properties); } } catch (Syncroton_Exception_NotFound $e) { $response->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_ITEM_FAILED_CONVERSION)); } catch (Exception $e) { //echo __LINE__; echo $e->getMessage(); echo $e->getTraceAsString(); $response->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', Syncroton_Command_ItemOperations::STATUS_SERVER_ERROR)); } } foreach ($this->_emptyFolderContents as $emptyFolderContents) { try { $folder = $this->_folderBackend->getFolder($this->_device, $emptyFolderContents['collectionId']); $dataController = Syncroton_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp); $dataController->emptyFolderContents($emptyFolderContents['collectionId'], $emptyFolderContents['options']); $status = Syncroton_Command_ItemOperations::STATUS_SUCCESS; } catch (Syncroton_Exception_Status_ItemOperations $e) { $status = $e->getCode(); } catch (Exception $e) { $status = Syncroton_Exception_Status_ItemOperations::ITEM_SERVER_ERROR; } $emptyFolderContentsTag = $this->_outputDom->createElementNS('uri:ItemOperations', 'EmptyFolderContents'); $emptyFolderContentsTag->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', $status)); $emptyFolderContentsTag->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $emptyFolderContents['collectionId'])); $response->appendChild($emptyFolderContentsTag); } return $this->_outputDom; } /** * parse fetch request * * @param SimpleXMLElement $fetch * @return array */ protected function _handleFetch(SimpleXMLElement $fetch) { $fetchArray = array( 'store' => (string)$fetch->Store, 'options' => array() ); // try to fetch element from namespace AirSync $airSync = $fetch->children('uri:AirSync'); if (isset($airSync->CollectionId)) { $fetchArray['collectionId'] = (string)$airSync->CollectionId; $fetchArray['serverId'] = (string)$airSync->ServerId; } // try to fetch element from namespace Search $search = $fetch->children('uri:Search'); if (isset($search->LongId)) { $fetchArray['longId'] = (string)$search->LongId; } // try to fetch element from namespace AirSyncBase $airSyncBase = $fetch->children('uri:AirSyncBase'); if (isset($airSyncBase->FileReference)) { $fetchArray['fileReference'] = (string)$airSyncBase->FileReference; } if (isset($fetch->Options)) { // try to fetch element from namespace AirSyncBase $airSyncBase = $fetch->Options->children('uri:AirSyncBase'); if (isset($airSyncBase->BodyPreference)) { foreach ($airSyncBase->BodyPreference as $bodyPreference) { $type = (int) $bodyPreference->Type; $fetchArray['options']['bodyPreferences'][$type] = array( 'type' => $type ); // optional if (isset($bodyPreference->TruncationSize)) { $fetchArray['options']['bodyPreferences'][$type]['truncationSize'] = (int) $bodyPreference->TruncationSize; } // optional if (isset($bodyPreference->AllOrNone)) { $fetchArray['options']['bodyPreferences'][$type]['allOrNone'] = (int) $bodyPreference->AllOrNone; } } } if (isset($fetch->Options->MIMESupport)){ $fetchArray['options']['mimeSupport'] = (int) $fetch->Options->MIMESupport; } if (isset($airSyncBase->Range)) { $fetchArray['options']['range'] = (string) $airSyncBase->Range; } } return $fetchArray; } /** * handle empty folder request * * @param SimpleXMLElement $emptyFolderContent * @return array */ protected function _handleEmptyFolderContents(SimpleXMLElement $emptyFolderContent) { $folderArray = array( 'collectiondId' => null, 'options' => array('deleteSubFolders' => FALSE) ); // try to fetch element from namespace AirSync $airSync = $emptyFolderContent->children('uri:AirSync'); $folderArray['collectionId'] = (string)$airSync->CollectionId; if (isset($emptyFolderContent->Options)) { $folderArray['options']['deleteSubFolders'] = isset($emptyFolderContent->Options->DeleteSubFolders); } return $folderArray; } /** * return length of data * * @param string|resource $data * @return number */ protected function _getDataSize($data) { if (is_resource($data)) { rewind($data); fseek($data, 0, SEEK_END); return ftell($data); } else { return strlen($data); } } } diff --git a/lib/ext/Syncroton/Command/MoveItems.php b/lib/ext/Syncroton/Command/MoveItems.php index 1ae2a86..73e57dd 100644 --- a/lib/ext/Syncroton/Command/MoveItems.php +++ b/lib/ext/Syncroton/Command/MoveItems.php @@ -1,97 +1,94 @@ * @author Aleksander Machniak */ /** * class to handle ActiveSync MoveItems command * * @package Syncroton * @subpackage Command */ class Syncroton_Command_MoveItems extends Syncroton_Command_Wbxml { const STATUS_SUCCESS = 3; protected $_defaultNameSpace = 'uri:Move'; protected $_documentElement = 'MoveItems'; /** * list of items to move * * @var array */ protected $_moves = array(); /** * parse MoveItems request */ public function handle() { $xml = simplexml_import_dom($this->_requestBody); foreach ($xml->Move as $move) { $this->_moves[] = array( 'srcMsgId' => (string)$move->SrcMsgId, 'srcFldId' => (string)$move->SrcFldId, 'dstFldId' => (string)$move->DstFldId ); } - - if ($this->_logger instanceof Zend_Log) - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " moves: " . print_r($this->_moves, true)); } /** * generate MoveItems response */ public function getResponse() { $moves = $this->_outputDom->documentElement; foreach ($this->_moves as $move) { $response = $moves->appendChild($this->_outputDom->createElementNS('uri:Move', 'Response')); $response->appendChild($this->_outputDom->createElementNS('uri:Move', 'SrcMsgId', $move['srcMsgId'])); try { if ($move['srcFldId'] === $move['dstFldId']) { throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::SAME_FOLDER); } try { $sourceFolder = $this->_folderBackend->getFolder($this->_device, $move['srcFldId']); } catch (Syncroton_Exception_NotFound $e) { throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE); } try { $destinationFolder = $this->_folderBackend->getFolder($this->_device, $move['dstFldId']); } catch (Syncroton_Exception_NotFound $senf) { throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_DESTINATION); } $dataController = Syncroton_Data_Factory::factory($sourceFolder->class, $this->_device, $this->_syncTimeStamp); $newId = $dataController->moveItem($move['srcFldId'], $move['srcMsgId'], $move['dstFldId']); $response->appendChild($this->_outputDom->createElementNS('uri:Move', 'Status', Syncroton_Command_MoveItems::STATUS_SUCCESS)); if ($newId) $response->appendChild($this->_outputDom->createElementNS('uri:Move', 'DstMsgId', $newId)); } catch (Syncroton_Exception_Status $e) { $response->appendChild($this->_outputDom->createElementNS('uri:Move', 'Status', $e->getCode())); } catch (Exception $e) { $response->appendChild($this->_outputDom->createElementNS('uri:Move', 'Status', Syncroton_Exception_Status::SERVER_ERROR)); } } return $this->_outputDom; } } diff --git a/lib/ext/Syncroton/Command/Search.php b/lib/ext/Syncroton/Command/Search.php index 4913785..7f7f412 100644 --- a/lib/ext/Syncroton/Command/Search.php +++ b/lib/ext/Syncroton/Command/Search.php @@ -1,85 +1,82 @@ */ /** * class to handle ActiveSync Search command * * @package Syncroton * @subpackage Command */ class Syncroton_Command_Search extends Syncroton_Command_Wbxml { const STATUS_SUCCESS = 1; const STATUS_SERVER_ERROR = 3; protected $_defaultNameSpace = 'uri:Search'; protected $_documentElement = 'Search'; /** * store data * * @var Syncroton_Model_StoreRequest */ protected $_store; /** * parse search command request * */ public function handle() { $xml = simplexml_import_dom($this->_requestBody); $this->_store = new Syncroton_Model_StoreRequest($xml->Store); - - if ($this->_logger instanceof Zend_Log) - $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " stores: " . print_r($this->_store, true)); } /** * generate search command response * */ public function getResponse() { $dataController = Syncroton_Data_Factory::factory($this->_store->name, $this->_device, new DateTime()); if (! $dataController instanceof Syncroton_Data_IDataSearch) { throw new RuntimeException('class must be instanceof Syncroton_Data_IDataSearch'); } try { // Search $storeResponse = $dataController->search($this->_store); $storeResponse->status = self::STATUS_SUCCESS; } catch (Exception $e) { if ($this->_logger instanceof Zend_Log) $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " search exception: " . $e->getMessage()); if ($this->_logger instanceof Zend_Log) $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " saerch exception trace : " . $e->getTraceAsString()); $storeResponse = new Syncroton_Model_StoreResponse(array( 'status' => self::STATUS_SERVER_ERROR )); } $search = $this->_outputDom->documentElement; $search->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', self::STATUS_SUCCESS)); $response = $search->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Response')); $store = $response->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Store')); $storeResponse->appendXML($store, $this->_device); return $this->_outputDom; } }