Ejemplo n.º 1
0
    if ($ex instanceof AuthenticationRequiredException) {
        ZPush::PrintZPushLegal($exclass, sprintf('<pre>%s</pre>', $ex->getMessage()));
        // log the failed login attemt e.g. for fail2ban
        if (defined('LOGAUTHFAIL') && LOGAUTHFAIL != false) {
            ZLog::Write(LOGLEVEL_WARN, sprintf("IP: %s failed to authenticate user '%s'", Request::GetRemoteAddr(), Request::GetAuthUser() ? Request::GetAuthUser() : Request::GetGETUser()));
        }
    } else {
        if ($ex instanceof WBXMLException) {
            ZLog::Write(LOGLEVEL_FATAL, "Request could not be processed correctly due to a WBXMLException. Please report this.");
        } else {
            if (!$ex instanceof ZPushException || $ex->showLegalNotice()) {
                $cmdinfo = Request::GetCommand() ? sprintf(" processing command <i>%s</i>", Request::GetCommand()) : "";
                $extrace = $ex->getTrace();
                $trace = !empty($extrace) ? "\n\nTrace:\n" . print_r($extrace, 1) : "";
                ZPush::PrintZPushLegal($exclass . $cmdinfo, sprintf('<pre>%s</pre>', $ex->getMessage() . $trace));
            }
        }
    }
    // Announce exception to process loop detection
    if (ZPush::GetDeviceManager(false)) {
        ZPush::GetDeviceManager()->AnnounceProcessException($ex);
    }
    // Announce exception if the TopCollector if available
    ZPush::GetTopCollector()->AnnounceInformation(get_class($ex), true);
}
// save device data if the DeviceManager is available
if (ZPush::GetDeviceManager(false)) {
    ZPush::GetDeviceManager()->Save();
}
// end gracefully
ZLog::Write(LOGLEVEL_DEBUG, '-------- End');
Ejemplo n.º 2
0
 /**
  * Saves the permanent and state related storage data of the user and device
  * if they were loaded previousily
  * If the backend storage is used this should be called
  *
  * @access protected
  * @return
  */
 protected function SaveStorages()
 {
     if (isset($this->permanentStorage)) {
         try {
             ZPush::GetDeviceManager()->GetStateManager()->SetBackendStorage($this->permanentStorage, StateManager::BACKENDSTORAGE_PERMANENT);
         } catch (StateNotYetAvailableException $snyae) {
         } catch (StateNotFoundException $snfe) {
         }
     }
     if (isset($this->stateStorage)) {
         try {
             $this->storage_state = ZPush::GetDeviceManager()->GetStateManager()->SetBackendStorage($this->stateStorage, StateManager::BACKENDSTORAGE_STATE);
         } catch (StateNotYetAvailableException $snyae) {
         } catch (StateNotFoundException $snfe) {
         }
     }
 }
