/** * Checks a folder for changes performing Exporter->GetChangeCount() * * @param string $folderid counts changes for a folder * * @access private * @return boolean indicating if changes were found or not */ private function CountChange($folderid) { $spa = $this->GetCollection($folderid); if (!$spa) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CountChange(): Could not get SyncParameters object from cache for folderid '%s' to verify notification. Ignoring.", $folderid)); return false; } // prevent ZP-623 by checking if the states have been used before, if so force a sync on this folder if (ZPush::GetDeviceManager()->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) { ZLog::Write(LOGLEVEL_DEBUG, "SyncCollections->CountChange(): Cannot verify changes for state as it was already used. Forcing sync of folder."); $this->changes[$folderid] = 1; return true; } $backendFolderId = ZPush::GetDeviceManager()->GetBackendIdForFolderId($folderid); // switch user store if this is a additional folder (additional true -> do not debug) ZPush::GetBackend()->Setup(ZPush::GetAdditionalSyncFolderStore($backendFolderId, true)); $changecount = false; try { $exporter = ZPush::GetBackend()->GetExporter($backendFolderId); if ($exporter !== false && isset($this->addparms[$folderid]["state"])) { $importer = false; $exporter->SetMoveStates($spa->GetMoveState()); $exporter->Config($this->addparms[$folderid]["state"], BACKEND_DISCARD_DATA); $exporter->ConfigContentParameters($spa->GetCPO()); $ret = $exporter->InitializeExporter($importer); if ($ret !== false) { $changecount = $exporter->GetChangeCount(); } } } catch (StatusException $ste) { if ($ste->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) { ZLog::Write(LOGLEVEL_WARN, "SyncCollections->CountChange(): exporter can not be re-configured due to state error, emulating change in folder to force Sync."); $this->changes[$folderid] = 1; // make sure this folder is fully synched on next Sync request $this->invalidateFolderStat($spa); return true; } throw new StatusException("SyncCollections->CountChange(): exporter can not be re-configured.", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN); } // start over if exporter can not be configured atm if ($changecount === false) { ZLog::Write(LOGLEVEL_WARN, "SyncCollections->CountChange(): no changes received from Exporter."); } $this->changes[$folderid] = $changecount; return $changecount > 0; }
/** * Handles the MoveItems command * * @param int $commandCode * * @access public * @return boolean */ public function Handle($commandCode) { if (!self::$decoder->getElementStartTag(SYNC_MOVE_MOVES)) { return false; } $moves = array(); while (self::$decoder->getElementStartTag(SYNC_MOVE_MOVE)) { $move = array(); if (self::$decoder->getElementStartTag(SYNC_MOVE_SRCMSGID)) { $move["srcmsgid"] = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { break; } } if (self::$decoder->getElementStartTag(SYNC_MOVE_SRCFLDID)) { $move["srcfldid"] = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { break; } } if (self::$decoder->getElementStartTag(SYNC_MOVE_DSTFLDID)) { $move["dstfldid"] = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { break; } } array_push($moves, $move); if (!self::$decoder->getElementEndTag()) { return false; } } if (!self::$decoder->getElementEndTag()) { return false; } self::$encoder->StartWBXML(); self::$encoder->startTag(SYNC_MOVE_MOVES); foreach ($moves as $move) { self::$encoder->startTag(SYNC_MOVE_RESPONSE); self::$encoder->startTag(SYNC_MOVE_SRCMSGID); self::$encoder->content($move["srcmsgid"]); self::$encoder->endTag(); $status = SYNC_MOVEITEMSSTATUS_SUCCESS; $result = false; try { // if the source folder is an additional folder the backend has to be setup correctly if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($move["srcfldid"]))) { throw new StatusException(sprintf("HandleMoveItems() could not Setup() the backend for folder id '%s'", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } $importer = self::$backend->GetImporter($move["srcfldid"]); if ($importer === false) { throw new StatusException(sprintf("HandleMoveItems() could not get an importer for folder id '%s'", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } $result = $importer->ImportMessageMove($move["srcmsgid"], $move["dstfldid"]); // We discard the importer state for now. } catch (StatusException $stex) { if ($stex->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) { // same as SYNC_FSSTATUS_CODEUNKNOWN $status = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID; } else { $status = $stex->getCode(); } } self::$topCollector->AnnounceInformation(sprintf("Operation status: %s", $status), true); self::$encoder->startTag(SYNC_MOVE_STATUS); self::$encoder->content($status); self::$encoder->endTag(); self::$encoder->startTag(SYNC_MOVE_DSTMSGID); self::$encoder->content($result !== false ? $result : $move["srcmsgid"]); self::$encoder->endTag(); self::$encoder->endTag(); } self::$encoder->endTag(); return true; }
/** * Checks a folder for changes performing Exporter->GetChangeCount() * * @param string $folderid counts changes for a folder * * @access private * @return boolean indicating if changes were found or not */ private function CountChange($folderid) { $spa = $this->GetCollection($folderid); // switch user store if this is a additional folder (additional true -> do not debug) ZPush::GetBackend()->Setup(ZPush::GetAdditionalSyncFolderStore($folderid, true)); $changecount = false; try { $exporter = ZPush::GetBackend()->GetExporter($folderid); if ($exporter !== false && isset($this->addparms[$folderid]["state"])) { $importer = false; $exporter->Config($this->addparms[$folderid]["state"], BACKEND_DISCARD_DATA); $exporter->ConfigContentParameters($spa->GetCPO()); $ret = $exporter->InitializeExporter($importer); if ($ret !== false) { $changecount = $exporter->GetChangeCount(); } } } catch (StatusException $ste) { throw new StatusException("SyncCollections->CountChange(): exporter can not be re-configured.", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN); } // start over if exporter can not be configured atm if ($changecount === false) { ZLog::Write(LOGLEVEL_WARN, "SyncCollections->CountChange(): no changes received from Exporter."); } $this->changes[$folderid] = $changecount; if (isset($this->addparms[$folderid]['savestate'])) { try { // Discard any data while (is_array($exporter->Synchronize())) { } $this->addparms[$folderid]['savestate'] = $exporter->GetState(); } catch (StatusException $ste) { throw new StatusException("SyncCollections->CountChange(): could not get new state from exporter", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN); } } return $changecount > 0; }
/** * Handles creates, updates or deletes of a folder * issued by the commands FolderCreate, FolderUpdate and FolderDelete * * @param int $commandCode * * @access public * @return boolean */ public function Handle($commandCode) { $el = self::$decoder->getElement(); if ($el[EN_TYPE] != EN_TYPE_STARTTAG) { return false; } $create = $update = $delete = false; if ($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERCREATE) { $create = true; } else { if ($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERUPDATE) { $update = true; } else { if ($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERDELETE) { $delete = true; } } } if (!$create && !$update && !$delete) { return false; } // SyncKey if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) { return false; } $synckey = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { return false; } // ServerID $serverid = false; if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID)) { $serverid = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { return false; } } // Parent $parentid = false; // when creating or updating more information is necessary if (!$delete) { if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_PARENTID)) { $parentid = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { return false; } } // Displayname if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_DISPLAYNAME)) { return false; } $displayname = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { return false; } // Type $type = false; if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_TYPE)) { $type = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { return false; } } } // endtag foldercreate, folderupdate, folderdelete if (!self::$decoder->getElementEndTag()) { return false; } $status = SYNC_FSSTATUS_SUCCESS; // Get state of hierarchy try { $syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey); $newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey); // Over the ChangesWrapper the HierarchyCache is notified about all changes $changesMem = self::$deviceManager->GetHierarchyChangesWrapper(); // the hierarchyCache should now fully be initialized - check for changes in the additional folders $changesMem->Config(ZPush::GetAdditionalSyncFolders()); // there are unprocessed changes in the hierarchy, trigger resync if ($changesMem->GetChangeCount() > 0) { throw new StatusException("HandleFolderChange() can not proceed as there are unprocessed hierarchy changes", SYNC_FSSTATUS_SERVERERROR); } // any additional folders can not be modified! if ($serverid !== false && ZPush::GetAdditionalSyncFolderStore($serverid)) { throw new StatusException("HandleFolderChange() can not change additional folders which are configured", SYNC_FSSTATUS_SYSTEMFOLDER); } // switch user store if this this happens inside an additional folder // if this is an additional folder the backend has to be setup correctly if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($parentid != false ? $parentid : $serverid))) { throw new StatusException(sprintf("HandleFolderChange() could not Setup() the backend for folder id '%s'", $parentid != false ? $parentid : $serverid), SYNC_FSSTATUS_SERVERERROR); } } catch (StateNotFoundException $snfex) { $status = SYNC_FSSTATUS_SYNCKEYERROR; } catch (StatusException $stex) { $status = $stex->getCode(); } // set $newsynckey in case of an error if (!isset($newsynckey)) { $newsynckey = $synckey; } if ($status == SYNC_FSSTATUS_SUCCESS) { try { // Configure importer with last state $importer = self::$backend->GetImporter(); $importer->Config($syncstate); // the messages from the PIM will be forwarded to the real importer $changesMem->SetDestinationImporter($importer); // process incoming change if (!$delete) { // Send change $folder = new SyncFolder(); $folder->serverid = $serverid; $folder->parentid = $parentid; $folder->displayname = $displayname; $folder->type = $type; $serverid = $changesMem->ImportFolderChange($folder); } else { // delete folder $changesMem->ImportFolderDeletion($serverid, 0); } } catch (StatusException $stex) { $status = $stex->getCode(); } } self::$encoder->startWBXML(); if ($create) { self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERCREATE); self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS); self::$encoder->content($status); self::$encoder->endTag(); self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY); self::$encoder->content($newsynckey); self::$encoder->endTag(); self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID); self::$encoder->content($serverid); self::$encoder->endTag(); self::$encoder->endTag(); } elseif ($update) { self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERUPDATE); self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS); self::$encoder->content($status); self::$encoder->endTag(); self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY); self::$encoder->content($newsynckey); self::$encoder->endTag(); self::$encoder->endTag(); } elseif ($delete) { self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERDELETE); self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS); self::$encoder->content($status); self::$encoder->endTag(); self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY); self::$encoder->content($newsynckey); self::$encoder->endTag(); self::$encoder->endTag(); } self::$topCollector->AnnounceInformation(sprintf("Operation status %d", $status), true); // Save the sync state for the next time if (isset($importer)) { self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $importer->GetState()); } return true; }
/** * Loads the states and writes them into the SyncCollection Object and the actiondata failstate * * @param SyncCollection $sc SyncCollection object * @param SyncParameters $spa SyncParameters object * @param array $actiondata Actiondata array * @param boolean $loadFailsave (opt) default false - indicates if the failsave states should be loaded * * @access private * @return status indicating if there were errors. If no errors, status is SYNC_STATUS_SUCCESS */ private function loadStates($sc, $spa, &$actiondata, $loadFailsave = false) { $status = SYNC_STATUS_SUCCESS; if ($sc->GetParameter($spa, "state") == null) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync->loadStates(): loading states for folder '%s'", $spa->GetFolderId())); try { $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey())); if ($loadFailsave) { // if this request was made before, there will be a failstate available $actiondata["failstate"] = self::$deviceManager->GetStateManager()->GetSyncFailState(); } // if this is an additional folder the backend has to be setup correctly if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) { throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED); } } catch (StateNotFoundException $snfex) { $status = SYNC_STATUS_INVALIDSYNCKEY; self::$topCollector->AnnounceInformation("StateNotFoundException", true); } catch (StatusException $stex) { $status = $stex->getCode(); self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true); } } return $status; }
/** * Imports a move of a message. This occurs when a user moves an item to another folder * * Normally, we would implement this via the 'offical' importmessagemove() function on the ICS importer, * but the Zarafa importer does not support this. Therefore we currently implement it via a standard mapi * call. This causes a mirror 'add/delete' to be sent to the PDA at the next sync. * Manfred, 2010-10-21. For some mobiles import was causing duplicate messages in the destination folder * (Mantis #202). Therefore we will create a new message in the destination folder, copy properties * of the source message to the new one and then delete the source message. * * @param string $id * @param string $newfolder destination folder * * @access public * @return boolean * @throws StatusException */ public function ImportMessageMove($id, $newfolder) { if (strtolower($newfolder) == strtolower(bin2hex($this->folderid))) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST); } // Get the entryid of the message we're moving $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($id)); if (!$entryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source message id", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } //open the source message $srcmessage = mapi_msgstore_openentry($this->store, $entryid); if (!$srcmessage) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source message: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // get correct mapi store for the destination folder $dststore = ZPush::GetBackend()->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder); if ($dststore === false) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open store of destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $dstentryid = mapi_msgstore_entryidfromsourcekey($dststore, hex2bin($newfolder)); if (!$dstentryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $dstfolder = mapi_msgstore_openentry($dststore, $dstentryid); if (!$dstfolder) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $newmessage = mapi_folder_createmessage($dstfolder); if (!$newmessage) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to create message in destination folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } // Copy message mapi_copyto($srcmessage, array(), array(), $newmessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, copy to destination message failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } $srcfolderentryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid); if (!$srcfolderentryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } $srcfolder = mapi_msgstore_openentry($this->store, $srcfolderentryid); if (!$srcfolder) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // Save changes mapi_savechanges($newmessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, mapi_savechanges() failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } // Delete the old message if (!mapi_folder_deletemessages($srcfolder, array($entryid))) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, delete of source message failed: 0x%X. Possible duplicates.", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_SOURCEORDESTLOCKED); } $sourcekeyprops = mapi_getprops($newmessage, array(PR_SOURCE_KEY)); if (isset($sourcekeyprops[PR_SOURCE_KEY]) && $sourcekeyprops[PR_SOURCE_KEY]) { return bin2hex($sourcekeyprops[PR_SOURCE_KEY]); } return false; }
/** * Sends an e-mail * This messages needs to be saved into the 'sent items' folder * * @param SyncSendMail $sm SyncSendMail object * * @access public * @return boolean * @throws StatusException */ public function SendMail($sm) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): RFC822: %d bytes forward-id: '%s' reply-id: '%s' parent-id: '%s' SaveInSent: '%s' ReplaceMIME: '%s'", strlen($sm->mime), Utils::PrintAsString($sm->forwardflag), Utils::PrintAsString($sm->replyflag), Utils::PrintAsString(isset($sm->source->folderid) ? $sm->source->folderid : false), Utils::PrintAsString($sm->saveinsent), Utils::PrintAsString(isset($sm->replacemime)))); // by splitting the message in several lines we can easily grep later foreach (preg_split("/((\r)?\n)/", $sm->mime) as $rfc822line) { ZLog::Write(LOGLEVEL_WBXML, "RFC822: " . $rfc822line); } $mimeParams = array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8'); $mimeObject = new Mail_mimeDecode($sm->mime); $message = $mimeObject->decode($mimeParams); $sendMailProps = MAPIMapping::GetSendMailProperties(); $sendMailProps = getPropIdsFromStrings($this->store, $sendMailProps); // Open the outbox and create the message there $storeprops = mapi_getprops($this->store, array($sendMailProps["outboxentryid"], $sendMailProps["ipmsentmailentryid"])); if (isset($storeprops[$sendMailProps["outboxentryid"]])) { $outbox = mapi_msgstore_openentry($this->store, $storeprops[$sendMailProps["outboxentryid"]]); } if (!$outbox) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): No Outbox found or unable to create message: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_SERVERERROR); } $mapimessage = mapi_folder_createmessage($outbox); //message properties to be set $mapiprops = array(); // only save the outgoing in sent items folder if the mobile requests it $mapiprops[$sendMailProps["sentmailentryid"]] = $storeprops[$sendMailProps["ipmsentmailentryid"]]; // Check if imtomapi function is available and use it to send the mime message. // It is available since ZCP 7.0.6 // @see http://jira.zarafa.com/browse/ZCP-9508 if (function_exists('mapi_feature') && mapi_feature('INETMAPI_IMTOMAPI')) { ZLog::Write(LOGLEVEL_DEBUG, "Use the mapi_inetmapi_imtomapi function"); $ab = mapi_openaddressbook($this->session); mapi_inetmapi_imtomapi($this->session, $this->store, $ab, $mapimessage, $sm->mime, array()); // Set the appSeqNr so that tracking tab can be updated for meeting request updates // @see http://jira.zarafa.com/browse/ZP-68 $meetingRequestProps = MAPIMapping::GetMeetingRequestProperties(); $meetingRequestProps = getPropIdsFromStrings($this->store, $meetingRequestProps); $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS, $meetingRequestProps["goidtag"], $sendMailProps["internetcpid"])); if (stripos($props[PR_MESSAGE_CLASS], "IPM.Schedule.Meeting.Resp.") === 0) { // search for calendar items using goid $mr = new Meetingrequest($this->store, $mapimessage); $appointments = $mr->findCalendarItems($props[$meetingRequestProps["goidtag"]]); if (is_array($appointments) && !empty($appointments)) { $app = mapi_msgstore_openentry($this->store, $appointments[0]); $appprops = mapi_getprops($app, array($meetingRequestProps["appSeqNr"])); if (isset($appprops[$meetingRequestProps["appSeqNr"]]) && $appprops[$meetingRequestProps["appSeqNr"]]) { $mapiprops[$meetingRequestProps["appSeqNr"]] = $appprops[$meetingRequestProps["appSeqNr"]]; ZLog::Write(LOGLEVEL_DEBUG, sprintf("Set sequence number to:%d", $appprops[$meetingRequestProps["appSeqNr"]])); } } } // Delete the PR_SENT_REPRESENTING_* properties because some android devices // do not send neither From nor Sender header causing empty PR_SENT_REPRESENTING_NAME and // PR_SENT_REPRESENTING_EMAIL_ADDRESS properties and "broken" PR_SENT_REPRESENTING_ENTRYID // which results in spooler not being able to send the message. // @see http://jira.zarafa.com/browse/ZP-85 mapi_deleteprops($mapimessage, array($sendMailProps["sentrepresentingname"], $sendMailProps["sentrepresentingemail"], $sendMailProps["representingentryid"], $sendMailProps["sentrepresentingaddt"], $sendMailProps["sentrepresentinsrchk"])); if (isset($sm->source->itemid) && $sm->source->itemid) { // answering an email in a public/shared folder if (!$this->Setup(ZPush::GetAdditionalSyncFolderStore($sm->source->folderid))) { throw new StatusException(sprintf("ZarafaBackend->SendMail() could not Setup() the backend for folder id '%s'", $sm->source->folderid), SYNC_COMMONSTATUS_SERVERERROR); } $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid)); if ($entryid) { $fwmessage = mapi_msgstore_openentry($this->store, $entryid); } if (!isset($fwmessage) || !$fwmessage) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Could not open message id '%s' in folder id '%s' to be replied/forwarded: 0x%X", $sm->source->itemid, $sm->source->folderid, mapi_last_hresult()), SYNC_COMMONSTATUS_ITEMNOTFOUND); } //update icon when forwarding or replying message if ($sm->forwardflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 262)); } elseif ($sm->replyflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 261)); } mapi_savechanges($fwmessage); // only attach the original message if the mobile does not send it itself if (!isset($sm->replacemime)) { // get message's body in order to append forward or reply text $body = MAPIUtils::readPropStream($mapimessage, PR_BODY); $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML); $cpid = mapi_getprops($fwmessage, array($sendMailProps["internetcpid"])); if ($sm->forwardflag) { // attach the original attachments to the outgoing message $this->copyAttachments($mapimessage, $fwmessage); } // regarding the conversion @see ZP-470 if (strlen($body) > 0) { $fwbody = MAPIUtils::readPropStream($fwmessage, PR_BODY); // if both cpids are set convert from the existing charset to the new charset if (isset($cpid[$sendMailProps["internetcpid"]]) && isset($props[$sendMailProps["internetcpid"]])) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert plain forwarded message charset (both set) from '%s' to '%s'", $cpid[$sendMailProps["internetcpid"]], $props[$sendMailProps["internetcpid"]])); $fwbody = Utils::ConvertCodepage($cpid[$sendMailProps["internetcpid"]], $props[$sendMailProps["internetcpid"]], $fwbody); } elseif (isset($cpid[$sendMailProps["internetcpid"]])) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert plain forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]])); $fwbody = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbody); } else { ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): no charset conversion done for plain forwarded message"); $fwbody = w2u($fwbody); } $mapiprops[$sendMailProps["body"]] = $body . "\r\n\r\n" . $fwbody; } if (strlen($bodyHtml) > 0) { $fwbodyHtml = MAPIUtils::readPropStream($fwmessage, PR_HTML); if (isset($cpid[$sendMailProps["internetcpid"]]) && isset($props[$sendMailProps["internetcpid"]])) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert html forwarded message charset (both set) from '%s' to '%s'", $cpid[$sendMailProps["internetcpid"]], $props[$sendMailProps["internetcpid"]])); $fwbodyHtml = Utils::ConvertCodepage($cpid[$sendMailProps["internetcpid"]], $props[$sendMailProps["internetcpid"]], $fwbodyHtml); } elseif (isset($cpid[$sendMailProps["internetcpid"]])) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert html forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]])); $fwbodyHtml = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbodyHtml); } else { ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): no charset conversion done for html forwarded message"); $fwbodyHtml = w2u($fwbodyHtml); } $mapiprops[$sendMailProps["html"]] = $bodyHtml . "<br><br>" . $fwbodyHtml; } } } mapi_setprops($mapimessage, $mapiprops); mapi_message_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); $hr = mapi_last_hresult(); if ($hr) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Error saving/submitting the message to the Outbox: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED); } ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): email submitted"); return true; } $mapiprops[$sendMailProps["subject"]] = u2wi(isset($message->headers["subject"]) ? $message->headers["subject"] : ""); $mapiprops[$sendMailProps["messageclass"]] = "IPM.Note"; $mapiprops[$sendMailProps["deliverytime"]] = time(); if (isset($message->headers["x-priority"])) { $this->getImportanceAndPriority($message->headers["x-priority"], $mapiprops, $sendMailProps); } $this->addRecipients($message->headers, $mapimessage); // Loop through message subparts. $body = ""; $body_html = ""; if ($message->ctype_primary == "multipart" && ($message->ctype_secondary == "mixed" || $message->ctype_secondary == "alternative")) { $mparts = $message->parts; for ($i = 0; $i < count($mparts); $i++) { $part = $mparts[$i]; // palm pre & iPhone send forwarded messages in another subpart which are also parsed if ($part->ctype_primary == "multipart" && ($part->ctype_secondary == "mixed" || $part->ctype_secondary == "alternative" || $part->ctype_secondary == "related")) { foreach ($part->parts as $spart) { $mparts[] = $spart; } continue; } // standard body if ($part->ctype_primary == "text" && $part->ctype_secondary == "plain" && isset($part->body) && (!isset($part->disposition) || $part->disposition != "attachment")) { $body .= u2wi($part->body); // assume only one text body } elseif ($part->ctype_primary == "text" && $part->ctype_secondary == "html") { $body_html .= u2wi($part->body); } elseif ($part->ctype_primary == "ms-tnef" || $part->ctype_secondary == "ms-tnef") { if (!isset($tnefAndIcalProps)) { $tnefAndIcalProps = MAPIMapping::GetTnefAndIcalProperties(); $tnefAndIcalProps = getPropIdsFromStrings($this->store, $tnefAndIcalProps); } require_once 'tnefparser.php'; $zptnef = new TNEFParser($this->store, $tnefAndIcalProps); $zptnef->ExtractProps($part->body, $mapiprops); if (is_array($mapiprops) && !empty($mapiprops)) { //check if it is a recurring item if (isset($mapiprops[$tnefAndIcalProps["tnefrecurr"]])) { MAPIUtils::handleRecurringItem($mapiprops, $tnefAndIcalProps); } } else { ZLog::Write(LOGLEVEL_WARN, "ZarafaBackend->Sendmail(): TNEFParser: Mapi property array was empty"); } } elseif ($part->ctype_primary == "text" && $part->ctype_secondary == "calendar") { if (!isset($tnefAndIcalProps)) { $tnefAndIcalProps = MAPIMapping::GetTnefAndIcalProperties(); $tnefAndIcalProps = getPropIdsFromStrings($this->store, $tnefAndIcalProps); } require_once 'icalparser.php'; $zpical = new ICalParser($this->store, $tnefAndIcalProps); $zpical->ExtractProps($part->body, $mapiprops); // iPhone sends a second ICS which we ignore if we can if (!isset($mapiprops[PR_MESSAGE_CLASS]) && strlen(trim($body)) == 0) { ZLog::Write(LOGLEVEL_WARN, "ZarafaBackend->Sendmail(): Secondary iPhone response is being ignored!! Mail dropped!"); return true; } if (!Utils::CheckMapiExtVersion("6.30") && is_array($mapiprops) && !empty($mapiprops)) { mapi_setprops($mapimessage, $mapiprops); } else { // store ics as attachment //see Utils::IcalTimezoneFix() in utils.php for more information $part->body = Utils::IcalTimezoneFix($part->body); MAPIUtils::StoreAttachment($mapimessage, $part); ZLog::Write(LOGLEVEL_INFO, "ZarafaBackend->Sendmail(): Sending ICS file as attachment"); } } else { MAPIUtils::StoreAttachment($mapimessage, $part); } } } else { if ($message->ctype_primary == "text" && $message->ctype_secondary == "html") { $body_html .= u2wi($message->body); } else { $body = u2wi($message->body); } } // some devices only transmit a html body if (strlen($body) == 0 && strlen($body_html) > 0) { ZLog::Write(LOGLEVEL_WARN, "ZarafaBackend->SendMail(): only html body sent, transformed into plain text"); $body = strip_tags($body_html); } if (isset($sm->source->itemid) && $sm->source->itemid) { // Append the original text body for reply/forward $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid)); if ($entryid) { $fwmessage = mapi_msgstore_openentry($this->store, $entryid); } if (!isset($fwmessage) || !$fwmessage) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Could not open message id '%s' in folder id '%s' to be replied/forwarded: 0x%X", $sm->source->itemid, $sm->source->folderid, mapi_last_hresult()), SYNC_COMMONSTATUS_ITEMNOTFOUND); } //update icon when forwarding or replying message if ($sm->forwardflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 262)); } elseif ($sm->replyflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 261)); } mapi_savechanges($fwmessage); // only attach the original message if the mobile does not send it itself if (!isset($sm->replacemime)) { $fwbody = MAPIUtils::readPropStream($fwmessage, PR_BODY); $fwbodyHtml = MAPIUtils::readPropStream($fwmessage, PR_HTML); if ($sm->forwardflag) { // During a forward, we have to add the forward header ourselves. This is because // normally the forwarded message is added as an attachment. However, we don't want this // because it would be rather complicated to copy over the entire original message due // to the lack of IMessage::CopyTo .. $fwheader = $this->getForwardHeaders($fwmessage); // add fwheader to body and body_html $body .= $fwheader; if (strlen($body_html) > 0) { $body_html .= str_ireplace("\r\n", "<br>", $fwheader); } // attach the original attachments to the outgoing message $this->copyAttachments($mapimessage, $fwmessage); } if (strlen($body) > 0) { $body .= $fwbody; } if (strlen($body_html) > 0) { $body_html .= $fwbodyHtml; } } } //set PR_INTERNET_CPID to 65001 (utf-8) if store supports it and to 1252 otherwise $internetcpid = INTERNET_CPID_WINDOWS1252; if (defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE == true) { $internetcpid = INTERNET_CPID_UTF8; } $mapiprops[$sendMailProps["body"]] = $body; $mapiprops[$sendMailProps["internetcpid"]] = $internetcpid; if (strlen($body_html) > 0) { $mapiprops[$sendMailProps["html"]] = $body_html; } //TODO if setting all properties fails, try setting them infividually like in mapiprovider mapi_setprops($mapimessage, $mapiprops); mapi_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Error saving/submitting the message to the Outbox: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED); } ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): email submitted"); return true; }
/** * Handles the GetItemEstimate command * Returns an estimation of how many items will be synchronized at the next sync * This is mostly used to show something in the progress bar * * @param int $commandCode * * @access public * @return boolean */ public function Handle($commandCode) { $sc = new SyncCollections(); if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE)) { return false; } if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERS)) { return false; } while (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDER)) { $spa = new SyncParameters(); $spastatus = false; if (Request::GetProtocolVersion() >= 14.0) { if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) { try { $spa->SetSyncKey(self::$decoder->getElementContent()); } catch (StateInvalidException $siex) { $spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED; } if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) { $spa->SetFolderId(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } // conversation mode requested if (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) { $spa->SetConversationMode(true); if (($conversationmode = self::$decoder->getElementContent()) !== false) { $spa->SetConversationMode((bool) $conversationmode); if (!self::$decoder->getElementEndTag()) { return false; } } } if (self::$decoder->getElementStartTag(SYNC_OPTIONS)) { while (1) { if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) { $spa->SetFilterType(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) { $spa->SetContentClass(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_MAXITEMS)) { $spa->SetWindowSize($maxitems = self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } $e = self::$decoder->peek(); if ($e[EN_TYPE] == EN_TYPE_ENDTAG) { self::$decoder->getElementEndTag(); break; } } } } else { //get items estimate does not necessarily send the folder type if (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERTYPE)) { $spa->SetContentClass(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) { $spa->SetFolderId(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } if (!self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) { return false; } $spa->SetFilterType(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } if (!self::$decoder->getElementStartTag(SYNC_SYNCKEY)) { return false; } try { $spa->SetSyncKey(self::$decoder->getElementContent()); } catch (StateInvalidException $siex) { $spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED; } if (!self::$decoder->getElementEndTag()) { return false; } } if (!self::$decoder->getElementEndTag()) { return false; } //SYNC_GETITEMESTIMATE_FOLDER // Process folder data //In AS 14 request only collectionid is sent, without class if (!$spa->HasContentClass() && $spa->HasFolderId()) { try { $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId())); } catch (NoHierarchyCacheAvailableException $nhca) { $spastatus = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID; } } // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy() if (!$spa->HasFolderId() && $spa->HasContentClass()) { $spa->SetFolderId(self::$deviceManager->GetFolderIdFromCacheByClass($spa->GetContentClass())); } // Add collection to SC and load state $sc->AddCollection($spa); if ($spastatus) { // the CPO has a folder id now, so we can set the status $sc->AddParameter($spa, "status", $spastatus); } else { try { $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey())); // if this is an additional folder the backend has to be setup correctly if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) { throw new StatusException(sprintf("HandleGetItemEstimate() could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), SYNC_GETITEMESTSTATUS_COLLECTIONINVALID); } } catch (StateNotFoundException $snfex) { // ok, the key is invalid. Question is, if the hierarchycache is still ok //if not, we have to issue SYNC_GETITEMESTSTATUS_COLLECTIONINVALID which triggers a FolderSync try { self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()); // we got here, so the HierarchyCache is ok $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCKKEYINVALID); } catch (NoHierarchyCacheAvailableException $nhca) { $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID); } self::$topCollector->AnnounceInformation("StateNotFoundException " . $sc->GetParameter($spa, "status"), true); } catch (StatusException $stex) { if ($stex->getCode() == SYNC_GETITEMESTSTATUS_COLLECTIONINVALID) { $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID); } else { $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED); } self::$topCollector->AnnounceInformation("StatusException " . $sc->GetParameter($spa, "status"), true); } } } if (!self::$decoder->getElementEndTag()) { return false; } //SYNC_GETITEMESTIMATE_FOLDERS if (!self::$decoder->getElementEndTag()) { return false; } //SYNC_GETITEMESTIMATE_GETITEMESTIMATE self::$encoder->startWBXML(); self::$encoder->startTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE); $status = SYNC_GETITEMESTSTATUS_SUCCESS; // look for changes in all collections try { $sc->CountChanges(); } catch (StatusException $ste) { $status = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID; } $changes = $sc->GetChangedFolderIds(); foreach ($sc as $folderid => $spa) { self::$encoder->startTag(SYNC_GETITEMESTIMATE_RESPONSE); if ($sc->GetParameter($spa, "status")) { $status = $sc->GetParameter($spa, "status"); } self::$encoder->startTag(SYNC_GETITEMESTIMATE_STATUS); self::$encoder->content($status); self::$encoder->endTag(); self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDER); self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERTYPE); self::$encoder->content($spa->GetContentClass()); self::$encoder->endTag(); self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERID); self::$encoder->content($spa->GetFolderId()); self::$encoder->endTag(); if (isset($changes[$folderid]) && $changes[$folderid] !== false) { self::$encoder->startTag(SYNC_GETITEMESTIMATE_ESTIMATE); self::$encoder->content($changes[$folderid]); self::$encoder->endTag(); if ($changes[$folderid] > 0) { self::$topCollector->AnnounceInformation(sprintf("%s %d changes", $spa->GetContentClass(), $changes[$folderid]), true); } } self::$encoder->endTag(); self::$encoder->endTag(); } if (array_sum($changes) == 0) { self::$topCollector->AnnounceInformation("No changes found", true); } self::$encoder->endTag(); return true; }
/** * Imports a move of a message. This occurs when a user moves an item to another folder. * * @param string $id * @param string $newfolder destination folder * * @access public * @return boolean * @throws StatusException */ public function ImportMessageMove($id, $newfolder) { $this->didMove = true; // When we setup the $current importer, we didn't know what we needed to do, so we look only at the src folder for permissions. // Now the $newfolder could be read only as well. So we need to check it's permissions and then switch to a ReplyBackImExporter if it's r/o. if (!$this->isReplyBackExporter()) { // check if the user has permissions on the destination folder $dststore = self::$backend->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder); if (!self::$backend->HasSecretaryACLs($dststore, $newfolder)) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("KopanoChangesWrapper->ImportMessageMove(): destination folderid '%s' is missing permissions. Switching to ReplyBackImExporter.", Utils::PrintAsString($newfolder))); $this->replyback = $this->getReplyBackImExporter(); $this->current = $this->replyback; $this->current->SetMoveStates($this->moveSrcState, $this->moveDstState); if (isset($this->state)) { $this->current->Config($this->state->GetReplyBackState()); } } } return $this->current->ImportMessageMove($id, $newfolder); }
/** * Imports a move of a message. This occurs when a user moves an item to another folder * * Normally, we would implement this via the 'offical' importmessagemove() function on the ICS importer, * but the Zarafa/Kopano importer does not support this. Therefore we currently implement it via a standard mapi * call. This causes a mirror 'add/delete' to be sent to the PDA at the next sync. * Manfred, 2010-10-21. For some mobiles import was causing duplicate messages in the destination folder * (Mantis #202). Therefore we will create a new message in the destination folder, copy properties * of the source message to the new one and then delete the source message. * * @param string $id * @param string $newfolder destination folder * * @access public * @return boolean * @throws StatusException */ public function ImportMessageMove($id, $newfolder) { list(, $sk) = Utils::SplitMessageId($id); if (strtolower($newfolder) == strtolower(bin2hex($this->folderid))) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST); } // Get the entryid of the message we're moving $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($sk)); $srcmessage = false; if ($entryid) { //open the source message $srcmessage = mapi_msgstore_openentry($this->store, $entryid); } if (!$entryid || !$srcmessage) { $code = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID; // if we move to the trash and the source message is not found, we can also just tell the mobile that we successfully moved to avoid errors (ZP-624) if ($newfolder == ZPush::GetBackend()->GetWasteBasket()) { $code = SYNC_MOVEITEMSSTATUS_SUCCESS; } $errorCase = !$entryid ? "resolve source message id" : "open source message"; throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to %s: 0x%X", $sk, $newfolder, $errorCase, mapi_last_hresult()), $code); } // check if the source message is in the current syncinterval if (!$this->isMessageInSyncInterval($sk)) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Source message is outside the sync interval. Move not performed.", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // get correct mapi store for the destination folder $dststore = ZPush::GetBackend()->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder); if ($dststore === false) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open store of destination folder", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $dstentryid = mapi_msgstore_entryidfromsourcekey($dststore, hex2bin($newfolder)); if (!$dstentryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve destination folder", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $dstfolder = mapi_msgstore_openentry($dststore, $dstentryid); if (!$dstfolder) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open destination folder", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } $newmessage = mapi_folder_createmessage($dstfolder); if (!$newmessage) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to create message in destination folder: 0x%X", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); } // Copy message mapi_copyto($srcmessage, array(), array(), $newmessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, copy to destination message failed: 0x%X", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } $srcfolderentryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid); if (!$srcfolderentryid) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source folder", $sk, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } $srcfolder = mapi_msgstore_openentry($this->store, $srcfolderentryid); if (!$srcfolder) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source folder: 0x%X", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // Save changes mapi_savechanges($newmessage); if (mapi_last_hresult()) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, mapi_savechanges() failed: 0x%X", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); } // Delete the old message if (!mapi_folder_deletemessages($srcfolder, array($entryid))) { throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, delete of source message failed: 0x%X. Possible duplicates.", $sk, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_SOURCEORDESTLOCKED); } $sourcekeyprops = mapi_getprops($newmessage, array(PR_SOURCE_KEY)); if (isset($sourcekeyprops[PR_SOURCE_KEY]) && $sourcekeyprops[PR_SOURCE_KEY]) { $prefix = ""; // prepend the destination short folderid, if it exists $destShortId = ZPush::GetDeviceManager()->GetFolderIdForBackendId($newfolder); if ($destShortId !== $newfolder) { $prefix = $destShortId . ":"; } return $prefix . bin2hex($sourcekeyprops[PR_SOURCE_KEY]); } return false; }
/** * Sends an e-mail * This messages needs to be saved into the 'sent items' folder * * @param SyncSendMail $sm SyncSendMail object * * @access public * @return boolean * @throws StatusException */ public function SendMail($sm) { // Check if imtomapi function is available and use it to send the mime message. // It is available since ZCP 7.0.6 // @see http://jira.zarafa.com/browse/ZCP-9508 if (!(function_exists('mapi_feature') && mapi_feature('INETMAPI_IMTOMAPI'))) { throw new StatusException("ZarafaBackend->SendMail(): ZCP version is too old, INETMAPI_IMTOMAPI is not available. Install at least ZCP version 7.0.6 or later.", SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED, null, LOGLEVEL_FATAL); return false; } ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): RFC822: %d bytes forward-id: '%s' reply-id: '%s' parent-id: '%s' SaveInSent: '%s' ReplaceMIME: '%s'", strlen($sm->mime), Utils::PrintAsString($sm->forwardflag), Utils::PrintAsString($sm->replyflag), Utils::PrintAsString(isset($sm->source->folderid) ? $sm->source->folderid : false), Utils::PrintAsString($sm->saveinsent), Utils::PrintAsString(isset($sm->replacemime)))); // by splitting the message in several lines we can easily grep later foreach (preg_split("/((\r)?\n)/", $sm->mime) as $rfc822line) { ZLog::Write(LOGLEVEL_WBXML, "RFC822: " . $rfc822line); } $sendMailProps = MAPIMapping::GetSendMailProperties(); $sendMailProps = getPropIdsFromStrings($this->store, $sendMailProps); // Open the outbox and create the message there $storeprops = mapi_getprops($this->store, array($sendMailProps["outboxentryid"], $sendMailProps["ipmsentmailentryid"])); if (isset($storeprops[$sendMailProps["outboxentryid"]])) { $outbox = mapi_msgstore_openentry($this->store, $storeprops[$sendMailProps["outboxentryid"]]); } if (!$outbox) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): No Outbox found or unable to create message: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_SERVERERROR); } $mapimessage = mapi_folder_createmessage($outbox); //message properties to be set $mapiprops = array(); // only save the outgoing in sent items folder if the mobile requests it $mapiprops[$sendMailProps["sentmailentryid"]] = $storeprops[$sendMailProps["ipmsentmailentryid"]]; ZLog::Write(LOGLEVEL_DEBUG, "Use the mapi_inetmapi_imtomapi function"); $ab = mapi_openaddressbook($this->session); mapi_inetmapi_imtomapi($this->session, $this->store, $ab, $mapimessage, $sm->mime, array()); // Set the appSeqNr so that tracking tab can be updated for meeting request updates // @see http://jira.zarafa.com/browse/ZP-68 $meetingRequestProps = MAPIMapping::GetMeetingRequestProperties(); $meetingRequestProps = getPropIdsFromStrings($this->store, $meetingRequestProps); $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS, $meetingRequestProps["goidtag"], $sendMailProps["internetcpid"])); // Convert sent message's body to UTF-8. // @see http://jira.zarafa.com/browse/ZP-505 if (isset($props[$sendMailProps["internetcpid"]]) && $props[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sent email cpid is not unicode (%d). Set it to unicode and convert email body.", $props[$sendMailProps["internetcpid"]])); $mapiprops[$sendMailProps["internetcpid"]] = INTERNET_CPID_UTF8; $body = MAPIUtils::readPropStream($mapimessage, PR_BODY); $body = Utils::ConvertCodepageStringToUtf8($props[$sendMailProps["internetcpid"]], $body); $mapiprops[$sendMailProps["body"]] = $body; $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML); $bodyHtml = Utils::ConvertCodepageStringToUtf8($props[$sendMailProps["internetcpid"]], $bodyHtml); $mapiprops[$sendMailProps["html"]] = $bodyHtml; mapi_setprops($mapimessage, $mapiprops); } if (stripos($props[PR_MESSAGE_CLASS], "IPM.Schedule.Meeting.Resp.") === 0) { // search for calendar items using goid $mr = new Meetingrequest($this->store, $mapimessage); $appointments = $mr->findCalendarItems($props[$meetingRequestProps["goidtag"]]); if (is_array($appointments) && !empty($appointments)) { $app = mapi_msgstore_openentry($this->store, $appointments[0]); $appprops = mapi_getprops($app, array($meetingRequestProps["appSeqNr"])); if (isset($appprops[$meetingRequestProps["appSeqNr"]]) && $appprops[$meetingRequestProps["appSeqNr"]]) { $mapiprops[$meetingRequestProps["appSeqNr"]] = $appprops[$meetingRequestProps["appSeqNr"]]; ZLog::Write(LOGLEVEL_DEBUG, sprintf("Set sequence number to:%d", $appprops[$meetingRequestProps["appSeqNr"]])); } } } // Delete the PR_SENT_REPRESENTING_* properties because some android devices // do not send neither From nor Sender header causing empty PR_SENT_REPRESENTING_NAME and // PR_SENT_REPRESENTING_EMAIL_ADDRESS properties and "broken" PR_SENT_REPRESENTING_ENTRYID // which results in spooler not being able to send the message. // @see http://jira.zarafa.com/browse/ZP-85 mapi_deleteprops($mapimessage, array($sendMailProps["sentrepresentingname"], $sendMailProps["sentrepresentingemail"], $sendMailProps["representingentryid"], $sendMailProps["sentrepresentingaddt"], $sendMailProps["sentrepresentinsrchk"])); if (isset($sm->source->itemid) && $sm->source->itemid) { // answering an email in a public/shared folder if (!$this->Setup(ZPush::GetAdditionalSyncFolderStore($sm->source->folderid))) { throw new StatusException(sprintf("ZarafaBackend->SendMail() could not Setup() the backend for folder id '%s'", $sm->source->folderid), SYNC_COMMONSTATUS_SERVERERROR); } $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid)); if ($entryid) { $fwmessage = mapi_msgstore_openentry($this->store, $entryid); } if (!isset($fwmessage) || !$fwmessage) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Could not open message id '%s' in folder id '%s' to be replied/forwarded: 0x%X", $sm->source->itemid, $sm->source->folderid, mapi_last_hresult()), SYNC_COMMONSTATUS_ITEMNOTFOUND); } //update icon when forwarding or replying message if ($sm->forwardflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 262)); } elseif ($sm->replyflag) { mapi_setprops($fwmessage, array(PR_ICON_INDEX => 261)); } mapi_savechanges($fwmessage); // only attach the original message if the mobile does not send it itself if (!isset($sm->replacemime)) { // get message's body in order to append forward or reply text if (!isset($body)) { $body = MAPIUtils::readPropStream($mapimessage, PR_BODY); } if (!isset($bodyHtml)) { $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML); } $cpid = mapi_getprops($fwmessage, array($sendMailProps["internetcpid"])); if ($sm->forwardflag) { // attach the original attachments to the outgoing message $this->copyAttachments($mapimessage, $fwmessage); } // regarding the conversion @see ZP-470 if (strlen($body) > 0) { $fwbody = MAPIUtils::readPropStream($fwmessage, PR_BODY); // if only the old message's cpid is set, convert from old charset to utf-8 if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert plain forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]])); $fwbody = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbody); } else { ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): no charset conversion done for plain forwarded message"); $fwbody = w2u($fwbody); } $mapiprops[$sendMailProps["body"]] = $body . "\r\n\r\n" . $fwbody; } if (strlen($bodyHtml) > 0) { $fwbodyHtml = MAPIUtils::readPropStream($fwmessage, PR_HTML); // if only new message's cpid is set, convert to UTF-8 if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert html forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]])); $fwbodyHtml = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbodyHtml); } else { ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): no charset conversion done for html forwarded message"); $fwbodyHtml = w2u($fwbodyHtml); } $mapiprops[$sendMailProps["html"]] = $bodyHtml . "<br><br>" . $fwbodyHtml; } } } mapi_setprops($mapimessage, $mapiprops); mapi_message_savechanges($mapimessage); mapi_message_submitmessage($mapimessage); $hr = mapi_last_hresult(); if ($hr) { throw new StatusException(sprintf("ZarafaBackend->SendMail(): Error saving/submitting the message to the Outbox: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED); } ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): email submitted"); return true; }
/** * Handles the GetItemEstimate command * Returns an estimation of how many items will be synchronized at the next sync * This is mostly used to show something in the progress bar * * @param int $commandCode * * @access public * @return boolean */ public function Handle($commandCode) { $sc = new SyncCollections(); if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE)) { return false; } if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERS)) { return false; } while (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDER)) { $spa = new SyncParameters(); $spastatus = false; // read the folder properties WBXMLDecoder::ResetInWhile("getItemEstimateFolders"); while (WBXMLDecoder::InWhile("getItemEstimateFolders")) { if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) { try { $spa->SetSyncKey(self::$decoder->getElementContent()); } catch (StateInvalidException $siex) { $spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED; } if (!self::$decoder->getElementEndTag()) { return false; } } elseif (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) { $fid = self::$decoder->getElementContent(); $spa->SetFolderId($fid); $spa->SetBackendFolderId(self::$deviceManager->GetBackendIdForFolderId($fid)); if (!self::$decoder->getElementEndTag()) { return false; } } elseif (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) { $spa->SetConversationMode(true); if (($conversationmode = self::$decoder->getElementContent()) !== false) { $spa->SetConversationMode((bool) $conversationmode); if (!self::$decoder->getElementEndTag()) { return false; } } } elseif (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERTYPE)) { $spa->SetContentClass(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } elseif (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) { $spa->SetFilterType(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } while (self::$decoder->getElementStartTag(SYNC_OPTIONS)) { WBXMLDecoder::ResetInWhile("getItemEstimateOptions"); while (WBXMLDecoder::InWhile("getItemEstimateOptions")) { $firstOption = true; // foldertype definition if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) { $foldertype = self::$decoder->getElementContent(); ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleGetItemEstimate(): specified options block with foldertype '%s'", $foldertype)); // switch the foldertype for the next options $spa->UseCPO($foldertype); // set to synchronize all changes. The mobile could overwrite this value $spa->SetFilterType(SYNC_FILTERTYPE_ALL); if (!self::$decoder->getElementEndTag()) { return false; } } else { if ($firstOption) { $spa->UseCPO(); // set to synchronize all changes. The mobile could overwrite this value $spa->SetFilterType(SYNC_FILTERTYPE_ALL); } } $firstOption = false; if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) { $spa->SetFilterType(self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_MAXITEMS)) { $spa->SetWindowSize($maxitems = self::$decoder->getElementContent()); if (!self::$decoder->getElementEndTag()) { return false; } } $e = self::$decoder->peek(); if ($e[EN_TYPE] == EN_TYPE_ENDTAG) { self::$decoder->getElementEndTag(); break; } } } $e = self::$decoder->peek(); if ($e[EN_TYPE] == EN_TYPE_ENDTAG) { self::$decoder->getElementEndTag(); //SYNC_GETITEMESTIMATE_FOLDER break; } } // Process folder data //In AS 14 request only collectionid is sent, without class if (!$spa->HasContentClass() && $spa->HasFolderId()) { try { $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId())); } catch (NoHierarchyCacheAvailableException $nhca) { $spastatus = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID; } } // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy() if (!$spa->HasFolderId() && $spa->HasContentClass()) { $spa->SetFolderId(self::$deviceManager->GetFolderIdFromCacheByClass($spa->GetContentClass())); } // Add collection to SC and load state $sc->AddCollection($spa); if ($spastatus) { // the CPO has a folder id now, so we can set the status $sc->AddParameter($spa, "status", $spastatus); } else { try { $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey())); // if this is an additional folder the backend has to be setup correctly if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()))) { throw new StatusException(sprintf("HandleGetItemEstimate() could not Setup() the backend for folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_GETITEMESTSTATUS_COLLECTIONINVALID); } } catch (StateNotFoundException $snfex) { // ok, the key is invalid. Question is, if the hierarchycache is still ok //if not, we have to issue SYNC_GETITEMESTSTATUS_COLLECTIONINVALID which triggers a FolderSync try { self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()); // we got here, so the HierarchyCache is ok $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCKKEYINVALID); } catch (NoHierarchyCacheAvailableException $nhca) { $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID); } self::$topCollector->AnnounceInformation("StateNotFoundException " . $sc->GetParameter($spa, "status"), true); } catch (StatusException $stex) { if ($stex->getCode() == SYNC_GETITEMESTSTATUS_COLLECTIONINVALID) { $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID); } else { $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED); } self::$topCollector->AnnounceInformation("StatusException " . $sc->GetParameter($spa, "status"), true); } } } if (!self::$decoder->getElementEndTag()) { return false; } //SYNC_GETITEMESTIMATE_FOLDERS if (!self::$decoder->getElementEndTag()) { return false; } //SYNC_GETITEMESTIMATE_GETITEMESTIMATE self::$encoder->startWBXML(); self::$encoder->startTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE); $status = SYNC_GETITEMESTSTATUS_SUCCESS; // look for changes in all collections try { $sc->CountChanges(); } catch (StatusException $ste) { $status = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID; } $changes = $sc->GetChangedFolderIds(); foreach ($sc as $folderid => $spa) { self::$encoder->startTag(SYNC_GETITEMESTIMATE_RESPONSE); if ($sc->GetParameter($spa, "status")) { $status = $sc->GetParameter($spa, "status"); } self::$encoder->startTag(SYNC_GETITEMESTIMATE_STATUS); self::$encoder->content($status); self::$encoder->endTag(); self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDER); self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERTYPE); self::$encoder->content($spa->GetContentClass()); self::$encoder->endTag(); self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERID); self::$encoder->content($spa->GetFolderId()); self::$encoder->endTag(); if (isset($changes[$folderid]) && $changes[$folderid] !== false) { self::$encoder->startTag(SYNC_GETITEMESTIMATE_ESTIMATE); self::$encoder->content($changes[$folderid]); self::$encoder->endTag(); if ($changes[$folderid] > 0) { self::$topCollector->AnnounceInformation(sprintf("%s %d changes", $spa->GetContentClass(), $changes[$folderid]), true); } // update the device data to mark folders as complete when synching with WM if ($changes[$folderid] == 0) { self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_COMPLETED); } } self::$encoder->endTag(); self::$encoder->endTag(); } if (array_sum($changes) == 0) { self::$topCollector->AnnounceInformation("No changes found", true); } self::$encoder->endTag(); return true; }
/** * Handles the MoveItems command * * @param int $commandCode * * @access public * @return boolean */ public function Handle($commandCode) { if (!self::$decoder->getElementStartTag(SYNC_MOVE_MOVES)) { return false; } $moves = array(); while (self::$decoder->getElementStartTag(SYNC_MOVE_MOVE)) { $move = array(); if (self::$decoder->getElementStartTag(SYNC_MOVE_SRCMSGID)) { $move["srcmsgid"] = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { break; } } if (self::$decoder->getElementStartTag(SYNC_MOVE_SRCFLDID)) { $move["srcfldid"] = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { break; } } if (self::$decoder->getElementStartTag(SYNC_MOVE_DSTFLDID)) { $move["dstfldid"] = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { break; } } array_push($moves, $move); if (!self::$decoder->getElementEndTag()) { return false; } } if (!self::$decoder->getElementEndTag()) { return false; } self::$encoder->StartWBXML(); self::$encoder->startTag(SYNC_MOVE_MOVES); foreach ($moves as $move) { self::$encoder->startTag(SYNC_MOVE_RESPONSE); self::$encoder->startTag(SYNC_MOVE_SRCMSGID); self::$encoder->content($move["srcmsgid"]); self::$encoder->endTag(); $status = SYNC_MOVEITEMSSTATUS_SUCCESS; $result = false; try { $sourceBackendFolderId = self::$deviceManager->GetBackendIdForFolderId($move["srcfldid"]); // if the source folder is an additional folder the backend has to be setup correctly if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($sourceBackendFolderId))) { throw new StatusException(sprintf("HandleMoveItems() could not Setup() the backend for folder id %s/%s", $move["srcfldid"], $sourceBackendFolderId), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } $importer = self::$backend->GetImporter($sourceBackendFolderId); if ($importer === false) { throw new StatusException(sprintf("HandleMoveItems() could not get an importer for folder id %s/%s", $move["srcfldid"], $sourceBackendFolderId), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // get saved SyncParameters of the source folder $spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($move["srcfldid"]); if (!$spa->HasSyncKey()) { throw new StatusException(sprintf("MoveItems(): Source folder id '%s' is not fully synchronized. Unable to perform operation.", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); } // get saved SyncParameters of the destination folder $destSpa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($move["dstfldid"]); if (!$destSpa->HasSyncKey()) { $destSpa->SetFolderId($move["dstfldid"]); $destSpa->SetSyncKey(self::$deviceManager->GetStateManager()->GetZeroSyncKey()); } $importer->SetMoveStates($spa->GetMoveState(), $destSpa->GetMoveState()); $importer->ConfigContentParameters($spa->GetCPO()); $result = $importer->ImportMessageMove($move["srcmsgid"], self::$deviceManager->GetBackendIdForFolderId($move["dstfldid"])); // We discard the standard importer state for now. // Get the move states and save them in the SyncParameters of the src and dst folder list($srcMoveState, $dstMoveState) = $importer->GetMoveStates(); if ($spa->GetMoveState() !== $srcMoveState) { $spa->SetMoveState($srcMoveState); self::$deviceManager->GetStateManager()->SetSynchedFolderState($spa); } if ($destSpa->GetMoveState() !== $dstMoveState) { $destSpa->SetMoveState($dstMoveState); self::$deviceManager->GetStateManager()->SetSynchedFolderState($destSpa); } } catch (StatusException $stex) { if ($stex->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) { // same as SYNC_FSSTATUS_CODEUNKNOWN $status = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID; } else { $status = $stex->getCode(); } } self::$topCollector->AnnounceInformation(sprintf("Operation status: %s", $status), true); self::$encoder->startTag(SYNC_MOVE_STATUS); self::$encoder->content($status); self::$encoder->endTag(); self::$encoder->startTag(SYNC_MOVE_DSTMSGID); self::$encoder->content($result !== false ? $result : $move["srcmsgid"]); self::$encoder->endTag(); self::$encoder->endTag(); } self::$encoder->endTag(); return true; }
/** * Handles the MeetingResponse command * * @param int $commandCode * * @access public * @return boolean */ public function Handle($commandCode) { $requests = array(); if (!self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE)) { return false; } while (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUEST)) { $req = array(); WBXMLDecoder::ResetInWhile("meetingResponseRequest"); while (WBXMLDecoder::InWhile("meetingResponseRequest")) { if (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_USERRESPONSE)) { $req["response"] = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_FOLDERID)) { $req["folderid"] = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { return false; } } if (self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUESTID)) { $req["requestid"] = self::$decoder->getElementContent(); if (!self::$decoder->getElementEndTag()) { return false; } } $e = self::$decoder->peek(); if ($e[EN_TYPE] == EN_TYPE_ENDTAG) { self::$decoder->getElementEndTag(); break; } } array_push($requests, $req); } if (!self::$decoder->getElementEndTag()) { return false; } // output the error code, plus the ID of the calendar item that was generated by the // accept of the meeting response self::$encoder->StartWBXML(); self::$encoder->startTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE); foreach ($requests as $req) { $status = SYNC_MEETRESPSTATUS_SUCCESS; try { $backendFolderId = self::$deviceManager->GetBackendIdForFolderId($req["folderid"]); // if the source folder is an additional folder the backend has to be setup correctly if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($backendFolderId))) { throw new StatusException(sprintf("HandleMoveItems() could not Setup() the backend for folder id %s/%s", $req["folderid"], $backendFolderId), SYNC_MEETRESPSTATUS_SERVERERROR); } $calendarid = self::$backend->MeetingResponse($req["requestid"], $backendFolderId, $req["response"]); if ($calendarid === false) { throw new StatusException("HandleMeetingResponse() not possible", SYNC_MEETRESPSTATUS_SERVERERROR); } } catch (StatusException $stex) { $status = $stex->getCode(); } self::$encoder->startTag(SYNC_MEETINGRESPONSE_RESULT); self::$encoder->startTag(SYNC_MEETINGRESPONSE_REQUESTID); self::$encoder->content($req["requestid"]); self::$encoder->endTag(); self::$encoder->startTag(SYNC_MEETINGRESPONSE_STATUS); self::$encoder->content($status); self::$encoder->endTag(); if ($status == SYNC_MEETRESPSTATUS_SUCCESS && !empty($calendarid)) { self::$encoder->startTag(SYNC_MEETINGRESPONSE_CALENDARID); self::$encoder->content($calendarid); self::$encoder->endTag(); } self::$encoder->endTag(); self::$topCollector->AnnounceInformation(sprintf("Operation status %d", $status), true); } self::$encoder->endTag(); return true; }
/** * Returns the content of the named attachment as stream * * @param string $attname * @access public * @return SyncItemOperationsAttachment * @throws StatusException */ public function GetAttachmentData($attname) { ZLog::Write(LOGLEVEL_DEBUG, sprintf("KopanoBackend->GetAttachmentData('%s')", $attname)); if (!strpos($attname, ":")) { throw new StatusException(sprintf("KopanoBackend->GetAttachmentData('%s'): Error, attachment requested for non-existing item", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); } list($id, $attachnum, $parentEntryid) = explode(":", $attname); if (isset($parentEntryid)) { $this->Setup(ZPush::GetAdditionalSyncFolderStore($parentEntryid)); } $entryid = hex2bin($id); $message = mapi_msgstore_openentry($this->store, $entryid); if (!$message) { throw new StatusException(sprintf("KopanoBackend->GetAttachmentData('%s'): Error, unable to open item for attachment data for id '%s' with: 0x%X", $attname, $id, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); } MAPIUtils::ParseSmime($this->session, $this->defaultstore, $this->getAddressbook(), $message); $attach = mapi_message_openattach($message, $attachnum); if (!$attach) { throw new StatusException(sprintf("KopanoBackend->GetAttachmentData('%s'): Error, unable to open attachment number '%s' with: 0x%X", $attname, $attachnum, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); } // get necessary attachment props $attprops = mapi_getprops($attach, array(PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W, PR_ATTACH_METHOD)); $attachment = new SyncItemOperationsAttachment(); // check if it's an embedded message and open it in such a case if (isset($attprops[PR_ATTACH_METHOD]) && $attprops[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG) { $embMessage = mapi_attach_openobj($attach); $addrbook = $this->getAddressbook(); $stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $embMessage, array('use_tnef' => -1)); // set the default contenttype for this kind of messages $attachment->contenttype = "message/rfc822"; } else { $stream = mapi_openpropertytostream($attach, PR_ATTACH_DATA_BIN); } if (!$stream) { throw new StatusException(sprintf("KopanoBackend->GetAttachmentData('%s'): Error, unable to open attachment data stream: 0x%X", $attname, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); } // put the mapi stream into a wrapper to get a standard stream $attachment->data = MAPIStreamWrapper::Open($stream); if (isset($attprops[PR_ATTACH_MIME_TAG])) { $attachment->contenttype = $attprops[PR_ATTACH_MIME_TAG]; } elseif (isset($attprops[PR_ATTACH_MIME_TAG_W])) { $attachment->contenttype = $attprops[PR_ATTACH_MIME_TAG_W]; } //TODO default contenttype return $attachment; }