Ejemplo n.º 3
0
 /**
  * Handles the Sync command
  * Performs the synchronization of messages
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     // Contains all requested folders (containers)
     $sc = new SyncCollections();
     $status = SYNC_STATUS_SUCCESS;
     $wbxmlproblem = false;
     $emptysync = false;
     // check if the hierarchySync was fully completed
     if (USE_PARTIAL_FOLDERSYNC) {
         if (self::$deviceManager->GetFolderSyncComplete() === false) {
             ZLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): Sync request aborted, as exporting of folders has not yet completed");
             self::$topCollector->AnnounceInformation("Aborted due incomplete folder sync", true);
             $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
         } else {
             ZLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): FolderSync marked as complete");
         }
     }
     // Start Synchronize
     if (self::$decoder->getElementStartTag(SYNC_SYNCHRONIZE)) {
         // AS 1.0 sends version information in WBXML
         if (self::$decoder->getElementStartTag(SYNC_VERSION)) {
             $sync_version = self::$decoder->getElementContent();
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("WBXML sync version: '%s'", $sync_version));
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         // Synching specified folders
         // Android still sends heartbeat sync even if all syncfolders are disabled.
         // Check if Folders tag is empty (<Folders/>) and only sync if there are
         // some folders in the request. See ZP-172
         $startTag = self::$decoder->getElementStartTag(SYNC_FOLDERS);
         if (isset($startTag[EN_FLAGS]) && $startTag[EN_FLAGS]) {
             while (self::$decoder->getElementStartTag(SYNC_FOLDER)) {
                 $actiondata = array();
                 $actiondata["requested"] = true;
                 $actiondata["clientids"] = array();
                 $actiondata["modifyids"] = array();
                 $actiondata["removeids"] = array();
                 $actiondata["fetchids"] = array();
                 $actiondata["statusids"] = array();
                 // read class, synckey and folderid without SyncParameters Object for now
                 $class = $synckey = $folderid = false;
                 //for AS versions < 2.5
                 if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                     $class = self::$decoder->getElementContent();
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync folder: '%s'", $class));
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 // SyncKey
                 if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
                     $synckey = "0";
                     if (($synckey = self::$decoder->getElementContent()) !== false) {
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 } else {
                     return false;
                 }
                 // FolderId
                 if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
                     $folderid = self::$decoder->getElementContent();
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
                 if (!$folderid && $class) {
                     $folderid = self::$deviceManager->GetFolderIdFromCacheByClass($class);
                 }
                 // folderid HAS TO BE known by now, so we retrieve the correct SyncParameters object for an update
                 try {
                     $spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($folderid);
                     // TODO remove resync of folders for < Z-Push 2 beta4 users
                     // this forces a resync of all states previous to Z-Push 2 beta4
                     if (!$spa instanceof SyncParameters) {
                         throw new StateInvalidException("Saved state are not of type SyncParameters");
                     }
                     // new/resync requested
                     if ($synckey == "0") {
                         $spa->RemoveSyncKey();
                     } else {
                         if ($synckey !== false) {
                             $spa->SetSyncKey($synckey);
                         }
                     }
                 } catch (StateInvalidException $stie) {
                     $spa = new SyncParameters();
                     $status = SYNC_STATUS_INVALIDSYNCKEY;
                     self::$topCollector->AnnounceInformation("State invalid - Resync folder", true);
                     self::$deviceManager->ForceFolderResync($folderid);
                 }
                 // update folderid.. this might be a new object
                 $spa->SetFolderId($folderid);
                 if ($class !== false) {
                     $spa->SetContentClass($class);
                 }
                 // Get class for as versions >= 12.0
                 if (!$spa->HasContentClass()) {
                     try {
                         $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("GetFolderClassFromCacheByID from Device Manager: '%s' for id:'%s'", $spa->GetContentClass(), $spa->GetFolderId()));
                     } catch (NoHierarchyCacheAvailableException $nhca) {
                         $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                         self::$deviceManager->ForceFullResync();
                     }
                 }
                 // done basic SPA initialization/loading -> add to SyncCollection
                 $sc->AddCollection($spa);
                 $sc->AddParameter($spa, "requested", true);
                 if ($spa->HasContentClass()) {
                     self::$topCollector->AnnounceInformation(sprintf("%s request", $spa->GetContentClass()), true);
                 } else {
                     ZLog::Write(LOGLEVEL_WARN, "Not possible to determine class of request. Request did not contain class and apparently there is an issue with the HierarchyCache.");
                 }
                 // SUPPORTED properties
                 if (($se = self::$decoder->getElementStartTag(SYNC_SUPPORTED)) !== false) {
                     // ZP-481: LG phones send an empty supported tag, so only read the contents if available here
                     // if <Supported/> is received, it's as no supported fields would have been sent at all.
                     // unsure if this is the correct approach, or if in this case some default list should be used
                     if ($se[EN_FLAGS] & EN_FLAGS_CONTENT) {
                         $supfields = array();
                         while (1) {
                             $el = self::$decoder->getElement();
                             if ($el[EN_TYPE] == EN_TYPE_ENDTAG) {
                                 break;
                             } else {
                                 $supfields[] = $el[EN_TAG];
                             }
                         }
                         self::$deviceManager->SetSupportedFields($spa->GetFolderId(), $supfields);
                     }
                 }
                 // Deletes as moves can be an empty tag as well as have value
                 if (self::$decoder->getElementStartTag(SYNC_DELETESASMOVES)) {
                     $spa->SetDeletesAsMoves(true);
                     if (($dam = self::$decoder->getElementContent()) !== false) {
                         $spa->SetDeletesAsMoves((bool) $dam);
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 }
                 // Get changes can be an empty tag as well as have value
                 // code block partly contributed by dw2412
                 if (self::$decoder->getElementStartTag(SYNC_GETCHANGES)) {
                     $sc->AddParameter($spa, "getchanges", true);
                     if (($gc = self::$decoder->getElementContent()) !== false) {
                         $sc->AddParameter($spa, "getchanges", $gc);
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                 }
                 if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
                     $ws = self::$decoder->getElementContent();
                     // normalize windowsize - see ZP-477
                     if ($ws == 0 || $ws > 512) {
                         $ws = 512;
                     }
                     $spa->SetWindowSize($ws);
                     // also announce the currently requested window size to the DeviceManager
                     self::$deviceManager->SetWindowSize($spa->GetFolderId(), $spa->GetWindowSize());
                     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;
                         }
                     }
                 }
                 // Do not truncate by default
                 $spa->SetTruncation(SYNC_TRUNCATION_ALL);
                 // use default conflict handling if not specified by the mobile
                 $spa->SetConflict(SYNC_CONFLICT_DEFAULT);
                 while (self::$decoder->getElementStartTag(SYNC_OPTIONS)) {
                     $firstOption = true;
                     while (1) {
                         // foldertype definition
                         if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                             $foldertype = self::$decoder->getElementContent();
                             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): 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_TRUNCATION)) {
                             $spa->SetTruncation(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_RTFTRUNCATION)) {
                             $spa->SetRTFTruncation(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) {
                             $spa->SetMimeSupport(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_MIMETRUNCATION)) {
                             $spa->SetMimeTruncation(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_CONFLICT)) {
                             $spa->SetConflict(self::$decoder->getElementContent());
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) {
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
                                 $bptype = self::$decoder->getElementContent();
                                 $spa->BodyPreference($bptype);
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
                                 $spa->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent());
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
                                 $spa->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent());
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
                                 $spa->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent());
                                 if (!self::$decoder->getElementEndTag()) {
                                     return false;
                                 }
                             }
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         }
                         $e = self::$decoder->peek();
                         if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
                             self::$decoder->getElementEndTag();
                             break;
                         }
                     }
                 }
                 // limit items to be synchronized to the mobiles if configured
                 if (defined('SYNC_FILTERTIME_MAX') && SYNC_FILTERTIME_MAX > SYNC_FILTERTYPE_ALL && (!$spa->HasFilterType() || $spa->GetFilterType() == SYNC_FILTERTYPE_ALL || $spa->GetFilterType() > SYNC_FILTERTIME_MAX)) {
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("SYNC_FILTERTIME_MAX defined. Filter set to value: %s", SYNC_FILTERTIME_MAX));
                     $spa->SetFilterType(SYNC_FILTERTIME_MAX);
                 }
                 // Check if the hierarchycache is available. If not, trigger a HierarchySync
                 if (self::$deviceManager->IsHierarchySyncRequired()) {
                     $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                     ZLog::Write(LOGLEVEL_DEBUG, "HierarchyCache is also not available. Triggering HierarchySync to device");
                 }
                 if (($el = self::$decoder->getElementStartTag(SYNC_PERFORM)) && $el[EN_FLAGS] & EN_FLAGS_CONTENT) {
                     // We can not proceed here as the content class is unknown
                     if ($status != SYNC_STATUS_SUCCESS) {
                         ZLog::Write(LOGLEVEL_WARN, "Ignoring all incoming actions as global status indicates problem.");
                         $wbxmlproblem = true;
                         break;
                     }
                     $performaction = true;
                     // unset the importer
                     $this->importer = false;
                     $nchanges = 0;
                     while (1) {
                         // ADD, MODIFY, REMOVE or FETCH
                         $element = self::$decoder->getElement();
                         if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
                             self::$decoder->ungetElement($element);
                             break;
                         }
                         if ($status == SYNC_STATUS_SUCCESS) {
                             $nchanges++;
                         }
                         // Foldertype sent when synching SMS
                         if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                             $foldertype = self::$decoder->getElementContent();
                             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): incoming data with foldertype '%s'", $foldertype));
                             if (!self::$decoder->getElementEndTag()) {
                                 return false;
                             }
                         } else {
                             $foldertype = false;
                         }
                         $serverid = false;
                         if (self::$decoder->getElementStartTag(SYNC_SERVERENTRYID)) {
                             if (($serverid = self::$decoder->getElementContent()) !== false) {
                                 if (!self::$decoder->getElementEndTag()) {
                                     // end serverid
                                     return false;
                                 }
                             }
                         }
                         if (self::$decoder->getElementStartTag(SYNC_CLIENTENTRYID)) {
                             $clientid = self::$decoder->getElementContent();
                             if (!self::$decoder->getElementEndTag()) {
                                 // end clientid
                                 return false;
                             }
                         } else {
                             $clientid = false;
                         }
                         // Get the SyncMessage if sent
                         if (self::$decoder->getElementStartTag(SYNC_DATA)) {
                             $message = ZPush::getSyncObjectFromFolderClass($spa->GetContentClass());
                             $message->Decode(self::$decoder);
                             // set Ghosted fields
                             $message->emptySupported(self::$deviceManager->GetSupportedFields($spa->GetFolderId()));
                             if (!self::$decoder->getElementEndTag()) {
                                 // end applicationdata
                                 return false;
                             }
                         } else {
                             $message = false;
                         }
                         switch ($element[EN_TAG]) {
                             case SYNC_FETCH:
                                 array_push($actiondata["fetchids"], $serverid);
                                 break;
                             default:
                                 // get the importer
                                 if ($this->importer == false) {
                                     $status = $this->getImporter($sc, $spa, $actiondata);
                                 }
                                 if ($status == SYNC_STATUS_SUCCESS) {
                                     $this->importMessage($spa, $actiondata, $element[EN_TAG], $message, $clientid, $serverid, $foldertype, $nchanges);
                                 } else {
                                     ZLog::Write(LOGLEVEL_WARN, "Ignored incoming change, global status indicates problem.");
                                 }
                                 break;
                         }
                         if ($actiondata["fetchids"]) {
                             self::$topCollector->AnnounceInformation(sprintf("Fetching %d", $nchanges));
                         } else {
                             self::$topCollector->AnnounceInformation(sprintf("Incoming %d", $nchanges));
                         }
                         if (!self::$decoder->getElementEndTag()) {
                             // end add/change/delete/move
                             return false;
                         }
                     }
                     if ($status == SYNC_STATUS_SUCCESS && $this->importer !== false) {
                         ZLog::Write(LOGLEVEL_INFO, sprintf("Sync->Handle(): Processed %d incoming changes", $nchanges));
                         if (!$actiondata["fetchids"]) {
                             self::$topCollector->AnnounceInformation(sprintf("%d incoming", $nchanges), true);
                         }
                         try {
                             // Save the updated state, which is used for the exporter later
                             $sc->AddParameter($spa, "state", $this->importer->GetState());
                         } catch (StatusException $stex) {
                             $status = $stex->getCode();
                         }
                     }
                     if (!self::$decoder->getElementEndTag()) {
                         // end PERFORM
                         return false;
                     }
                 }
                 // save the failsave state
                 if (!empty($actiondata["statusids"])) {
                     unset($actiondata["failstate"]);
                     $actiondata["failedsyncstate"] = $sc->GetParameter($spa, "state");
                     self::$deviceManager->GetStateManager()->SetSyncFailState($actiondata);
                 }
                 // save actiondata
                 $sc->AddParameter($spa, "actiondata", $actiondata);
                 if (!self::$decoder->getElementEndTag()) {
                     // end collection
                     return false;
                 }
                 // AS14 does not send GetChanges anymore. We should do it if there were no incoming changes
                 if (!isset($performaction) && !$sc->GetParameter($spa, "getchanges") && $spa->HasSyncKey()) {
                     $sc->AddParameter($spa, "getchanges", true);
                 }
             }
             // END FOLDER
             if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) {
                 // end collections
                 return false;
             }
         }
         // end FOLDERS
         if (self::$decoder->getElementStartTag(SYNC_HEARTBEATINTERVAL)) {
             $hbinterval = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_HEARTBEATINTERVAL
                 return false;
             }
         }
         if (self::$decoder->getElementStartTag(SYNC_WAIT)) {
             $wait = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_WAIT
                 return false;
             }
             // internally the heartbeat interval and the wait time are the same
             // heartbeat is in seconds, wait in minutes
             $hbinterval = $wait * 60;
         }
         if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) {
             $sc->SetGlobalWindowSize(self::$decoder->getElementContent());
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_WINDOWSIZE
                 return false;
             }
         }
         if (self::$decoder->getElementStartTag(SYNC_PARTIAL)) {
             $partial = true;
         } else {
             $partial = false;
         }
         if (!$wbxmlproblem && !self::$decoder->getElementEndTag()) {
             // end sync
             return false;
         }
     } else {
         $emptysync = true;
     }
     // END SYNCHRONIZE
     // check heartbeat/wait time
     if (isset($hbinterval)) {
         if ($hbinterval < 60 || $hbinterval > 3540) {
             $status = SYNC_STATUS_INVALIDWAITORHBVALUE;
             ZLog::Write(LOGLEVEL_WARN, sprintf("HandleSync(): Invalid heartbeat or wait value '%s'", $hbinterval));
         }
     }
     // Partial & Empty Syncs need saved data to proceed with synchronization
     if ($status == SYNC_STATUS_SUCCESS && ($emptysync === true || $partial === true)) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Partial or Empty sync requested. Retrieving data of synchronized folders."));
         // Load all collections - do not overwrite existing (received!), load states and check permissions
         try {
             $sc->LoadAllCollections(false, true, true);
         } catch (StateNotFoundException $snfex) {
             $status = SYNC_STATUS_INVALIDSYNCKEY;
             self::$topCollector->AnnounceInformation("StateNotFoundException", true);
         } catch (StatusException $stex) {
             $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
             self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true);
         }
         // update a few values
         foreach ($sc as $folderid => $spa) {
             // manually set getchanges parameter for this collection
             $sc->AddParameter($spa, "getchanges", true);
             // set new global windowsize without marking the SPA as changed
             if ($sc->GetGlobalWindowSize()) {
                 $spa->SetWindowSize($sc->GetGlobalWindowSize(), false);
             }
             // announce WindowSize to DeviceManager
             self::$deviceManager->SetWindowSize($folderid, $spa->GetWindowSize());
         }
         if (!$sc->HasCollections()) {
             $status = SYNC_STATUS_SYNCREQUESTINCOMPLETE;
         }
     }
     // HEARTBEAT & Empty sync
     if ($status == SYNC_STATUS_SUCCESS && (isset($hbinterval) || $emptysync == true)) {
         $interval = defined('PING_INTERVAL') && PING_INTERVAL > 0 ? PING_INTERVAL : 30;
         if (isset($hbinterval)) {
             $sc->SetLifetime($hbinterval);
         }
         // states are lazy loaded - we have to make sure that they are there!
         $loadstatus = SYNC_STATUS_SUCCESS;
         foreach ($sc as $folderid => $spa) {
             // some androids do heartbeat on the OUTBOX folder, with weird results - ZP-362
             // we do not load the state so we will never get relevant changes on the OUTBOX folder
             if (self::$deviceManager->GetFolderTypeFromCacheById($folderid) == SYNC_FOLDER_TYPE_OUTBOX) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Heartbeat on Outbox folder not allowed"));
                 continue;
             }
             $fad = array();
             // if loading the states fails, we do not enter heartbeat, but we keep $status on SYNC_STATUS_SUCCESS
             // so when the changes are exported the correct folder gets an SYNC_STATUS_INVALIDSYNCKEY
             if ($loadstatus == SYNC_STATUS_SUCCESS) {
                 $loadstatus = $this->loadStates($sc, $spa, $fad);
             }
         }
         if ($loadstatus == SYNC_STATUS_SUCCESS) {
             $foundchanges = false;
             try {
                 // always check for changes
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Entering Heartbeat mode"));
                 $foundchanges = $sc->CheckForChanges($sc->GetLifetime(), $interval);
             } catch (StatusException $stex) {
                 if ($stex->getCode() == SyncCollections::OBSOLETE_CONNECTION) {
                     $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
                 } else {
                     $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED;
                     self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true);
                 }
             }
             // in case there are no changes and no other request has synchronized while we waited, we can reply with an empty response
             if (!$foundchanges && $status == SYNC_STATUS_SUCCESS) {
                 // if there were changes to the SPA or CPOs we need to save this before we terminate
                 // only save if the state was not modified by some other request, if so, return state invalid status
                 foreach ($sc as $folderid => $spa) {
                     if (self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
                         $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
                     } else {
                         $sc->SaveCollection($spa);
                     }
                 }
                 if ($status == SYNC_STATUS_SUCCESS) {
                     ZLog::Write(LOGLEVEL_DEBUG, "No changes found and no other process changed states. Replying with empty response and closing connection.");
                     self::$specialHeaders = array();
                     self::$specialHeaders[] = "Connection: close";
                     return true;
                 }
             }
             if ($foundchanges) {
                 foreach ($sc->GetChangedFolderIds() as $folderid => $changecount) {
                     // check if there were other sync requests for a folder during the heartbeat
                     $spa = $sc->GetCollection($folderid);
                     if ($changecount > 0 && $sc->WaitedForChanges() && self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s' which was already synchronized. Heartbeat aborted!", $changecount, $folderid));
                         $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID;
                     } else {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s'", $changecount, $folderid));
                     }
                 }
             }
         }
     }
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Start Output"));
     // Start the output
     self::$encoder->startWBXML();
     self::$encoder->startTag(SYNC_SYNCHRONIZE);
     // global status
     // SYNC_COMMONSTATUS_* start with values from 101
     if ($status != SYNC_COMMONSTATUS_SUCCESS && $status > 100) {
         self::$encoder->startTag(SYNC_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
     } else {
         self::$encoder->startTag(SYNC_FOLDERS);
         foreach ($sc as $folderid => $spa) {
             // get actiondata
             $actiondata = $sc->GetParameter($spa, "actiondata");
             if ($status == SYNC_STATUS_SUCCESS && (!$spa->GetContentClass() || !$spa->GetFolderId())) {
                 ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): no content class or folderid found for collection."));
                 continue;
             }
             if (!$sc->GetParameter($spa, "requested")) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): partial sync for folder class '%s' with id '%s'", $spa->GetContentClass(), $spa->GetFolderId()));
             }
             // initialize exporter to get changecount
             $changecount = false;
             if (isset($exporter)) {
                 unset($exporter);
             }
             // TODO we could check against $sc->GetChangedFolderIds() on heartbeat so we do not need to configure all exporter again
             if ($status == SYNC_STATUS_SUCCESS && ($sc->GetParameter($spa, "getchanges") || !$spa->HasSyncKey())) {
                 //make sure the states are loaded
                 $status = $this->loadStates($sc, $spa, $actiondata);
                 if ($status == SYNC_STATUS_SUCCESS) {
                     try {
                         // 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);
                         }
                         // Use the state from the importer, as changes may have already happened
                         $exporter = self::$backend->GetExporter($spa->GetFolderId());
                         if ($exporter === false) {
                             throw new StatusException(sprintf("HandleSync() could not get an exporter for folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
                         }
                     } catch (StatusException $stex) {
                         $status = $stex->getCode();
                     }
                     try {
                         // Stream the messages directly to the PDA
                         $streamimporter = new ImportChangesStream(self::$encoder, ZPush::getSyncObjectFromFolderClass($spa->GetContentClass()));
                         if ($exporter !== false) {
                             $exporter->Config($sc->GetParameter($spa, "state"));
                             $exporter->ConfigContentParameters($spa->GetCPO());
                             $exporter->InitializeExporter($streamimporter);
                             $changecount = $exporter->GetChangeCount();
                         }
                     } catch (StatusException $stex) {
                         if ($stex->getCode() === SYNC_FSSTATUS_CODEUNKNOWN && $spa->HasSyncKey()) {
                             $status = SYNC_STATUS_INVALIDSYNCKEY;
                         } else {
                             $status = $stex->getCode();
                         }
                     }
                     if (!$spa->HasSyncKey()) {
                         self::$topCollector->AnnounceInformation(sprintf("Exporter registered. %d objects queued.", $changecount), true);
                         // update folder status as initialized
                         $spa->SetFolderSyncTotal($changecount);
                         $spa->SetFolderSyncRemaining($changecount);
                         if ($changecount > 0) {
                             self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INITIALIZED);
                         }
                     } else {
                         if ($status != SYNC_STATUS_SUCCESS) {
                             self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true);
                         }
                     }
                 }
             }
             if (isset($hbinterval) && $changecount == 0 && $status == SYNC_STATUS_SUCCESS) {
                 ZLog::Write(LOGLEVEL_DEBUG, "No changes found for heartbeat folder. Omitting empty output.");
                 continue;
             }
             // Get a new sync key to output to the client if any changes have been send or will are available
             if (!empty($actiondata["modifyids"]) || !empty($actiondata["clientids"]) || !empty($actiondata["removeids"]) || $changecount > 0 || !$spa->HasSyncKey() && $status == SYNC_STATUS_SUCCESS) {
                 $spa->SetNewSyncKey(self::$deviceManager->GetStateManager()->GetNewSyncKey($spa->GetSyncKey()));
             }
             self::$encoder->startTag(SYNC_FOLDER);
             if ($spa->HasContentClass()) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("Folder type: %s", $spa->GetContentClass()));
                 // AS 12.0 devices require content class
                 if (Request::GetProtocolVersion() < 12.1) {
                     self::$encoder->startTag(SYNC_FOLDERTYPE);
                     self::$encoder->content($spa->GetContentClass());
                     self::$encoder->endTag();
                 }
             }
             self::$encoder->startTag(SYNC_SYNCKEY);
             if ($status == SYNC_STATUS_SUCCESS && $spa->HasNewSyncKey()) {
                 self::$encoder->content($spa->GetNewSyncKey());
             } else {
                 self::$encoder->content($spa->GetSyncKey());
             }
             self::$encoder->endTag();
             self::$encoder->startTag(SYNC_FOLDERID);
             self::$encoder->content($spa->GetFolderId());
             self::$encoder->endTag();
             self::$encoder->startTag(SYNC_STATUS);
             self::$encoder->content($status);
             self::$encoder->endTag();
             // announce failing status to the process loop detection
             if ($status !== SYNC_STATUS_SUCCESS) {
                 self::$deviceManager->AnnounceProcessStatus($spa->GetFolderId(), $status);
             }
             // Output IDs and status for incoming items & requests
             if ($status == SYNC_STATUS_SUCCESS && (!empty($actiondata["clientids"]) || !empty($actiondata["modifyids"]) || !empty($actiondata["removeids"]) || !empty($actiondata["fetchids"]))) {
                 self::$encoder->startTag(SYNC_REPLIES);
                 // output result of all new incoming items
                 foreach ($actiondata["clientids"] as $clientid => $serverid) {
                     self::$encoder->startTag(SYNC_ADD);
                     self::$encoder->startTag(SYNC_CLIENTENTRYID);
                     self::$encoder->content($clientid);
                     self::$encoder->endTag();
                     if ($serverid) {
                         self::$encoder->startTag(SYNC_SERVERENTRYID);
                         self::$encoder->content($serverid);
                         self::$encoder->endTag();
                     }
                     self::$encoder->startTag(SYNC_STATUS);
                     self::$encoder->content(isset($actiondata["statusids"][$clientid]) ? $actiondata["statusids"][$clientid] : SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR);
                     self::$encoder->endTag();
                     self::$encoder->endTag();
                 }
                 // loop through modify operations which were not a success, send status
                 foreach ($actiondata["modifyids"] as $serverid) {
                     if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
                         self::$encoder->startTag(SYNC_MODIFY);
                         self::$encoder->startTag(SYNC_SERVERENTRYID);
                         self::$encoder->content($serverid);
                         self::$encoder->endTag();
                         self::$encoder->startTag(SYNC_STATUS);
                         self::$encoder->content($actiondata["statusids"][$serverid]);
                         self::$encoder->endTag();
                         self::$encoder->endTag();
                     }
                 }
                 // loop through remove operations which were not a success, send status
                 foreach ($actiondata["removeids"] as $serverid) {
                     if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
                         self::$encoder->startTag(SYNC_REMOVE);
                         self::$encoder->startTag(SYNC_SERVERENTRYID);
                         self::$encoder->content($serverid);
                         self::$encoder->endTag();
                         self::$encoder->startTag(SYNC_STATUS);
                         self::$encoder->content($actiondata["statusids"][$serverid]);
                         self::$encoder->endTag();
                         self::$encoder->endTag();
                     }
                 }
                 if (!empty($actiondata["fetchids"])) {
                     self::$topCollector->AnnounceInformation(sprintf("Fetching %d objects ", count($actiondata["fetchids"])), true);
                 }
                 foreach ($actiondata["fetchids"] as $id) {
                     $data = false;
                     try {
                         $fetchstatus = SYNC_STATUS_SUCCESS;
                         // 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 to fetch in folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_OBJECTNOTFOUND);
                         }
                         $data = self::$backend->Fetch($spa->GetFolderId(), $id, $spa->GetCPO());
                         // check if the message is broken
                         if (ZPush::GetDeviceManager(false) && ZPush::GetDeviceManager()->DoNotStreamMessage($id, $data)) {
                             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): message not to be streamed as requested by DeviceManager, id = %s", $id));
                             $fetchstatus = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
                         }
                     } catch (StatusException $stex) {
                         $fetchstatus = $stex->getCode();
                     }
                     self::$encoder->startTag(SYNC_FETCH);
                     self::$encoder->startTag(SYNC_SERVERENTRYID);
                     self::$encoder->content($id);
                     self::$encoder->endTag();
                     self::$encoder->startTag(SYNC_STATUS);
                     self::$encoder->content($fetchstatus);
                     self::$encoder->endTag();
                     if ($data !== false && $status == SYNC_STATUS_SUCCESS) {
                         self::$encoder->startTag(SYNC_DATA);
                         $data->Encode(self::$encoder);
                         self::$encoder->endTag();
                     } else {
                         ZLog::Write(LOGLEVEL_WARN, sprintf("Unable to Fetch '%s'", $id));
                     }
                     self::$encoder->endTag();
                 }
                 self::$encoder->endTag();
             }
             if ($sc->GetParameter($spa, "getchanges") && $spa->HasFolderId() && $spa->HasContentClass() && $spa->HasSyncKey()) {
                 $windowSize = self::$deviceManager->GetWindowSize($spa->GetFolderId(), $spa->GetContentClass(), $spa->GetUuid(), $spa->GetUuidCounter(), $changecount);
                 if ($changecount > $windowSize) {
                     self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true);
                 }
             }
             // Stream outgoing changes
             if ($status == SYNC_STATUS_SUCCESS && $sc->GetParameter($spa, "getchanges") == true && $windowSize > 0) {
                 self::$topCollector->AnnounceInformation(sprintf("Streaming data of %d objects", $changecount > $windowSize ? $windowSize : $changecount));
                 // Output message changes per folder
                 self::$encoder->startTag(SYNC_PERFORM);
                 $n = 0;
                 while (1) {
                     try {
                         $progress = $exporter->Synchronize();
                         if (!is_array($progress)) {
                             break;
                         }
                         $n++;
                     } catch (SyncObjectBrokenException $mbe) {
                         $brokenSO = $mbe->GetSyncObject();
                         if (!$brokenSO) {
                             ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Catched SyncObjectBrokenException but broken SyncObject not available. This should be fixed in the backend."));
                         } else {
                             if (!isset($brokenSO->id)) {
                                 $brokenSO->id = "Unknown ID";
                                 ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Catched SyncObjectBrokenException but no ID of object set. This should be fixed in the backend."));
                             }
                             self::$deviceManager->AnnounceIgnoredMessage($spa->GetFolderId(), $brokenSO->id, $brokenSO);
                         }
                     } catch (StatusException $stex) {
                         $status = $stex->getCode();
                         // during export we found out that the states should be thrown away (ZP-623)
                         if ($status == SYNC_STATUS_INVALIDSYNCKEY) {
                             self::$deviceManager->ForceFolderResync($spa->GetFolderId());
                             break;
                         }
                     }
                     if ($n >= $windowSize) {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Exported maxItems of messages: %d / %d", $n, $changecount));
                         break;
                     }
                 }
                 // $progress is not an array when exporting the last message
                 // so we get the number to display from the streamimporter
                 if (isset($streamimporter)) {
                     $n = $streamimporter->GetImportedMessages();
                 }
                 self::$encoder->endTag();
                 self::$topCollector->AnnounceInformation(sprintf("Outgoing %d objects%s", $n, $n >= $windowSize ? " of " . $changecount : ""), true);
                 // update folder status, if there is something set
                 if ($spa->GetFolderSyncRemaining() && $changecount > 0) {
                     $spa->SetFolderSyncRemaining($changecount);
                 }
                 // changecount is initialized with 'false', so 0 means no changes!
                 if ($changecount === 0 || $changecount !== false && $changecount <= $windowSize) {
                     self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_COMPLETED);
                 } else {
                     self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INPROGRESS);
                 }
             }
             self::$encoder->endTag();
             // Save the sync state for the next time
             if ($spa->HasNewSyncKey()) {
                 self::$topCollector->AnnounceInformation("Saving state");
                 try {
                     if (isset($exporter) && $exporter) {
                         $state = $exporter->GetState();
                     } else {
                         if ($sc->GetParameter($spa, "state") !== null) {
                             $state = $sc->GetParameter($spa, "state");
                         } else {
                             if (!$spa->HasSyncKey()) {
                                 $state = "";
                             }
                         }
                     }
                 } catch (StatusException $stex) {
                     $status = $stex->getCode();
                 }
                 if (isset($state) && $status == SYNC_STATUS_SUCCESS) {
                     self::$deviceManager->GetStateManager()->SetSyncState($spa->GetNewSyncKey(), $state, $spa->GetFolderId());
                 } else {
                     ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): error saving '%s' - no state information available", $spa->GetNewSyncKey()));
                 }
             }
             // save SyncParameters
             if ($status == SYNC_STATUS_SUCCESS && empty($actiondata["fetchids"])) {
                 $sc->SaveCollection($spa);
             }
             // reset status for the next folder
             $status = SYNC_STATUS_SUCCESS;
         }
         // END foreach collection
         self::$encoder->endTag();
         //SYNC_FOLDERS
     }
     self::$encoder->endTag();
     //SYNC_SYNCHRONIZE
     return true;
 }
Ejemplo n.º 4
0
 /**
  * Gets the StateManager from the DeviceManager
  * if it's not available
  *
  * @access private
  * @return
  */
 private function loadStateManager()
 {
     if (!isset($this->stateManager)) {
         $this->stateManager = ZPush::GetDeviceManager()->GetStateManager();
     }
 }
Ejemplo n.º 5
0
 /**
  * Imports a single message
  *
  * @param array         $props
  * @param long          $flags
  * @param object        $retmapimessage
  *
  * @access public
  * @return long
  */
 public function ImportMessageChange($props, $flags, &$retmapimessage)
 {
     $sourcekey = $props[PR_SOURCE_KEY];
     $parentsourcekey = $props[PR_PARENT_SOURCE_KEY];
     $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $parentsourcekey, $sourcekey);
     if (!$entryid) {
         return SYNC_E_IGNORE;
     }
     $mapimessage = mapi_msgstore_openentry($this->store, $entryid);
     try {
         $message = $this->mapiprovider->GetMessage($mapimessage, $this->contentparameters);
     } catch (SyncObjectBrokenException $mbe) {
         $brokenSO = $mbe->GetSyncObject();
         if (!$brokenSO) {
             ZLog::Write(LOGLEVEL_ERROR, sprintf("PHPWrapper->ImportMessageChange(): Catched SyncObjectBrokenException but broken SyncObject available"));
         } else {
             if (!isset($brokenSO->id)) {
                 $brokenSO->id = "Unknown ID";
                 ZLog::Write(LOGLEVEL_ERROR, sprintf("PHPWrapper->ImportMessageChange(): Catched SyncObjectBrokenException but no ID of object set"));
             }
             ZPush::GetDeviceManager()->AnnounceIgnoredMessage(false, $brokenSO->id, $brokenSO);
         }
         // tell MAPI to ignore the message
         return SYNC_E_IGNORE;
     }
     // substitute the MAPI SYNC_NEW_MESSAGE flag by a z-push proprietary flag
     if ($flags == SYNC_NEW_MESSAGE) {
         $message->flags = SYNC_NEWMESSAGE;
     } else {
         $message->flags = $flags;
     }
     $this->importer->ImportMessageChange(bin2hex($sourcekey), $message);
     // Tell MAPI it doesn't need to do anything itself, as we've done all the work already.
     return SYNC_E_IGNORE;
 }
Ejemplo n.º 6
0
 /**
  * Imports a change on a folder
  *
  * @param object        $folder     SyncFolder
  *
  * @access public
  * @return string       id of the folder
  */
 public function ImportFolderChange($folder)
 {
     // checks if the next message may cause a loop or is broken
     if (ZPush::GetDeviceManager(false) && ZPush::GetDeviceManager()->DoNotStreamMessage($folder->serverid, $folder)) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportFolderChange('%s'): folder ignored as requested by DeviceManager.", $folder->serverid));
         return true;
     }
     // send a modify flag if the folder is already known on the device
     if (isset($folder->flags) && $folder->flags === SYNC_NEWMESSAGE) {
         $this->encoder->startTag(SYNC_FOLDERHIERARCHY_ADD);
     } else {
         $this->encoder->startTag(SYNC_FOLDERHIERARCHY_UPDATE);
     }
     $folder->Encode($this->encoder);
     $this->encoder->endTag();
     return true;
 }
Ejemplo n.º 7
0
 /**
  * Synchronizes a folder to the output stream. Changes for this folders are expected.
  *
  * @param SyncCollections       $sc
  * @param SyncParameters        $spa
  * @param IExportChanges        $exporter             Fully configured exporter for this folder
  * @param int                   $changecount          Amount of changes expected
  * @param ImportChangesStream   $streamimporter       Output stream
  * @param int                   $status               current status of the folder processing
  * @param string                $newFolderStat        the new folder stat to be set if everything was exported
  *
  * @throws StatusException
  * @return int  sync status code
  */
 private function syncFolder($sc, $spa, $exporter, $changecount, $streamimporter, $status, $newFolderStat)
 {
     $actiondata = $sc->GetParameter($spa, "actiondata");
     // send the WBXML start tags (if not happened already)
     $this->sendFolderStartTag();
     self::$encoder->startTag(SYNC_FOLDER);
     if ($spa->HasContentClass()) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("Folder type: %s", $spa->GetContentClass()));
         // AS 12.0 devices require content class
         if (Request::GetProtocolVersion() < 12.1) {
             self::$encoder->startTag(SYNC_FOLDERTYPE);
             self::$encoder->content($spa->GetContentClass());
             self::$encoder->endTag();
         }
     }
     self::$encoder->startTag(SYNC_SYNCKEY);
     if ($status == SYNC_STATUS_SUCCESS && $spa->HasNewSyncKey()) {
         self::$encoder->content($spa->GetNewSyncKey());
     } else {
         self::$encoder->content($spa->GetSyncKey());
     }
     self::$encoder->endTag();
     self::$encoder->startTag(SYNC_FOLDERID);
     self::$encoder->content($spa->GetFolderId());
     self::$encoder->endTag();
     self::$encoder->startTag(SYNC_STATUS);
     self::$encoder->content($status);
     self::$encoder->endTag();
     // announce failing status to the process loop detection
     if ($status !== SYNC_STATUS_SUCCESS) {
         self::$deviceManager->AnnounceProcessStatus($spa->GetFolderId(), $status);
     }
     // Output IDs and status for incoming items & requests
     if ($status == SYNC_STATUS_SUCCESS && (!empty($actiondata["clientids"]) || !empty($actiondata["modifyids"]) || !empty($actiondata["removeids"]) || !empty($actiondata["fetchids"]))) {
         self::$encoder->startTag(SYNC_REPLIES);
         // output result of all new incoming items
         foreach ($actiondata["clientids"] as $clientid => $serverid) {
             self::$encoder->startTag(SYNC_ADD);
             self::$encoder->startTag(SYNC_CLIENTENTRYID);
             self::$encoder->content($clientid);
             self::$encoder->endTag();
             if ($serverid) {
                 self::$encoder->startTag(SYNC_SERVERENTRYID);
                 self::$encoder->content($serverid);
                 self::$encoder->endTag();
             }
             self::$encoder->startTag(SYNC_STATUS);
             self::$encoder->content(isset($actiondata["statusids"][$clientid]) ? $actiondata["statusids"][$clientid] : SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR);
             self::$encoder->endTag();
             self::$encoder->endTag();
         }
         // loop through modify operations which were not a success, send status
         foreach ($actiondata["modifyids"] as $serverid) {
             if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
                 self::$encoder->startTag(SYNC_MODIFY);
                 self::$encoder->startTag(SYNC_SERVERENTRYID);
                 self::$encoder->content($serverid);
                 self::$encoder->endTag();
                 self::$encoder->startTag(SYNC_STATUS);
                 self::$encoder->content($actiondata["statusids"][$serverid]);
                 self::$encoder->endTag();
                 self::$encoder->endTag();
             }
         }
         // loop through remove operations which were not a success, send status
         foreach ($actiondata["removeids"] as $serverid) {
             if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) {
                 self::$encoder->startTag(SYNC_REMOVE);
                 self::$encoder->startTag(SYNC_SERVERENTRYID);
                 self::$encoder->content($serverid);
                 self::$encoder->endTag();
                 self::$encoder->startTag(SYNC_STATUS);
                 self::$encoder->content($actiondata["statusids"][$serverid]);
                 self::$encoder->endTag();
                 self::$encoder->endTag();
             }
         }
         if (!empty($actiondata["fetchids"])) {
             self::$topCollector->AnnounceInformation(sprintf("Fetching %d objects ", count($actiondata["fetchids"])), $this->singleFolder);
             $this->saveMultiFolderInfo("fetching", count($actiondata["fetchids"]));
         }
         foreach ($actiondata["fetchids"] as $id) {
             $data = false;
             try {
                 $fetchstatus = SYNC_STATUS_SUCCESS;
                 // 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("HandleSync(): could not Setup() the backend to fetch in folder id %s/%s", $spa->GetFolderId(), $spa->GetBackendFolderId()), SYNC_STATUS_OBJECTNOTFOUND);
                 }
                 $data = self::$backend->Fetch($spa->GetBackendFolderId(), $id, $spa->GetCPO());
                 // check if the message is broken
                 if (ZPush::GetDeviceManager(false) && ZPush::GetDeviceManager()->DoNotStreamMessage($id, $data)) {
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): message not to be streamed as requested by DeviceManager, id = %s", $id));
                     $fetchstatus = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR;
                 }
             } catch (StatusException $stex) {
                 $fetchstatus = $stex->getCode();
             }
             self::$encoder->startTag(SYNC_FETCH);
             self::$encoder->startTag(SYNC_SERVERENTRYID);
             self::$encoder->content($id);
             self::$encoder->endTag();
             self::$encoder->startTag(SYNC_STATUS);
             self::$encoder->content($fetchstatus);
             self::$encoder->endTag();
             if ($data !== false && $status == SYNC_STATUS_SUCCESS) {
                 self::$encoder->startTag(SYNC_DATA);
                 $data->Encode(self::$encoder);
                 self::$encoder->endTag();
             } else {
                 ZLog::Write(LOGLEVEL_WARN, sprintf("Unable to Fetch '%s'", $id));
             }
             self::$encoder->endTag();
         }
         self::$encoder->endTag();
     }
     if ($sc->GetParameter($spa, "getchanges") && $spa->HasFolderId() && $spa->HasContentClass() && $spa->HasSyncKey()) {
         $moreAvailableSent = false;
         $windowSize = self::$deviceManager->GetWindowSize($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter(), $changecount);
         // limit windowSize to the max available limit of the global window size left
         $globallyAvailable = $sc->GetGlobalWindowSize() - $this->globallyExportedItems;
         if ($changecount > $globallyAvailable && $windowSize > $globallyAvailable) {
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Limit window size to %d as the global window size limit will be reached", $globallyAvailable));
             $windowSize = $globallyAvailable;
         }
         // send <MoreAvailable/> if there are more changes than fit in the folder windowsize
         // or there is a move state (another sync should be done afterwards)
         if ($changecount > $windowSize || $spa->GetMoveState() !== false) {
             self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true);
             $moreAvailableSent = true;
             $spa->DelFolderStat();
         }
     }
     // Stream outgoing changes
     if ($status == SYNC_STATUS_SUCCESS && $sc->GetParameter($spa, "getchanges") == true && $windowSize > 0 && !!$exporter) {
         self::$topCollector->AnnounceInformation(sprintf("Streaming data of %d objects", $changecount > $windowSize ? $windowSize : $changecount));
         // Output message changes per folder
         self::$encoder->startTag(SYNC_PERFORM);
         $n = 0;
         WBXMLDecoder::ResetInWhile("syncSynchronize");
         while (WBXMLDecoder::InWhile("syncSynchronize")) {
             try {
                 $progress = $exporter->Synchronize();
                 if (!is_array($progress)) {
                     break;
                 }
                 $n++;
                 if ($n % 10 == 0) {
                     self::$topCollector->AnnounceInformation(sprintf("Streamed data of %d objects out of %d", $n, $changecount > $windowSize ? $windowSize : $changecount));
                 }
             } catch (SyncObjectBrokenException $mbe) {
                 $brokenSO = $mbe->GetSyncObject();
                 if (!$brokenSO) {
                     ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Catched SyncObjectBrokenException but broken SyncObject not available. This should be fixed in the backend."));
                 } else {
                     if (!isset($brokenSO->id)) {
                         $brokenSO->id = "Unknown ID";
                         ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Catched SyncObjectBrokenException but no ID of object set. This should be fixed in the backend."));
                     }
                     self::$deviceManager->AnnounceIgnoredMessage($spa->GetFolderId(), $brokenSO->id, $brokenSO);
                 }
             } catch (StatusException $stex) {
                 $status = $stex->getCode();
                 // during export we found out that the states should be thrown away (ZP-623)
                 if ($status == SYNC_STATUS_INVALIDSYNCKEY) {
                     self::$deviceManager->ForceFolderResync($spa->GetFolderId());
                     break;
                 }
             }
             if ($n >= $windowSize || Request::IsRequestTimeoutReached()) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Exported maxItems of messages: %d / %d", $n, $changecount));
                 break;
             }
         }
         // $progress is not an array when exporting the last message
         // so we get the number to display from the streamimporter if it's available
         if (!!$streamimporter) {
             $n = $streamimporter->GetImportedMessages();
         }
         self::$encoder->endTag();
         // log the request timeout
         if (Request::IsRequestTimeoutReached()) {
             ZLog::Write(LOGLEVEL_DEBUG, "HandleSync(): Stopping export as maximum request timeout is almost reached!");
             // Send a <MoreAvailable/> tag if we reached the request timeout, there are more changes and a moreavailable was not already send
             if (!$moreAvailableSent && $n > $windowSize) {
                 self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true);
                 $spa->DelFolderStat();
                 $moreAvailableSent = true;
             }
         }
         self::$topCollector->AnnounceInformation(sprintf("Outgoing %d objects%s", $n, $n >= $windowSize ? " of " . $changecount : ""), $this->singleFolder);
         $this->saveMultiFolderInfo("outgoing", $n);
         $this->saveMultiFolderInfo("queued", $changecount);
         $this->globallyExportedItems += $n;
         // update folder status, if there is something set
         if ($spa->GetFolderSyncRemaining() && $changecount > 0) {
             $spa->SetFolderSyncRemaining($changecount);
         }
         // changecount is initialized with 'false', so 0 means no changes!
         if ($changecount === 0 || $changecount !== false && $changecount <= $windowSize) {
             self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_COMPLETED);
             // we should update the folderstat, but we recheck to see if it changed. If so, it's not updated to force another sync
             $newFolderStatAfterExport = self::$backend->GetFolderStat(ZPush::GetAdditionalSyncFolderStore($spa->GetBackendFolderId()), $spa->GetBackendFolderId());
             if ($newFolderStat === $newFolderStatAfterExport) {
                 $this->setFolderStat($spa, $newFolderStat);
             } else {
                 ZLog::Write(LOGLEVEL_DEBUG, "Sync() Folderstat differs after export, force another exporter run.");
             }
         } else {
             self::$deviceManager->SetFolderSyncStatus($spa->GetFolderId(), DeviceManager::FLD_SYNC_INPROGRESS);
         }
     }
     self::$encoder->endTag();
     // Save the sync state for the next time
     if ($spa->HasNewSyncKey()) {
         self::$topCollector->AnnounceInformation("Saving state");
         try {
             if (isset($exporter) && $exporter) {
                 $state = $exporter->GetState();
                 // update the move state (it should be gone now)
                 list($moveState, ) = $exporter->GetMoveStates();
                 $spa->SetMoveState($moveState);
             } else {
                 if ($sc->GetParameter($spa, "state") !== null) {
                     $state = $sc->GetParameter($spa, "state");
                 } else {
                     if (!$spa->HasSyncKey()) {
                         $state = "";
                     }
                 }
             }
         } catch (StatusException $stex) {
             $status = $stex->getCode();
         }
         if (isset($state) && $status == SYNC_STATUS_SUCCESS) {
             self::$deviceManager->GetStateManager()->SetSyncState($spa->GetNewSyncKey(), $state, $spa->GetFolderId());
         } else {
             ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): error saving '%s' - no state information available", $spa->GetNewSyncKey()));
         }
     }
     // save SyncParameters
     if ($status == SYNC_STATUS_SUCCESS && empty($actiondata["fetchids"])) {
         $sc->SaveCollection($spa);
     }
     return $status;
 }
 /**
  * Initialize the RequestProcessor
  *
  * @access public
  * @return
  */
 public static function Initialize()
 {
     self::$backend = ZPush::GetBackend();
     self::$deviceManager = ZPush::GetDeviceManager();
     self::$topCollector = ZPush::GetTopCollector();
     if (!ZPush::CommandNeedsPlainInput(Request::GetCommandCode())) {
         self::$decoder = new WBXMLDecoder(Request::GetInputStream());
     }
     self::$encoder = new WBXMLEncoder(Request::GetOutputStream(), Request::GetGETAcceptMultipart());
 }
Ejemplo n.º 9
0
 /**
  * Creates a SyncFolder from MAPI properties.
  *
  * @param mixed             $folderprops
  *
  * @access public
  * @return SyncFolder
  */
 public function GetFolder($folderprops)
 {
     $folder = new SyncFolder();
     $storeprops = $this->GetStoreProps();
     // For ZCP 7.0.x we need to retrieve more properties explicitly, see ZP-780
     if (isset($folderprops[PR_SOURCE_KEY]) && !isset($folderprops[PR_ENTRYID]) && !isset($folderprops[PR_CONTAINER_CLASS])) {
         $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $folderprops[PR_SOURCE_KEY]);
         $mapifolder = mapi_msgstore_openentry($this->store, $entryid);
         $folderprops = mapi_getprops($mapifolder, array(PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_CONTAINER_CLASS, PR_ATTR_HIDDEN, PR_EXTENDED_FOLDER_FLAGS));
         ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->GetFolder(): received insuffient of data from ICS. Fetching required data.");
     }
     if (!isset($folderprops[PR_DISPLAY_NAME]) || !isset($folderprops[PR_PARENT_ENTRYID]) || !isset($folderprops[PR_SOURCE_KEY]) || !isset($folderprops[PR_ENTRYID]) || !isset($folderprops[PR_PARENT_SOURCE_KEY]) || !isset($storeprops[PR_IPM_SUBTREE_ENTRYID])) {
         ZLog::Write(LOGLEVEL_ERROR, "MAPIProvider->GetFolder(): invalid folder. Missing properties");
         return false;
     }
     // ignore hidden folders
     if (isset($folderprops[PR_ATTR_HIDDEN]) && $folderprops[PR_ATTR_HIDDEN] != false) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->GetFolder(): invalid folder '%s' as it is a hidden folder (PR_ATTR_HIDDEN)", $folderprops[PR_DISPLAY_NAME]));
         return false;
     }
     // ignore certain undesired folders, like "RSS Feeds"
     if (isset($folderprops[PR_CONTAINER_CLASS]) && $folderprops[PR_CONTAINER_CLASS] == "IPF.Note.OutlookHomepage") {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->GetFolder(): folder '%s' should not be synchronized", $folderprops[PR_DISPLAY_NAME]));
         return false;
     }
     // ignore suggested contacts folder
     if (isset($folderprops[PR_CONTAINER_CLASS]) && $folderprops[PR_CONTAINER_CLASS] == "IPF.Contact" && isset($folderprops[PR_EXTENDED_FOLDER_FLAGS])) {
         // the PR_EXTENDED_FOLDER_FLAGS is a binary value which consists of subproperties. 070403000000 indicates a suggested contacts folder
         $extendedFlags = bin2hex($folderprops[PR_EXTENDED_FOLDER_FLAGS]);
         if (substr_count($extendedFlags, "070403000000") > 0) {
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->GetFolder(): folder '%s' should not be synchronized", $folderprops[PR_DISPLAY_NAME]));
             return false;
         }
     }
     $folder->BackendId = bin2hex($folderprops[PR_SOURCE_KEY]);
     $folder->serverid = ZPush::GetDeviceManager()->GetFolderIdForBackendId($folder->BackendId, true, DeviceManager::FLD_ORIGIN_USER, $folderprops[PR_DISPLAY_NAME]);
     if ($folderprops[PR_PARENT_ENTRYID] == $storeprops[PR_IPM_SUBTREE_ENTRYID]) {
         $folder->parentid = "0";
     } else {
         $folder->parentid = ZPush::GetDeviceManager()->GetFolderIdForBackendId(bin2hex($folderprops[PR_PARENT_SOURCE_KEY]));
     }
     $folder->displayname = w2u($folderprops[PR_DISPLAY_NAME]);
     $folder->type = $this->GetFolderType($folderprops[PR_ENTRYID], isset($folderprops[PR_CONTAINER_CLASS]) ? $folderprops[PR_CONTAINER_CLASS] : false);
     return $folder;
 }
Ejemplo n.º 10
0
 /**
  * Imports a folder change
  *
  * @param SyncFolder    $folder     folder to be changed
  *
  * @access public
  * @return boolean/SyncObject           status/object with the ath least the serverid of the folder set
  */
 public function ImportFolderChange($folder)
 {
     // if the destinationImporter is set, then this folder should be processed by another importer
     // instead of being loaded in memory.
     if (isset($this->destinationImporter)) {
         // normally the $folder->type is not set, but we need this value to check if the change operation is permitted
         // e.g. system folders can normally not be changed - set the type from cache and let the destinationImporter decide
         if (!isset($folder->type) || !$folder->type) {
             $cacheFolder = $this->GetFolder($folder->serverid);
             $folder->type = $cacheFolder->type;
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Set foldertype for folder '%s' from cache as it was not sent: '%s'", $folder->displayname, $folder->type));
         }
         // KOE ZO-42: When Notes folders are updated in Outlook, it tries to update the name (that fails by default, as it's a system folder)
         // catch this case here and ignore the change
         if (($folder->type == SYNC_FOLDER_TYPE_NOTE || $folder->type == SYNC_FOLDER_TYPE_USER_NOTE) && ZPush::GetDeviceManager()->IsKoe()) {
             $retFolder = false;
         } else {
             $retFolder = $this->destinationImporter->ImportFolderChange($folder);
         }
         // if the operation was sucessfull, update the HierarchyCache
         if ($retFolder) {
             // if we get a folder back, we need to update some data in the cache
             if (isset($retFolder->serverid) && $retFolder->serverid) {
                 // for folder creation, the serverid & backendid are not set and have to be updated
                 if (!isset($folder->serverid) || $folder->serverid == "") {
                     $folder->serverid = $retFolder->serverid;
                     if (isset($retFolder->BackendId) && $retFolder->BackendId) {
                         $folder->BackendId = $retFolder->BackendId;
                     }
                 }
                 // if the parentid changed (folder was moved) this needs to be updated as well
                 if ($retFolder->parentid != $folder->parentid) {
                     $folder->parentid = $retFolder->parentid;
                 }
             }
             $this->AddFolder($folder);
         }
         return $retFolder;
     } else {
         if (isset($folder->serverid)) {
             // The Zarafa/Kopano HierarchyExporter exports all kinds of changes for folders (e.g. update no. of unread messages in a folder).
             // These changes are not relevant for the mobiles, as something changes but the relevant displayname and parentid
             // stay the same. These changes will be dropped and are not sent!
             $cacheFolder = $this->GetFolder($folder->serverid);
             if ($folder->equals($this->GetFolder($folder->serverid))) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Change for folder '%s' will not be sent as modification is not relevant.", $folder->displayname));
                 return false;
             }
             // check if the parent ID is known on the device
             if (!isset($folder->parentid) || $folder->parentid != "0" && !$this->GetFolder($folder->parentid)) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Change for folder '%s' will not be sent as parent folder is not set or not known on mobile.", $folder->displayname));
                 return false;
             }
             // load this change into memory
             $this->changes[] = array(self::CHANGE, $folder);
             // HierarchyCache: already add/update the folder so changes are not sent twice (if exported twice)
             $this->AddFolder($folder);
             return true;
         }
         return false;
     }
 }
Ejemplo n.º 11
0
 /**
  * Verifies Timezone, StateMachine and Backend configuration
  *
  * @access public
  * @return boolean
  * @trows FatalMisconfigurationException
  */
 public static function CheckAdvancedConfig()
 {
     global $specialLogUsers, $additionalFolders;
     if (!is_array($specialLogUsers)) {
         throw new FatalMisconfigurationException("The WBXML log users is not an array.");
     }
     if (!defined('SYNC_CONTACTS_MAXPICTURESIZE')) {
         define('SYNC_CONTACTS_MAXPICTURESIZE', 49152);
     } else {
         if (!is_int(SYNC_CONTACTS_MAXPICTURESIZE) || SYNC_CONTACTS_MAXPICTURESIZE < 1) {
             throw new FatalMisconfigurationException("The SYNC_CONTACTS_MAXPICTURESIZE value must be a number higher than 0.");
         }
     }
     if (!defined('USE_PARTIAL_FOLDERSYNC')) {
         define('USE_PARTIAL_FOLDERSYNC', false);
     }
     if (!defined('PING_LOWER_BOUND_LIFETIME')) {
         define('PING_LOWER_BOUND_LIFETIME', false);
     } elseif (PING_LOWER_BOUND_LIFETIME !== false && (!is_int(PING_LOWER_BOUND_LIFETIME) || PING_LOWER_BOUND_LIFETIME < 1 || PING_LOWER_BOUND_LIFETIME > 3540)) {
         throw new FatalMisconfigurationException("The PING_LOWER_BOUND_LIFETIME value must be 'false' or a number between 1 and 3540 inclusively.");
     }
     if (!defined('PING_HIGHER_BOUND_LIFETIME')) {
         define('PING_HIGHER_BOUND_LIFETIME', false);
     } elseif (PING_HIGHER_BOUND_LIFETIME !== false && (!is_int(PING_HIGHER_BOUND_LIFETIME) || PING_HIGHER_BOUND_LIFETIME < 1 || PING_HIGHER_BOUND_LIFETIME > 3540)) {
         throw new FatalMisconfigurationException("The PING_HIGHER_BOUND_LIFETIME value must be 'false' or a number between 1 and 3540 inclusively.");
     }
     if (PING_HIGHER_BOUND_LIFETIME !== false && PING_LOWER_BOUND_LIFETIME !== false && PING_HIGHER_BOUND_LIFETIME < PING_LOWER_BOUND_LIFETIME) {
         throw new FatalMisconfigurationException("The PING_HIGHER_BOUND_LIFETIME value must be greater or equal to PING_LOWER_BOUND_LIFETIME.");
     }
     // Check KOE flags
     if (!defined('KOE_CAPABILITY_GAB')) {
         define('KOE_CAPABILITY_GAB', false);
     }
     if (!defined('KOE_CAPABILITY_RECEIVEFLAGS')) {
         define('KOE_CAPABILITY_RECEIVEFLAGS', false);
     }
     if (!defined('KOE_CAPABILITY_SENDFLAGS')) {
         define('KOE_CAPABILITY_SENDFLAGS', false);
     }
     if (!defined('KOE_CAPABILITY_OOF')) {
         define('KOE_CAPABILITY_OOF', false);
     }
     if (!defined('KOE_CAPABILITY_OOFTIMES')) {
         define('KOE_CAPABILITY_OOFTIMES', false);
     }
     if (!defined('KOE_CAPABILITY_NOTES')) {
         define('KOE_CAPABILITY_NOTES', false);
     }
     if (!defined('KOE_CAPABILITY_SHAREDFOLDER')) {
         define('KOE_CAPABILITY_SHAREDFOLDER', false);
     }
     if (!defined('KOE_GAB_FOLDERID')) {
         define('KOE_GAB_FOLDERID', '');
     }
     if (!defined('KOE_GAB_STORE')) {
         define('KOE_GAB_STORE', '');
     }
     if (!defined('KOE_GAB_NAME')) {
         define('KOE_GAB_NAME', false);
     }
     // the check on additional folders will not throw hard errors, as this is probably changed on live systems
     if (isset($additionalFolders) && !is_array($additionalFolders)) {
         ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : The additional folders synchronization not available as array.");
     } else {
         self::$addSyncFolders = array();
         // process configured data
         foreach ($additionalFolders as $af) {
             if (!is_array($af) || !isset($af['store']) || !isset($af['folderid']) || !isset($af['name']) || !isset($af['type'])) {
                 ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Missing parameters. Entry will be ignored.");
                 continue;
             }
             if ($af['store'] == "" || $af['folderid'] == "" || $af['name'] == "" || $af['type'] == "") {
                 ZLog::Write(LOGLEVEL_WARN, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Empty parameters. Entry will be ignored.");
                 continue;
             }
             if (!in_array($af['type'], array(SYNC_FOLDER_TYPE_USER_NOTE, SYNC_FOLDER_TYPE_USER_CONTACT, SYNC_FOLDER_TYPE_USER_APPOINTMENT, SYNC_FOLDER_TYPE_USER_TASK, SYNC_FOLDER_TYPE_USER_MAIL))) {
                 ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPush::CheckConfig() : the type of the additional synchronization folder '%s is not permitted.", $af['name']));
                 continue;
             }
             $folder = new SyncFolder();
             $folder->BackendId = $af['folderid'];
             $folder->serverid = ZPush::GetDeviceManager(true)->GetFolderIdForBackendId($folder->BackendId, true, DeviceManager::FLD_ORIGIN_CONFIG, $af['name']);
             $folder->parentid = 0;
             // only top folders are supported
             $folder->displayname = $af['name'];
             $folder->type = $af['type'];
             // save store as custom property which is not streamed directly to the device
             $folder->NoBackendFolder = true;
             $folder->Store = $af['store'];
             self::$addSyncFolders[$folder->BackendId] = $folder;
         }
     }
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("Used timezone '%s'", date_default_timezone_get()));
     // get the statemachine, which will also try to load the backend.. This could throw errors
     self::GetStateMachine();
 }
Ejemplo n.º 12
0
 /**
  * Imports a change on a folder
  *
  * @param object        $folder     SyncFolder
  *
  * @access public
  * @return boolean|SyncFolder       false on error or a SyncFolder object with serverid and BackendId set (if available)
  * @throws StatusException
  */
 public function ImportFolderChange($folder)
 {
     $id = isset($folder->BackendId) ? $folder->BackendId : false;
     $parent = $folder->parentid;
     $parent_org = $folder->parentid;
     $displayname = u2wi($folder->displayname);
     $type = $folder->type;
     if (Utils::IsSystemFolder($type)) {
         throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, system folder can not be created/modified", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname), SYNC_FSSTATUS_SYSTEMFOLDER);
     }
     // create a new folder if $id is not set
     if (!$id) {
         // the root folder is "0" - get IPM_SUBTREE
         if ($parent == "0") {
             $parentprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
             if (isset($parentprops[PR_IPM_SUBTREE_ENTRYID])) {
                 $parentfentryid = $parentprops[PR_IPM_SUBTREE_ENTRYID];
             }
         } else {
             $parentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($parent));
         }
         if (!$parentfentryid) {
             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent folder (no entry id)", Utils::PrintAsString(false), $folder->parentid, $displayname), SYNC_FSSTATUS_PARENTNOTFOUND);
         }
         $parentfolder = mapi_msgstore_openentry($this->store, $parentfentryid);
         if (!$parentfolder) {
             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent folder (open entry)", Utils::PrintAsString(false), $folder->parentid, $displayname), SYNC_FSSTATUS_PARENTNOTFOUND);
         }
         //  mapi_folder_createfolder() fails if a folder with this name already exists -> MAPI_E_COLLISION
         $newfolder = mapi_folder_createfolder($parentfolder, $displayname, "");
         if (mapi_last_hresult()) {
             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, mapi_folder_createfolder() failed: 0x%X", Utils::PrintAsString(false), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_FOLDEREXISTS);
         }
         mapi_setprops($newfolder, array(PR_CONTAINER_CLASS => MAPIUtils::GetContainerClassFromFolderType($type)));
         $props = mapi_getprops($newfolder, array(PR_SOURCE_KEY));
         if (isset($props[PR_SOURCE_KEY])) {
             $folder->BackendId = bin2hex($props[PR_SOURCE_KEY]);
             $folder->serverid = ZPush::GetDeviceManager()->GetFolderIdForBackendId($folder->BackendId, true, DeviceManager::FLD_ORIGIN_USER, $folder->displayname);
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->ImportFolderChange(): Created folder '%s' with id: '%s' backendid: '%s'", $displayname, $folder->serverid, $folder->BackendId));
             return $folder;
         } else {
             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, folder created but PR_SOURCE_KEY not available: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
         }
     }
     // open folder for update
     $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($id));
     if (!$entryid) {
         throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
     }
     // check if this is a MAPI default folder
     if ($this->mapiprovider->IsMAPIDefaultFolder($entryid)) {
         throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, MAPI default folder can not be created/modified", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname), SYNC_FSSTATUS_SYSTEMFOLDER);
     }
     $mfolder = mapi_msgstore_openentry($this->store, $entryid);
     if (!$mfolder) {
         throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
     }
     $props = mapi_getprops($mfolder, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_DISPLAY_NAME, PR_CONTAINER_CLASS));
     if (!isset($props[PR_SOURCE_KEY]) || !isset($props[PR_PARENT_SOURCE_KEY]) || !isset($props[PR_DISPLAY_NAME]) || !isset($props[PR_CONTAINER_CLASS])) {
         throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, folder data not available: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
     }
     // get the real parent source key from mapi
     if ($parent == "0") {
         $parentprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
         $parentfentryid = $parentprops[PR_IPM_SUBTREE_ENTRYID];
         $mapifolder = mapi_msgstore_openentry($this->store, $parentfentryid);
         $rootfolderprops = mapi_getprops($mapifolder, array(PR_SOURCE_KEY));
         $parent = bin2hex($rootfolderprops[PR_SOURCE_KEY]);
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->ImportFolderChange(): resolved AS parent '0' to sourcekey '%s'", $parent));
     }
     // a changed parent id means that the folder should be moved
     if (bin2hex($props[PR_PARENT_SOURCE_KEY]) !== $parent) {
         $sourceparentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, $props[PR_PARENT_SOURCE_KEY]);
         if (!$sourceparentfentryid) {
             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent source folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
         }
         $sourceparentfolder = mapi_msgstore_openentry($this->store, $sourceparentfentryid);
         if (!$sourceparentfolder) {
             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent source folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
         }
         $destparentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($parent));
         if (!$sourceparentfentryid) {
             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open destination folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
         }
         $destfolder = mapi_msgstore_openentry($this->store, $destparentfentryid);
         if (!$destfolder) {
             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open destination folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
         }
         // mapi_folder_copyfolder() fails if a folder with this name already exists -> MAPI_E_COLLISION
         if (!mapi_folder_copyfolder($sourceparentfolder, $entryid, $destfolder, $displayname, FOLDER_MOVE)) {
             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to move folder: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_FOLDEREXISTS);
         }
         // the parent changed, but we got a backendID as parent and have to return an AS folderid - the parent-backendId must be mapped at this point already
         if ($folder->parentid != 0) {
             $folder->parentid = ZPush::GetDeviceManager()->GetFolderIdForBackendId($parent);
         }
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->ImportFolderChange(): Moved folder '%s' with id: %s/%s from: %s to: %s/%s", $displayname, $folder->serverid, $folder->BackendId, bin2hex($props[PR_PARENT_SOURCE_KEY]), $folder->parentid, $parent_org));
         return $folder;
     }
     // update the display name
     $props = array(PR_DISPLAY_NAME => $displayname);
     mapi_setprops($mfolder, $props);
     mapi_savechanges($mfolder);
     if (mapi_last_hresult()) {
         throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, mapi_savechanges() failed: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
     }
     ZLog::Write(LOGLEVEL_DEBUG, "Imported changes for folder: {$id}");
     return true;
 }
Ejemplo n.º 13
0
 /**
  * Sends an email notification to the user containing the data the user tried to save.
  *
  * @param SyncObject $message
  * @param SyncObject $oldmessage
  * @return void
  */
 private function sendNotificationEmail($message, $oldmessage)
 {
     // get email address and full name of the user
     $userinfo = ZPush::GetBackend()->GetUserDetails(Request::GetAuthUser());
     // get the name of the folder
     $foldername = "unknown";
     $folderid = bin2hex($this->folderid);
     $folders = ZPush::GetAdditionalSyncFolders();
     if (isset($folders[$folderid]) && isset($folders[$folderid]->displayname)) {
         $foldername = $folders[$folderid]->displayname;
     }
     // get the differences between the two objects
     $data = substr(get_class($oldmessage), 4) . "\r\n";
     // get the suppported fields as we need them to determine the ghosted properties
     $supportedFields = ZPush::GetDeviceManager()->GetSupportedFields(ZPush::GetDeviceManager()->GetFolderIdForBackendId($folderid));
     $dataarray = $oldmessage->EvaluateAndCompare($message, @constant('READ_ONLY_NOTIFY_YOURDATA'), $supportedFields);
     foreach ($dataarray as $key => $value) {
         $value = str_replace("\r", "", $value);
         $value = str_replace("\n", str_pad("\r\n", 25), $value);
         $data .= str_pad(ucfirst($key) . ":", 25) . $value . "\r\n";
     }
     // build a simple mime message
     $toEmail = $userinfo['emailaddress'];
     $mail = "From: Z-Push <no-reply>\r\n";
     $mail .= "To: {$toEmail}\r\n";
     $mail .= "Content-Type: text/plain; charset=utf-8\r\n";
     $mail .= "Subject: " . @constant('READ_ONLY_NOTIFY_SUBJECT') . "\r\n\r\n";
     $mail .= @constant('READ_ONLY_NOTIFY_BODY') . "\r\n";
     // replace values of template
     $mail = str_replace("**USERFULLNAME**", $userinfo['fullname'], $mail);
     $mail = str_replace("**DATE**", strftime(@constant('READ_ONLY_NOTIFY_DATE_FORMAT')), $mail);
     $mail = str_replace("**TIME**", strftime(@constant('READ_ONLY_NOTIFY_TIME_FORMAT')), $mail);
     $mail = str_replace("**FOLDERNAME**", $foldername, $mail);
     $mail = str_replace("**MOBILETYPE**", Request::GetDeviceType(), $mail);
     $mail = str_replace("**MOBILEDEVICEID**", Request::GetDeviceID(), $mail);
     $mail = str_replace("**DIFFERENCES**", $data, $mail);
     // user send email to himself
     $m = new SyncSendMail();
     $m->saveinsent = false;
     $m->replacemime = true;
     $m->mime = $mail;
     ZPush::GetBackend()->SendMail($m);
 }
Ejemplo n.º 14
0
 /**
  * Authenticates the user with the configured Zarafa server
  *
  * @param string        $username
  * @param string        $domain
  * @param string        $password
  *
  * @access public
  * @return boolean
  * @throws AuthenticationRequiredException
  */
 public function Logon($user, $domain, $pass)
 {
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->Logon(): Trying to authenticate user '%s'..", $user));
     $this->mainUser = strtolower($user);
     try {
         // check if notifications are available in php-mapi
         if (function_exists('mapi_feature') && mapi_feature('LOGONFLAGS')) {
             // send Z-Push version and user agent to ZCP - ZP-589
             if (Utils::CheckMapiExtVersion('7.2.0')) {
                 $zpush_version = 'Z-Push_' . @constant('ZPUSH_VERSION');
                 $user_agent = ZPush::GetDeviceManager()->GetUserAgent();
                 $this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER, null, null, 0, $zpush_version, $user_agent);
             } else {
                 $this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER, null, null, 0);
             }
             $this->notifications = true;
         } else {
             $this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER);
             $this->notifications = false;
         }
         if (mapi_last_hresult()) {
             ZLog::Write(LOGLEVEL_ERROR, sprintf("ZarafaBackend->Logon(): login failed with error code: 0x%X", mapi_last_hresult()));
             if (mapi_last_hresult() == MAPI_E_NETWORK_ERROR) {
                 throw new HTTPReturnCodeException("Error connecting to ZCP (login)", 503, null, LOGLEVEL_INFO);
             }
         }
     } catch (MAPIException $ex) {
         throw new AuthenticationRequiredException($ex->getDisplayMessage());
     }
     if (!$this->session) {
         ZLog::Write(LOGLEVEL_WARN, sprintf("ZarafaBackend->Logon(): logon failed for user '%s'", $user));
         $this->defaultstore = false;
         return false;
     }
     // Get/open default store
     $this->defaultstore = $this->openMessageStore($this->mainUser);
     if (mapi_last_hresult() == MAPI_E_FAILONEPROVIDER) {
         throw new HTTPReturnCodeException("Error connecting to ZCP (open store)", 503, null, LOGLEVEL_INFO);
     }
     if ($this->defaultstore === false) {
         throw new AuthenticationRequiredException(sprintf("ZarafaBackend->Logon(): User '%s' has no default store", $user));
     }
     $this->store = $this->defaultstore;
     $this->storeName = $this->mainUser;
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->Logon(): User '%s' is authenticated", $user));
     $this->isZPushEnabled();
     // check if this is a Zarafa 7 store with unicode support
     MAPIUtils::IsUnicodeStore($this->store);
     return true;
 }
Ejemplo n.º 15
0
 /**
  * Searches for the emails on the server
  *
  * @param ContentParameter $cpo
  *
  * @return array
  */
 public function GetMailboxSearchResults($cpo)
 {
     $searchFolder = $this->getSearchFolder();
     $searchRestriction = $this->getSearchRestriction($cpo);
     $searchRange = explode('-', $cpo->GetSearchRange());
     $searchFolderId = $cpo->GetSearchFolderid();
     $searchFolders = array();
     // search only in required folders
     if (!empty($searchFolderId)) {
         $searchFolderEntryId = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($searchFolderId));
         $searchFolders[] = $searchFolderEntryId;
     } else {
         $tmp = mapi_getprops($this->store, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_IPM_SUBTREE_ENTRYID));
         $searchFolders[] = $tmp[PR_IPM_SUBTREE_ENTRYID];
     }
     $items = array();
     $flags = 0;
     // if subfolders are required, do a recursive search
     if ($cpo->GetSearchDeepTraversal()) {
         $flags |= SEARCH_RECURSIVE;
     }
     mapi_folder_setsearchcriteria($searchFolder, $searchRestriction, $searchFolders, $flags);
     $table = mapi_folder_getcontentstable($searchFolder);
     $searchStart = time();
     // do the search and wait for all the results available
     while (time() - $searchStart < SEARCH_WAIT) {
         $searchcriteria = mapi_folder_getsearchcriteria($searchFolder);
         if (($searchcriteria["searchstate"] & SEARCH_REBUILD) == 0) {
             break;
         }
         // Search is done
         sleep(1);
     }
     // if the search range is set limit the result to it, otherwise return all found messages
     $rows = is_array($searchRange) && isset($searchRange[0]) && isset($searchRange[1]) ? mapi_table_queryrows($table, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY), $searchRange[0], $searchRange[1] - $searchRange[0] + 1) : mapi_table_queryrows($table, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY), 0, SEARCH_MAXRESULTS);
     $cnt = count($rows);
     $items['searchtotal'] = $cnt;
     $items["range"] = $cpo->GetSearchRange();
     for ($i = 0; $i < $cnt; $i++) {
         $items[$i]['class'] = 'Email';
         $items[$i]['longid'] = ZPush::GetDeviceManager()->GetFolderIdForBackendId(bin2hex($rows[$i][PR_PARENT_SOURCE_KEY])) . ":" . bin2hex($rows[$i][PR_SOURCE_KEY]);
         $items[$i]['folderid'] = bin2hex($rows[$i][PR_PARENT_SOURCE_KEY]);
     }
     return $items;
 }