/**
  * Handles a webservice command
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  * @throws SoapFault
  */
 public function Handle($commandCode)
 {
     if (Request::GetDeviceType() !== "webservice" || Request::GetDeviceID() !== "webservice") {
         throw new FatalException("Invalid device id and type for webservice execution");
     }
     if (Request::GetGETUser() != Request::GetAuthUser()) {
         ZLog::Write(LOGLEVEL_INFO, sprintf("Webservice::HandleWebservice('%s'): user '%s' executing action for user '%s'", $commandCode, Request::GetAuthUser(), Request::GetGETUser()));
     }
     // initialize non-wsdl soap server
     $this->server = new SoapServer(null, array('uri' => "http://z-push.sf.net/webservice"));
     // the webservice command is handled by its class
     if ($commandCode == ZPush::COMMAND_WEBSERVICE_DEVICE) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): executing WebserviceDevice service", $commandCode));
         include_once 'webservicedevice.php';
         $this->server->setClass("WebserviceDevice");
     }
     // the webservice command is handled by its class
     if ($commandCode == ZPush::COMMAND_WEBSERVICE_USERS) {
         if (!defined("ALLOW_WEBSERVICE_USERS_ACCESS") || ALLOW_WEBSERVICE_USERS_ACCESS !== true) {
             throw new HTTPReturnCodeException(sprintf("Access to the WebserviceUsers service is disabled in configuration. Enable setting ALLOW_WEBSERVICE_USERS_ACCESS.", Request::GetAuthUser()), 403);
         }
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): executing WebserviceUsers service", $commandCode));
         if (ZPush::GetBackend()->Setup("SYSTEM", true) == false) {
             throw new AuthenticationRequiredException(sprintf("User '%s' has no admin privileges", Request::GetAuthUser()));
         }
         include_once 'webserviceusers.php';
         $this->server->setClass("WebserviceUsers");
     }
     $this->server->handle();
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): sucessfully sent %d bytes", $commandCode, ob_get_length()));
     return true;
 }
Example #2
0
 /**
  * Constructor of the combined backend
  *
  * @access public
  */
 public function BackendCombined()
 {
     parent::Backend();
     $this->config = BackendCombinedConfig::GetBackendCombinedConfig();
     foreach ($this->config['backends'] as $i => $b) {
         // load and instatiate backend
         ZPush::IncludeBackend($b['name']);
         $this->backends[$i] = new $b['name']($b['config']);
     }
     ZLog::Write(LOGLEVEL_INFO, sprintf("Combined %d backends loaded.", count($this->backends)));
 }
Example #3
0
 /**
  * Constructor of the combined backend
  *
  * @access public
  */
 public function BackendCombined()
 {
     parent::Backend();
     $this->config = BackendCombinedConfig::GetBackendCombinedConfig();
     $backend_values = array_unique(array_values($this->config['folderbackend']));
     foreach ($backend_values as $i) {
         ZPush::IncludeBackend($this->config['backends'][$i]['name']);
         $this->backends[$i] = new $this->config['backends'][$i]['name']();
     }
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined %d backends loaded.", count($this->backends)));
 }
Example #4
0
 /**
  * Returns a list of all known devices with users and when they synchronized for the first time
  *
  * @access public
  * @return array
  */
 public function ListDevicesDetails()
 {
     $devices = ZPushAdmin::ListDevices(false);
     $output = array();
     ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceUsers::ListLastSync(): found %d devices", count($devices)));
     ZPush::GetTopCollector()->AnnounceInformation(sprintf("Retrieved details of %d devices and getting users", count($devices)), true);
     foreach ($devices as $deviceId) {
         $output[$deviceId] = array();
         $users = ZPushAdmin::ListUsers($deviceId);
         foreach ($users as $user) {
             $output[$deviceId][$user] = ZPushAdmin::GetDeviceDetails($deviceId, $user);
         }
     }
     return $output;
 }
Example #5
0
 /**
  * Returns a list of folders of the Request::GetGETUser().
  * If the user has not enough permissions an empty result is returned.
  *
  * @access public
  * @return array
  */
 public function ListUserFolders()
 {
     $user = Request::GetGETUser();
     $output = array();
     $hasRights = ZPush::GetBackend()->Setup($user);
     ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceInfo::ListUserFolders(): permissions to open store '%s': %s", $user, Utils::PrintAsString($hasRights)));
     if ($hasRights) {
         $folders = ZPush::GetBackend()->GetHierarchy();
         ZPush::GetTopCollector()->AnnounceInformation(sprintf("Retrieved details of %d folders", count($folders)), true);
         foreach ($folders as $folder) {
             $folder->StripData();
             unset($folder->Store, $folder->flags, $folder->content, $folder->NoBackendFolder);
             $output[] = $folder;
         }
     }
     return $output;
 }
 /**
  * Only used to load additional folder sync information for hierarchy changes
  *
  * @param array    $state               current state of additional hierarchy folders
  *
  * @access public
  * @return boolean
  */
 public function Config($state, $flags = 0)
 {
     // we should never forward this changes to a backend
     if (!isset($this->destinationImporter)) {
         foreach ($state as $addKey => $addFolder) {
             ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : process folder '%s'", $addFolder->displayname));
             if (isset($addFolder->NoBackendFolder) && $addFolder->NoBackendFolder == true) {
                 $hasRights = ZPush::GetBackend()->Setup($addFolder->Store, true, $addFolder->serverid);
                 // delete the folder on the device
                 if (!$hasRights) {
                     // delete the folder only if it was an additional folder before, else ignore it
                     $synchedfolder = $this->GetFolder($addFolder->serverid);
                     if (isset($synchedfolder->NoBackendFolder) && $synchedfolder->NoBackendFolder == true) {
                         $this->ImportFolderDeletion($addFolder->serverid, $addFolder->parentid);
                     }
                     continue;
                 }
             }
             // add folder to the device - if folder is already on the device, nothing will happen
             $this->ImportFolderChange($addFolder);
         }
         // look for folders which are currently on the device if there are now not to be synched anymore
         $alreadyDeleted = $this->GetDeletedFolders();
         foreach ($this->ExportFolders(true) as $sid => $folder) {
             // we are only looking at additional folders
             if (isset($folder->NoBackendFolder)) {
                 // look if this folder is still in the list of additional folders and was not already deleted (e.g. missing permissions)
                 if (!array_key_exists($sid, $state) && !array_key_exists($sid, $alreadyDeleted)) {
                     ZLog::Write(LOGLEVEL_INFO, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : previously synchronized folder '%s' is not to be synched anymore. Sending delete to mobile.", $folder->displayname));
                     $this->ImportFolderDeletion($folder->serverid, $folder->parentid);
                 }
             }
         }
     }
     return true;
 }
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
require_once 'vendor/autoload.php';
require_once 'config.php';
/**
 * //TODO resync of single folders of a users device
 */
/************************************************
 * MAIN
 */
define('BASE_PATH_CLI', dirname(__FILE__) . "/");
set_include_path(get_include_path() . PATH_SEPARATOR . BASE_PATH_CLI);
try {
    ZPush::CheckConfig();
    ZLog::Initialize();
    ZPushAdminCLI::CheckEnv();
    ZPushAdminCLI::CheckOptions();
    if (!ZPushAdminCLI::SureWhatToDo()) {
        // show error message if available
        if (ZPushAdminCLI::GetErrorMessage()) {
            echo "ERROR: " . ZPushAdminCLI::GetErrorMessage() . "\n";
        }
        echo ZPushAdminCLI::UsageInstructions();
        exit(1);
    }
    ZPushAdminCLI::RunCommand();
} catch (ZPushException $zpe) {
    die(get_class($zpe) . ": " . $zpe->getMessage() . "\n");
}
 /**
  * Indicates if the next messages should be ignored (not be sent to the mobile!)
  *
  * @param string  $messageid        (opt) id of the message which is to be exported next
  * @param string  $folderid         (opt) parent id of the message
  * @param boolean $markAsIgnored    (opt) to peek without setting the next message to be
  *                                  ignored, set this value to false
  * @access public
  * @return boolean
  */
 public function IgnoreNextMessage($markAsIgnored = true, $messageid = false, $folderid = false)
 {
     // as the next message id is not available at all point this method is called, we use different indicators.
     // potentialbroken indicates that we know that the broken message should be exported next,
     // alltho we do not know for sure as it's export message orders can change
     // if the $messageid is available and matches then we are sure and only then really ignore it
     $potentialBroken = false;
     $realBroken = false;
     if (Request::GetCommandCode() == ZPush::COMMAND_SYNC && $this->ignore_messageid !== false) {
         $potentialBroken = true;
     }
     if ($messageid !== false && $this->ignore_messageid == $messageid) {
         $realBroken = true;
     }
     // this call is just to know what should be happening
     // no further actions necessary
     if ($markAsIgnored === false) {
         return $potentialBroken;
     }
     // we should really do something here
     // first we check if we are in the loop mode, if so,
     // we update the potential broken id message so we loop count the same message
     $changedData = false;
     // exclusive block
     if ($this->blockMutex()) {
         $loopdata = $this->hasData() ? $this->getData() : array();
         // check and initialize the array structure
         $this->checkArrayStructure($loopdata, $folderid);
         $current = $loopdata[self::$devid][self::$user][$folderid];
         // we found our broken message!
         if ($realBroken) {
             $this->ignore_messageid = false;
             $current['ignored'] = $messageid;
             $changedData = true;
             // check if this message was broken before - here we know that it still is and remove it from the tracking
             $brokenkey = self::BROKENMSGS . "-" . $folderid;
             if (isset($loopdata[self::$devid][self::$user][$brokenkey]) && isset($loopdata[self::$devid][self::$user][$brokenkey][$messageid])) {
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): previously broken message '%s' is still broken and will not be tracked anymore", $messageid));
                 unset($loopdata[self::$devid][self::$user][$brokenkey][$messageid]);
             }
         } else {
             // update potential id if looping on an item
             if (isset($current['loopcount'])) {
                 $current['potential'] = $messageid;
                 // this message should be the broken one, but is not!!
                 // we should reset the loop count because this is certainly not the broken one
                 if ($potentialBroken) {
                     $current['loopcount'] = 1;
                     ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IgnoreNextMessage(): this should be the broken one, but is not! Resetting loop count.");
                 }
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): Loop mode, potential broken message id '%s'", $current['potential']));
                 $changedData = true;
             }
         }
         // update loop data
         if ($changedData == true) {
             $loopdata[self::$devid][self::$user][$folderid] = $current;
             $ok = $this->setData($loopdata);
         }
         $this->releaseMutex();
     }
     // end exclusive block
     if ($realBroken) {
         ZPush::GetTopCollector()->AnnounceInformation("Broken message ignored", true);
     }
     return $realBroken;
 }
Example #9
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');
Example #10
0
 /**
  * Does the complete autodiscover.
  * @access public
  * @throws AuthenticationRequiredException if login to the backend failed.
  * @throws ZPushException if the incoming XML is invalid..
  *
  * @return void
  */
 public function DoAutodiscover()
 {
     $response = "";
     try {
         $incomingXml = $this->getIncomingXml();
         $backend = ZPush::GetBackend();
         $username = $this->login($backend, $incomingXml);
         $userDetails = $backend->GetUserDetails($username);
         $email = $this->getAttribFromUserDetails($userDetails, 'emailaddress') ? $this->getAttribFromUserDetails($userDetails, 'emailaddress') : $incomingXml->Request->EMailAddress;
         $userFullname = $this->getAttribFromUserDetails($userDetails, 'fullname') ? $this->getAttribFromUserDetails($userDetails, 'fullname') : $email;
         ZLog::Write(LOGLEVEL_WBXML, sprintf("Resolved user's '%s' fullname to '%s'", $username, $userFullname));
         // At the moment Z-Push only supports mobile response schema for autodiscover. Send empty response if the client request outlook response schema.
         if ($incomingXml->Request->AcceptableResponseSchema == ZPushAutodiscover::ACCEPTABLERESPONSESCHEMAMOBILESYNC) {
             $response = $this->createResponse($email, $userFullname);
             setcookie("membername", $username);
         }
     } catch (AuthenticationRequiredException $ex) {
         if (isset($incomingXml)) {
             ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover because login failed for user with email '%s'", $incomingXml->Request->EMailAddress));
         } else {
             ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover incorrect request: '%s'", $ex->getMessage()));
         }
         http_response_code(401);
         header('WWW-Authenticate: Basic realm="ZPush"');
     } catch (ZPushException $ex) {
         ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover because of ZPushException. Error: %s", $ex->getMessage()));
         if (!headers_sent()) {
             header('HTTP/1.1 ' . $ex->getHTTPCodeString());
             foreach ($ex->getHTTPHeaders() as $h) {
                 header($h);
             }
         }
     }
     $this->sendResponse($response);
 }
Example #11
0
 /**
  * Handles creates, updates or deletes of a folder
  * issued by the commands FolderCreate, FolderUpdate and FolderDelete
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     $el = self::$decoder->getElement();
     if ($el[EN_TYPE] != EN_TYPE_STARTTAG) {
         return false;
     }
     $create = $update = $delete = false;
     if ($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERCREATE) {
         $create = true;
     } else {
         if ($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERUPDATE) {
             $update = true;
         } else {
             if ($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERDELETE) {
                 $delete = true;
             }
         }
     }
     if (!$create && !$update && !$delete) {
         return false;
     }
     // SyncKey
     if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) {
         return false;
     }
     $synckey = self::$decoder->getElementContent();
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     // ServerID
     $serverid = false;
     if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID)) {
         $serverid = self::$decoder->getElementContent();
         if (!self::$decoder->getElementEndTag()) {
             return false;
         }
     }
     // Parent
     $parentid = false;
     // when creating or updating more information is necessary
     if (!$delete) {
         if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_PARENTID)) {
             $parentid = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         // Displayname
         if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_DISPLAYNAME)) {
             return false;
         }
         $displayname = self::$decoder->getElementContent();
         if (!self::$decoder->getElementEndTag()) {
             return false;
         }
         // Type
         $type = false;
         if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_TYPE)) {
             $type = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
     }
     // endtag foldercreate, folderupdate, folderdelete
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     $status = SYNC_FSSTATUS_SUCCESS;
     // Get state of hierarchy
     try {
         $syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey);
         $newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey);
         // Over the ChangesWrapper the HierarchyCache is notified about all changes
         $changesMem = self::$deviceManager->GetHierarchyChangesWrapper();
         // the hierarchyCache should now fully be initialized - check for changes in the additional folders
         $changesMem->Config(ZPush::GetAdditionalSyncFolders());
         // there are unprocessed changes in the hierarchy, trigger resync
         if ($changesMem->GetChangeCount() > 0) {
             throw new StatusException("HandleFolderChange() can not proceed as there are unprocessed hierarchy changes", SYNC_FSSTATUS_SERVERERROR);
         }
         // any additional folders can not be modified!
         if ($serverid !== false && ZPush::GetAdditionalSyncFolderStore($serverid)) {
             throw new StatusException("HandleFolderChange() can not change additional folders which are configured", SYNC_FSSTATUS_SYSTEMFOLDER);
         }
         // switch user store if this this happens inside an additional folder
         // if this is an additional folder the backend has to be setup correctly
         if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($parentid != false ? $parentid : $serverid))) {
             throw new StatusException(sprintf("HandleFolderChange() could not Setup() the backend for folder id '%s'", $parentid != false ? $parentid : $serverid), SYNC_FSSTATUS_SERVERERROR);
         }
     } catch (StateNotFoundException $snfex) {
         $status = SYNC_FSSTATUS_SYNCKEYERROR;
     } catch (StatusException $stex) {
         $status = $stex->getCode();
     }
     // set $newsynckey in case of an error
     if (!isset($newsynckey)) {
         $newsynckey = $synckey;
     }
     if ($status == SYNC_FSSTATUS_SUCCESS) {
         try {
             // Configure importer with last state
             $importer = self::$backend->GetImporter();
             $importer->Config($syncstate);
             // the messages from the PIM will be forwarded to the real importer
             $changesMem->SetDestinationImporter($importer);
             // process incoming change
             if (!$delete) {
                 // Send change
                 $folder = new SyncFolder();
                 $folder->serverid = $serverid;
                 $folder->parentid = $parentid;
                 $folder->displayname = $displayname;
                 $folder->type = $type;
                 $serverid = $changesMem->ImportFolderChange($folder);
             } else {
                 // delete folder
                 $changesMem->ImportFolderDeletion($serverid, 0);
             }
         } catch (StatusException $stex) {
             $status = $stex->getCode();
         }
     }
     self::$encoder->startWBXML();
     if ($create) {
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERCREATE);
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
         self::$encoder->content($newsynckey);
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID);
         self::$encoder->content($serverid);
         self::$encoder->endTag();
         self::$encoder->endTag();
     } elseif ($update) {
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERUPDATE);
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
         self::$encoder->content($newsynckey);
         self::$encoder->endTag();
         self::$encoder->endTag();
     } elseif ($delete) {
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERDELETE);
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
         self::$encoder->content($newsynckey);
         self::$encoder->endTag();
         self::$encoder->endTag();
     }
     self::$topCollector->AnnounceInformation(sprintf("Operation status %d", $status), true);
     // Save the sync state for the next time
     if (isset($importer)) {
         self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $importer->GetState());
     }
     return true;
 }
Example #12
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;
 }
Example #13
0
 /**
  * Handles the Search command
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     $searchrange = '0';
     $cpo = new ContentParameters();
     if (!self::$decoder->getElementStartTag(SYNC_SEARCH_SEARCH)) {
         return false;
     }
     // TODO check: possible to search in other stores?
     if (!self::$decoder->getElementStartTag(SYNC_SEARCH_STORE)) {
         return false;
     }
     if (!self::$decoder->getElementStartTag(SYNC_SEARCH_NAME)) {
         return false;
     }
     $searchname = strtoupper(self::$decoder->getElementContent());
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     if (!self::$decoder->getElementStartTag(SYNC_SEARCH_QUERY)) {
         return false;
     }
     // check if it is a content of an element (= GAL search)
     // or a starttag (= mailbox or documentlibrary search)
     $searchquery = self::$decoder->getElementContent();
     if ($searchquery && !self::$decoder->getElementEndTag()) {
         return false;
     }
     if ($searchquery === false) {
         $cpo->SetSearchName($searchname);
         if (self::$decoder->getElementStartTag(SYNC_SEARCH_AND)) {
             if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
                 $searchfolderid = self::$decoder->getElementContent();
                 $cpo->SetSearchFolderid($searchfolderid);
                 if (!self::$decoder->getElementEndTag()) {
                     // SYNC_FOLDERTYPE
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                 $searchclass = self::$decoder->getElementContent();
                 $cpo->SetSearchClass($searchclass);
                 if (!self::$decoder->getElementEndTag()) {
                     // SYNC_FOLDERTYPE
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) {
                 $searchfolderid = self::$decoder->getElementContent();
                 $cpo->SetSearchFolderid($searchfolderid);
                 if (!self::$decoder->getElementEndTag()) {
                     // SYNC_FOLDERTYPE
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_SEARCH_FREETEXT)) {
                 $searchfreetext = self::$decoder->getElementContent();
                 $cpo->SetSearchFreeText($searchfreetext);
                 if (!self::$decoder->getElementEndTag()) {
                     // SYNC_SEARCH_FREETEXT
                     return false;
                 }
             }
             //TODO - review
             if (self::$decoder->getElementStartTag(SYNC_SEARCH_GREATERTHAN)) {
                 if (self::$decoder->getElementStartTag(SYNC_POOMMAIL_DATERECEIVED)) {
                     $datereceivedgreater = true;
                     if (($dam = self::$decoder->getElementContent()) !== false) {
                         $datereceivedgreater = true;
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     $cpo->SetSearchDateReceivedGreater($datereceivedgreater);
                 }
                 if (self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) {
                     $searchvalue = self::$decoder->getElementContent();
                     $cpo->SetSearchValueGreater($searchvalue);
                     if (!self::$decoder->getElementEndTag()) {
                         // SYNC_SEARCH_VALUE
                         return false;
                     }
                 }
                 if (!self::$decoder->getElementEndTag()) {
                     // SYNC_SEARCH_GREATERTHAN
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_SEARCH_LESSTHAN)) {
                 if (self::$decoder->getElementStartTag(SYNC_POOMMAIL_DATERECEIVED)) {
                     $datereceivedless = true;
                     if (($dam = self::$decoder->getElementContent()) !== false) {
                         $datereceivedless = true;
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     $cpo->SetSearchDateReceivedLess($datereceivedless);
                 }
                 if (self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) {
                     $searchvalue = self::$decoder->getElementContent();
                     $cpo->SetSearchValueLess($searchvalue);
                     if (!self::$decoder->getElementEndTag()) {
                         // SYNC_SEARCH_VALUE
                         return false;
                     }
                 }
                 if (!self::$decoder->getElementEndTag()) {
                     // SYNC_SEARCH_LESSTHAN
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_SEARCH_FREETEXT)) {
                 $searchfreetext = self::$decoder->getElementContent();
                 $cpo->SetSearchFreeText($searchfreetext);
                 if (!self::$decoder->getElementEndTag()) {
                     // SYNC_SEARCH_FREETEXT
                     return false;
                 }
             }
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_SEARCH_AND
                 return false;
             }
         } elseif (self::$decoder->getElementStartTag(SYNC_SEARCH_EQUALTO)) {
             // linkid can be an empty tag as well as have value
             if (self::$decoder->getElementStartTag(SYNC_DOCUMENTLIBRARY_LINKID)) {
                 if (($linkId = self::$decoder->getElementContent()) !== false) {
                     $cpo->SetLinkId($linkId);
                     if (!self::$decoder->getElementEndTag()) {
                         // SYNC_DOCUMENTLIBRARY_LINKID
                         return false;
                     }
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) {
                 $searchvalue = self::$decoder->getElementContent();
                 $cpo->SetSearchValueLess($searchvalue);
                 if (!self::$decoder->getElementEndTag()) {
                     // SYNC_SEARCH_VALUE
                     return false;
                 }
             }
             if (!self::$decoder->getElementEndTag()) {
                 // SYNC_SEARCH_EQUALTO
                 return false;
             }
         }
         if (!self::$decoder->getElementEndTag()) {
             // SYNC_SEARCH_QUERY
             return false;
         }
     }
     if (self::$decoder->getElementStartTag(SYNC_SEARCH_OPTIONS)) {
         while (1) {
             if (self::$decoder->getElementStartTag(SYNC_SEARCH_RANGE)) {
                 $searchrange = self::$decoder->getElementContent();
                 $cpo->SetSearchRange($searchrange);
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_SEARCH_REBUILDRESULTS)) {
                 $rebuildresults = true;
                 if (($dam = self::$decoder->getElementContent()) !== false) {
                     $rebuildresults = true;
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 $cpo->SetSearchRebuildResults($rebuildresults);
             }
             if (self::$decoder->getElementStartTag(SYNC_SEARCH_DEEPTRAVERSAL)) {
                 $deeptraversal = true;
                 if (($dam = self::$decoder->getElementContent()) !== false) {
                     $deeptraversal = true;
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 $cpo->SetSearchDeepTraversal($deeptraversal);
             }
             if (self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) {
                 $cpo->SetMimeSupport(self::$decoder->getElementContent());
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             //TODO body preferences
             while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) {
                 if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) {
                     $bptype = self::$decoder->getElementContent();
                     $cpo->BodyPreference($bptype);
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) {
                     $cpo->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent());
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) {
                     $cpo->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent());
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 if (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) {
                     $cpo->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;
             }
         }
     }
     if (!self::$decoder->getElementEndTag()) {
         //store
         return false;
     }
     if (!self::$decoder->getElementEndTag()) {
         //search
         return false;
     }
     // get SearchProvider
     $searchprovider = ZPush::GetSearchProvider();
     $status = SYNC_SEARCHSTATUS_SUCCESS;
     $rows = array();
     // TODO support other searches
     if ($searchprovider->SupportsType($searchname)) {
         $storestatus = SYNC_SEARCHSTATUS_STORE_SUCCESS;
         try {
             if ($searchname == ISearchProvider::SEARCH_GAL) {
                 //get search results from the searchprovider
                 $rows = $searchprovider->GetGALSearchResults($searchquery, $searchrange);
             } elseif ($searchname == ISearchProvider::SEARCH_MAILBOX) {
                 $rows = $searchprovider->GetMailboxSearchResults($cpo);
             }
         } catch (StatusException $stex) {
             $storestatus = $stex->getCode();
         }
     } else {
         $rows = array('searchtotal' => 0);
         $status = SYNC_SEARCHSTATUS_SERVERERROR;
         ZLog::Write(LOGLEVEL_WARN, sprintf("Searchtype '%s' is not supported.", $searchname));
         self::$topCollector->AnnounceInformation(sprintf("Unsupported type '%s''", $searchname), true);
     }
     $searchprovider->Disconnect();
     self::$topCollector->AnnounceInformation(sprintf("'%s' search found %d results", $searchname, $rows['searchtotal']), true);
     self::$encoder->startWBXML();
     self::$encoder->startTag(SYNC_SEARCH_SEARCH);
     self::$encoder->startTag(SYNC_SEARCH_STATUS);
     self::$encoder->content($status);
     self::$encoder->endTag();
     if ($status == SYNC_SEARCHSTATUS_SUCCESS) {
         self::$encoder->startTag(SYNC_SEARCH_RESPONSE);
         self::$encoder->startTag(SYNC_SEARCH_STORE);
         self::$encoder->startTag(SYNC_SEARCH_STATUS);
         self::$encoder->content($storestatus);
         self::$encoder->endTag();
         if (isset($rows['range'])) {
             $searchrange = $rows['range'];
             unset($rows['range']);
         }
         if (isset($rows['searchtotal'])) {
             $searchtotal = $rows['searchtotal'];
             unset($rows['searchtotal']);
         }
         if ($searchname == ISearchProvider::SEARCH_GAL) {
             if (is_array($rows) && !empty($rows)) {
                 foreach ($rows as $u) {
                     self::$encoder->startTag(SYNC_SEARCH_RESULT);
                     self::$encoder->startTag(SYNC_SEARCH_PROPERTIES);
                     self::$encoder->startTag(SYNC_GAL_DISPLAYNAME);
                     self::$encoder->content(isset($u[SYNC_GAL_DISPLAYNAME]) ? $u[SYNC_GAL_DISPLAYNAME] : "No name");
                     self::$encoder->endTag();
                     if (isset($u[SYNC_GAL_PHONE])) {
                         self::$encoder->startTag(SYNC_GAL_PHONE);
                         self::$encoder->content($u[SYNC_GAL_PHONE]);
                         self::$encoder->endTag();
                     }
                     if (isset($u[SYNC_GAL_OFFICE])) {
                         self::$encoder->startTag(SYNC_GAL_OFFICE);
                         self::$encoder->content($u[SYNC_GAL_OFFICE]);
                         self::$encoder->endTag();
                     }
                     if (isset($u[SYNC_GAL_TITLE])) {
                         self::$encoder->startTag(SYNC_GAL_TITLE);
                         self::$encoder->content($u[SYNC_GAL_TITLE]);
                         self::$encoder->endTag();
                     }
                     if (isset($u[SYNC_GAL_COMPANY])) {
                         self::$encoder->startTag(SYNC_GAL_COMPANY);
                         self::$encoder->content($u[SYNC_GAL_COMPANY]);
                         self::$encoder->endTag();
                     }
                     if (isset($u[SYNC_GAL_ALIAS])) {
                         self::$encoder->startTag(SYNC_GAL_ALIAS);
                         self::$encoder->content($u[SYNC_GAL_ALIAS]);
                         self::$encoder->endTag();
                     }
                     // Always send the firstname, even empty. Nokia needs this to display the entry
                     self::$encoder->startTag(SYNC_GAL_FIRSTNAME);
                     self::$encoder->content(isset($u[SYNC_GAL_FIRSTNAME]) ? $u[SYNC_GAL_FIRSTNAME] : "");
                     self::$encoder->endTag();
                     self::$encoder->startTag(SYNC_GAL_LASTNAME);
                     self::$encoder->content(isset($u[SYNC_GAL_LASTNAME]) ? $u[SYNC_GAL_LASTNAME] : "No name");
                     self::$encoder->endTag();
                     if (isset($u[SYNC_GAL_HOMEPHONE])) {
                         self::$encoder->startTag(SYNC_GAL_HOMEPHONE);
                         self::$encoder->content($u[SYNC_GAL_HOMEPHONE]);
                         self::$encoder->endTag();
                     }
                     if (isset($u[SYNC_GAL_MOBILEPHONE])) {
                         self::$encoder->startTag(SYNC_GAL_MOBILEPHONE);
                         self::$encoder->content($u[SYNC_GAL_MOBILEPHONE]);
                         self::$encoder->endTag();
                     }
                     self::$encoder->startTag(SYNC_GAL_EMAILADDRESS);
                     self::$encoder->content(isset($u[SYNC_GAL_EMAILADDRESS]) ? $u[SYNC_GAL_EMAILADDRESS] : "");
                     self::$encoder->endTag();
                     self::$encoder->endTag();
                     //result
                     self::$encoder->endTag();
                     //properties
                 }
             }
         } elseif ($searchname == ISearchProvider::SEARCH_MAILBOX) {
             foreach ($rows as $u) {
                 self::$encoder->startTag(SYNC_SEARCH_RESULT);
                 self::$encoder->startTag(SYNC_FOLDERTYPE);
                 self::$encoder->content($u['class']);
                 self::$encoder->endTag();
                 self::$encoder->startTag(SYNC_SEARCH_LONGID);
                 self::$encoder->content($u['longid']);
                 self::$encoder->endTag();
                 self::$encoder->startTag(SYNC_FOLDERID);
                 self::$encoder->content($u['folderid']);
                 self::$encoder->endTag();
                 self::$encoder->startTag(SYNC_SEARCH_PROPERTIES);
                 $tmp = explode(":", $u['longid']);
                 $message = self::$backend->Fetch($u['folderid'], $tmp[1], $cpo);
                 $message->Encode(self::$encoder);
                 self::$encoder->endTag();
                 //result
                 self::$encoder->endTag();
                 //properties
             }
         }
         // it seems that android 4 requires range and searchtotal
         // or it won't display the search results
         if (isset($searchrange)) {
             self::$encoder->startTag(SYNC_SEARCH_RANGE);
             self::$encoder->content($searchrange);
             self::$encoder->endTag();
         }
         if (isset($searchtotal) && $searchtotal > 0) {
             self::$encoder->startTag(SYNC_SEARCH_TOTAL);
             self::$encoder->content($searchtotal);
             self::$encoder->endTag();
         }
         self::$encoder->endTag();
         //store
         self::$encoder->endTag();
         //response
     }
     self::$encoder->endTag();
     //search
     return true;
 }
Example #14
0
 /**
  * Does the complete autodiscover.
  * @access public
  * @throws AuthenticationRequiredException if login to the backend failed.
  * @throws ZPushException if the incoming XML is invalid..
  *
  * @return void
  */
 public function DoAutodiscover()
 {
     if (!defined('REAL_BASE_PATH')) {
         define('REAL_BASE_PATH', str_replace('autodiscover/', '', BASE_PATH));
     }
     set_include_path(get_include_path() . PATH_SEPARATOR . REAL_BASE_PATH);
     $response = "";
     try {
         $incomingXml = $this->getIncomingXml();
         $backend = ZPush::GetBackend();
         $username = $this->login($backend, $incomingXml);
         $userDetails = $backend->GetUserDetails($username);
         $email = $this->getAttribFromUserDetails($userDetails, 'emailaddress') ? $this->getAttribFromUserDetails($userDetails, 'emailaddress') : $incomingXml->Request->EMailAddress;
         $userFullname = $this->getAttribFromUserDetails($userDetails, 'fullname') ? $this->getAttribFromUserDetails($userDetails, 'fullname') : $email;
         ZLog::Write(LOGLEVEL_WBXML, sprintf("Resolved user's '%s' fullname to '%s'", $username, $userFullname));
         $response = $this->createResponse($email, $userFullname);
         setcookie("membername", $username);
     } catch (AuthenticationRequiredException $ex) {
         if (isset($incomingXml)) {
             ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover because login failed for user with email '%s'", $incomingXml->Request->EMailAddress));
         } else {
             ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover incorrect request: '%s'", $ex->getMessage()));
         }
         header('HTTP/1.1 401 Unauthorized');
         header('WWW-Authenticate: Basic realm="ZPush"');
         http_response_code(401);
     } catch (ZPushException $ex) {
         ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover because of ZPushException. Error: %s", $ex->getMessage()));
         if (!headers_sent()) {
             header('HTTP/1.1 ' . $ex->getHTTPCodeString());
             foreach ($ex->getHTTPHeaders() as $h) {
                 header($h);
             }
         }
     }
     $this->sendResponse($response);
 }
Example #15
0
 /**
  * Handles the FolderSync command
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     // Maps serverid -> clientid for items that are received from the PIM
     $map = array();
     // Parse input
     if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC)) {
         return false;
     }
     if (!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) {
         return false;
     }
     $synckey = self::$decoder->getElementContent();
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     $status = SYNC_FSSTATUS_SUCCESS;
     $newsynckey = $synckey;
     try {
         $syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey);
         // We will be saving the sync state under 'newsynckey'
         $newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey);
     } catch (StateNotFoundException $snfex) {
         $status = SYNC_FSSTATUS_SYNCKEYERROR;
     } catch (StateInvalidException $sive) {
         $status = SYNC_FSSTATUS_SYNCKEYERROR;
     }
     // The ChangesWrapper caches all imports in-memory, so we can send a change count
     // before sending the actual data.
     // the HierarchyCache is notified and the changes from the PIM are transmitted to the actual backend
     $changesMem = self::$deviceManager->GetHierarchyChangesWrapper();
     // the hierarchyCache should now fully be initialized - check for changes in the additional folders
     $changesMem->Config(ZPush::GetAdditionalSyncFolders());
     // process incoming changes
     if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) {
         // Ignore <Count> if present
         if (self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) {
             self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         // Process the changes (either <Add>, <Modify>, or <Remove>)
         $element = self::$decoder->getElement();
         if ($element[EN_TYPE] != EN_TYPE_STARTTAG) {
             return false;
         }
         $importer = false;
         while (1) {
             $folder = new SyncFolder();
             if (!$folder->Decode(self::$decoder)) {
                 break;
             }
             try {
                 if ($status == SYNC_FSSTATUS_SUCCESS && !$importer) {
                     // Configure the backends importer with last state
                     $importer = self::$backend->GetImporter();
                     $importer->Config($syncstate);
                     // the messages from the PIM will be forwarded to the backend
                     $changesMem->forwardImporter($importer);
                 }
                 if ($status == SYNC_FSSTATUS_SUCCESS) {
                     switch ($element[EN_TAG]) {
                         case SYNC_ADD:
                         case SYNC_MODIFY:
                             $serverid = $changesMem->ImportFolderChange($folder);
                             break;
                         case SYNC_REMOVE:
                             $serverid = $changesMem->ImportFolderDeletion($folder);
                             break;
                     }
                     // TODO what does $map??
                     if ($serverid) {
                         $map[$serverid] = $folder->clientid;
                     }
                 } else {
                     ZLog::Write(LOGLEVEL_WARN, sprintf("Request->HandleFolderSync(): ignoring incoming folderchange for folder '%s' as status indicates problem.", $folder->displayname));
                     self::$topCollector->AnnounceInformation("Incoming change ignored", true);
                 }
             } catch (StatusException $stex) {
                 $status = $stex->getCode();
             }
         }
         if (!self::$decoder->getElementEndTag()) {
             return false;
         }
     } else {
         // check for a potential process loop like described in Issue ZP-5
         if ($synckey != "0" && self::$deviceManager->IsHierarchyFullResyncRequired()) {
             $status = SYNC_FSSTATUS_SYNCKEYERROR;
         }
         self::$deviceManager->AnnounceProcessStatus(false, $status);
     }
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     // We have processed incoming foldersync requests, now send the PIM
     // our changes
     // Output our WBXML reply now
     self::$encoder->StartWBXML();
     self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC);
     if ($status == SYNC_FSSTATUS_SUCCESS) {
         try {
             // do nothing if this is an invalid device id (like the 'validate' Androids internal client sends)
             if (!Request::IsValidDeviceID()) {
                 throw new StatusException(sprintf("Request::IsValidDeviceID() indicated that '%s' is not a valid device id", Request::GetDeviceID()), SYNC_FSSTATUS_SERVERERROR);
             }
             // Changes from backend are sent to the MemImporter and processed for the HierarchyCache.
             // The state which is saved is from the backend, as the MemImporter is only a proxy.
             $exporter = self::$backend->GetExporter();
             $exporter->Config($syncstate);
             $exporter->InitializeExporter($changesMem);
             // Stream all changes to the ImportExportChangesMem
             while (is_array($exporter->Synchronize())) {
             }
             // get the new state from the backend
             $newsyncstate = isset($exporter) ? $exporter->GetState() : "";
         } catch (StatusException $stex) {
             if ($stex->getCode() == SYNC_FSSTATUS_CODEUNKNOWN) {
                 $status = SYNC_FSSTATUS_SYNCKEYERROR;
             } else {
                 $status = $stex->getCode();
             }
         }
     }
     self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS);
     self::$encoder->content($status);
     self::$encoder->endTag();
     if ($status == SYNC_FSSTATUS_SUCCESS) {
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY);
         $synckey = $changesMem->IsStateChanged() ? $newsynckey : $synckey;
         self::$encoder->content($synckey);
         self::$encoder->endTag();
         // Stream folders directly to the PDA
         $streamimporter = new ImportChangesStream(self::$encoder, false);
         $changesMem->InitializeExporter($streamimporter);
         $changeCount = $changesMem->GetChangeCount();
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES);
         self::$encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT);
         self::$encoder->content($changeCount);
         self::$encoder->endTag();
         while ($changesMem->Synchronize()) {
         }
         self::$encoder->endTag();
         self::$topCollector->AnnounceInformation(sprintf("Outgoing %d folders", $changeCount), true);
         // everything fine, save the sync state for the next time
         if ($synckey == $newsynckey) {
             self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $newsyncstate);
         }
     }
     self::$encoder->endTag();
     return true;
 }
 /**
  * Loads the command handler and processes a command sent from the mobile
  *
  * @access public
  * @return boolean
  */
 public static function HandleRequest()
 {
     $handler = ZPush::GetRequestHandlerForCommand(Request::GetCommandCode());
     // TODO handle WBXML exceptions here and print stack
     return $handler->Handle(Request::GetCommandCode());
 }
Example #17
0
 /**
  * Reads and processes the request headers
  *
  * @access public
  * @return
  */
 public static function ProcessHeaders()
 {
     self::$headers = array_change_key_case(apache_request_headers(), CASE_LOWER);
     self::$useragent = isset(self::$headers["user-agent"]) ? self::$headers["user-agent"] : self::UNKNOWN;
     if (!isset(self::$asProtocolVersion)) {
         self::$asProtocolVersion = isset(self::$headers["ms-asprotocolversion"]) ? self::filterEvilInput(self::$headers["ms-asprotocolversion"], self::NUMBERSDOT_ONLY) : ZPush::GetLatestSupportedASVersion();
     }
     //if policykey is not yet set, try to set it from the header
     //the policy key might be set in Request::Initialize from the base64 encoded query
     if (!isset(self::$policykey)) {
         if (isset(self::$headers["x-ms-policykey"])) {
             self::$policykey = (int) self::filterEvilInput(self::$headers["x-ms-policykey"], self::NUMBERS_ONLY);
         } else {
             self::$policykey = 0;
         }
     }
     if (!empty($_SERVER['QUERY_STRING']) && Utils::IsBase64String($_SERVER['QUERY_STRING'])) {
         ZLog::Write(LOGLEVEL_DEBUG, "Using data from base64 encoded query string");
         if (isset(self::$policykey)) {
             self::$headers["x-ms-policykey"] = self::$policykey;
         }
         if (isset(self::$asProtocolVersion)) {
             self::$headers["ms-asprotocolversion"] = self::$asProtocolVersion;
         }
     }
     if (!isset(self::$acceptMultipart) && isset(self::$headers["ms-asacceptmultipart"]) && strtoupper(self::$headers["ms-asacceptmultipart"]) == "T") {
         self::$acceptMultipart = true;
     }
     ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request::ProcessHeaders() ASVersion: %s", self::$asProtocolVersion));
     if (defined('USE_X_FORWARDED_FOR_HEADER') && USE_X_FORWARDED_FOR_HEADER == true && isset(self::$headers["x-forwarded-for"])) {
         $forwardedIP = self::filterEvilInput(self::$headers["x-forwarded-for"], self::NUMBERSDOT_ONLY);
         if ($forwardedIP) {
             self::$remoteAddr = $forwardedIP;
             ZLog::Write(LOGLEVEL_INFO, sprintf("'X-Forwarded-for' indicates remote IP: %s", self::$remoteAddr));
         }
     }
 }
 /**
  * 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;
     }
 }
Example #19
0
 /**
  * Constructor
  *
  * @access public
  */
 public function ZPushTop()
 {
     $this->starttime = time();
     $this->currenttime = time();
     $this->action = "";
     $this->filter = false;
     $this->status = false;
     $this->statusexpire = 0;
     $this->helpexpire = 0;
     $this->doingTail = false;
     $this->wide = false;
     $this->terminate = false;
     $this->showPush = true;
     $this->showOption = self::SHOW_DEFAULT;
     $this->showTermSec = self::SHOW_TERM_DEFAULT_TIME;
     $this->scrSize = array('width' => 80, 'height' => 24);
     $this->pingInterval = defined('PING_INTERVAL') && PING_INTERVAL > 0 ? PING_INTERVAL : 12;
     // get a TopCollector
     $this->topCollector = ZPush::GetTopCollector();
 }
Example #20
0
 /**
  * Marks a a device of the Request::GetGETUser() for resynchronization
  *
  * @param string    $deviceId       the device id
  *
  * @access public
  * @return boolean
  * @throws SoapFault
  */
 public function ResyncDevice($deviceId)
 {
     $deviceId = preg_replace("/[^A-Za-z0-9]/", "", $deviceId);
     ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceDevice::ResyncDevice('%s'): mark device of user '%s' for resynchronization", $deviceId, Request::GetGETUser()));
     if (!ZPushAdmin::ResyncDevice(Request::GetGETUser(), $deviceId)) {
         ZPush::GetTopCollector()->AnnounceInformation(ZLog::GetLastMessage(LOGLEVEL_ERROR), true);
         throw new SoapFault("ERROR", ZLog::GetLastMessage(LOGLEVEL_ERROR));
     }
     ZPush::GetTopCollector()->AnnounceInformation(sprintf("Resync requested - device id '%s'", $deviceId), true);
     return true;
 }
Example #21
0
 /**
  * Reads an email object from MAPI
  *
  * @param mixed             $mapimessage
  * @param ContentParameters $contentparameters
  *
  * @access private
  * @return SyncEmail
  */
 private function getEmail($mapimessage, $contentparameters)
 {
     $message = new SyncMail();
     $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetEmailMapping());
     $emailproperties = MAPIMapping::GetEmailProperties();
     $messageprops = $this->getProps($mapimessage, $emailproperties);
     if (isset($messageprops[PR_SOURCE_KEY])) {
         $sourcekey = $messageprops[PR_SOURCE_KEY];
     } else {
         return false;
     }
     //set the body according to contentparameters and supported AS version
     $this->setMessageBody($mapimessage, $contentparameters, $message);
     $fromname = $fromaddr = "";
     if (isset($messageprops[$emailproperties["representingname"]])) {
         // remove encapsulating double quotes from the representingname
         $fromname = preg_replace('/^\\"(.*)\\"$/', "\${1}", $messageprops[$emailproperties["representingname"]]);
     }
     if (isset($messageprops[$emailproperties["representingentryid"]])) {
         $fromaddr = $this->getSMTPAddressFromEntryID($messageprops[$emailproperties["representingentryid"]]);
     }
     if ($fromname == $fromaddr) {
         $fromname = "";
     }
     if ($fromname) {
         $from = "\"" . w2u($fromname) . "\" <" . w2u($fromaddr) . ">";
     } else {
         //START CHANGED dw2412 HTC shows "error" if sender name is unknown
         $from = "\"" . w2u($fromaddr) . "\" <" . w2u($fromaddr) . ">";
     }
     //END CHANGED dw2412 HTC shows "error" if sender name is unknown
     $message->from = $from;
     // process Meeting Requests
     if (isset($message->messageclass) && strpos($message->messageclass, "IPM.Schedule.Meeting") === 0) {
         $message->meetingrequest = new SyncMeetingRequest();
         $this->getPropsFromMAPI($message->meetingrequest, $mapimessage, MAPIMapping::GetMeetingRequestMapping());
         $meetingrequestproperties = MAPIMapping::GetMeetingRequestProperties();
         $props = $this->getProps($mapimessage, $meetingrequestproperties);
         // Get the GOID
         if (isset($props[$meetingrequestproperties["goidtag"]])) {
             $message->meetingrequest->globalobjid = base64_encode($props[$meetingrequestproperties["goidtag"]]);
         }
         // Set Timezone
         if (isset($props[$meetingrequestproperties["timezonetag"]])) {
             $tz = $this->getTZFromMAPIBlob($props[$meetingrequestproperties["timezonetag"]]);
         } else {
             $tz = $this->getGMTTZ();
         }
         $message->meetingrequest->timezone = base64_encode(TimezoneUtil::GetSyncBlobFromTZ($tz));
         // send basedate if exception
         if (isset($props[$meetingrequestproperties["recReplTime"]]) || isset($props[$meetingrequestproperties["lidIsException"]]) && $props[$meetingrequestproperties["lidIsException"]] == true) {
             if (isset($props[$meetingrequestproperties["recReplTime"]])) {
                 $basedate = $props[$meetingrequestproperties["recReplTime"]];
                 $message->meetingrequest->recurrenceid = $this->getGMTTimeByTZ($basedate, $this->getGMTTZ());
             } else {
                 if (!isset($props[$meetingrequestproperties["goidtag"]]) || !isset($props[$meetingrequestproperties["recurStartTime"]]) || !isset($props[$meetingrequestproperties["timezonetag"]])) {
                     ZLog::Write(LOGLEVEL_WARN, "Missing property to set correct basedate for exception");
                 } else {
                     $basedate = Utils::ExtractBaseDate($props[$meetingrequestproperties["goidtag"]], $props[$meetingrequestproperties["recurStartTime"]]);
                     $message->meetingrequest->recurrenceid = $this->getGMTTimeByTZ($basedate, $tz);
                 }
             }
         }
         // Organizer is the sender
         if (strpos($message->messageclass, "IPM.Schedule.Meeting.Resp") === 0) {
             $message->meetingrequest->organizer = $message->to;
         } else {
             $message->meetingrequest->organizer = $message->from;
         }
         // Process recurrence
         if (isset($props[$meetingrequestproperties["isrecurringtag"]]) && $props[$meetingrequestproperties["isrecurringtag"]]) {
             $myrec = new SyncMeetingRequestRecurrence();
             // get recurrence -> put $message->meetingrequest as message so the 'alldayevent' is set correctly
             $this->getRecurrence($mapimessage, $props, $message->meetingrequest, $myrec, $tz);
             $message->meetingrequest->recurrences = array($myrec);
         }
         // Force the 'alldayevent' in the object at all times. (non-existent == 0)
         if (!isset($message->meetingrequest->alldayevent) || $message->meetingrequest->alldayevent == "") {
             $message->meetingrequest->alldayevent = 0;
         }
         // Instancetype
         // 0 = single appointment
         // 1 = master recurring appointment
         // 2 = single instance of recurring appointment
         // 3 = exception of recurring appointment
         $message->meetingrequest->instancetype = 0;
         if (isset($props[$meetingrequestproperties["isrecurringtag"]]) && $props[$meetingrequestproperties["isrecurringtag"]] == 1) {
             $message->meetingrequest->instancetype = 1;
         } else {
             if ((!isset($props[$meetingrequestproperties["isrecurringtag"]]) || $props[$meetingrequestproperties["isrecurringtag"]] == 0) && isset($message->meetingrequest->recurrenceid)) {
                 if (isset($props[$meetingrequestproperties["appSeqNr"]]) && $props[$meetingrequestproperties["appSeqNr"]] == 0) {
                     $message->meetingrequest->instancetype = 2;
                 } else {
                     $message->meetingrequest->instancetype = 3;
                 }
             }
         }
         // Disable reminder if it is off
         if (!isset($props[$meetingrequestproperties["reminderset"]]) || $props[$meetingrequestproperties["reminderset"]] == false) {
             $message->meetingrequest->reminder = "";
         } else {
             ///set the default reminder time to seconds
             if ($props[$meetingrequestproperties["remindertime"]] == 0x5ae980e1) {
                 $message->meetingrequest->reminder = 900;
             } else {
                 $message->meetingrequest->reminder = $props[$meetingrequestproperties["remindertime"]] * 60;
             }
         }
         // Set sensitivity to 0 if missing
         if (!isset($message->meetingrequest->sensitivity)) {
             $message->meetingrequest->sensitivity = 0;
         }
         // If the user is working from a location other than the office the busystatus should be interpreted as free.
         if (isset($message->meetingrequest->busystatus) && $message->meetingrequest->busystatus == fbWorkingElsewhere) {
             $message->meetingrequest->busystatus = fbFree;
         }
         // If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581
         if (isset($message->meetingrequest->busystatus) && $message->meetingrequest->busystatus == -1) {
             $message->meetingrequest->busystatus = fbTentative;
         }
         // if a meeting request response hasn't been processed yet,
         // do it so that the attendee status is updated on the mobile
         if (!isset($messageprops[$emailproperties["processed"]])) {
             // check if we are not sending the MR so we can process it - ZP-581
             $cuser = ZPush::GetBackend()->GetUserDetails(ZPush::GetBackend()->GetCurrentUsername());
             if (isset($cuser["emailaddress"]) && $cuser["emailaddress"] != $fromaddr) {
                 $req = new Meetingrequest($this->store, $mapimessage, $this->session);
                 if ($req->isMeetingRequestResponse()) {
                     $req->processMeetingRequestResponse();
                 }
                 if ($req->isMeetingCancellation()) {
                     $req->processMeetingCancellation();
                 }
             }
         }
         $message->contentclass = DEFAULT_CALENDAR_CONTENTCLASS;
     }
     // Add attachments
     $attachtable = mapi_message_getattachmenttable($mapimessage);
     $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM));
     $entryid = bin2hex($messageprops[$emailproperties["entryid"]]);
     foreach ($rows as $row) {
         if (isset($row[PR_ATTACH_NUM])) {
             if (Request::GetProtocolVersion() >= 12.0) {
                 $attach = new SyncBaseAttachment();
             } else {
                 $attach = new SyncAttachment();
             }
             $mapiattach = mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]);
             $attachprops = mapi_getprops($mapiattach, array(PR_ATTACH_LONG_FILENAME, PR_ATTACH_FILENAME, PR_ATTACHMENT_HIDDEN, PR_ATTACH_CONTENT_ID, PR_ATTACH_CONTENT_ID_W, PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W, PR_ATTACH_METHOD, PR_DISPLAY_NAME, PR_DISPLAY_NAME_W, PR_ATTACH_SIZE));
             if (isset($attachprops[PR_ATTACH_MIME_TAG]) && strpos(strtolower($attachprops[PR_ATTACH_MIME_TAG]), 'signed') !== false || isset($attachprops[PR_ATTACH_MIME_TAG_W]) && strpos(strtolower($attachprops[PR_ATTACH_MIME_TAG_W]), 'signed') !== false) {
                 continue;
             }
             // the displayname is handled equaly for all AS versions
             $attach->displayname = w2u(isset($attachprops[PR_ATTACH_LONG_FILENAME]) ? $attachprops[PR_ATTACH_LONG_FILENAME] : (isset($attachprops[PR_ATTACH_FILENAME]) ? $attachprops[PR_ATTACH_FILENAME] : (isset($attachprops[PR_DISPLAY_NAME]) ? $attachprops[PR_DISPLAY_NAME] : "attachment.bin")));
             // fix attachment name in case of inline images
             if ($attach->displayname == "inline.txt" && (isset($attachprops[PR_ATTACH_MIME_TAG]) || $attachprops[PR_ATTACH_MIME_TAG_W])) {
                 $mimetype = isset($attachprops[PR_ATTACH_MIME_TAG]) ? $attachprops[PR_ATTACH_MIME_TAG] : $attachprops[PR_ATTACH_MIME_TAG_W];
                 $mime = explode("/", $mimetype);
                 if (count($mime) == 2 && $mime[0] == "image") {
                     $attach->displayname = "inline." . $mime[1];
                 }
             }
             // set AS version specific parameters
             if (Request::GetProtocolVersion() >= 12.0) {
                 $attach->filereference = $entryid . ":" . $row[PR_ATTACH_NUM];
                 $attach->method = isset($attachprops[PR_ATTACH_METHOD]) ? $attachprops[PR_ATTACH_METHOD] : ATTACH_BY_VALUE;
                 // if displayname does not have the eml extension for embedde messages, android and WP devices won't open it
                 if ($attach->method == ATTACH_EMBEDDED_MSG) {
                     if (strtolower(substr($attach->displayname, -4)) != '.eml') {
                         $attach->displayname .= '.eml';
                     }
                 }
                 $attach->estimatedDataSize = $attachprops[PR_ATTACH_SIZE];
                 if (isset($attachprops[PR_ATTACH_CONTENT_ID]) && $attachprops[PR_ATTACH_CONTENT_ID]) {
                     $attach->contentid = $attachprops[PR_ATTACH_CONTENT_ID];
                 }
                 if (!isset($attach->contentid) && isset($attachprops[PR_ATTACH_CONTENT_ID_W]) && $attachprops[PR_ATTACH_CONTENT_ID_W]) {
                     $attach->contentid = $attachprops[PR_ATTACH_CONTENT_ID_W];
                 }
                 if (isset($attachprops[PR_ATTACHMENT_HIDDEN]) && $attachprops[PR_ATTACHMENT_HIDDEN]) {
                     $attach->isinline = 1;
                 }
                 if (!isset($message->asattachments)) {
                     $message->asattachments = array();
                 }
                 array_push($message->asattachments, $attach);
             } else {
                 $attach->attsize = $attachprops[PR_ATTACH_SIZE];
                 $attach->attname = $entryid . ":" . $row[PR_ATTACH_NUM];
                 if (!isset($message->attachments)) {
                     $message->attachments = array();
                 }
                 array_push($message->attachments, $attach);
             }
         }
     }
     // Get To/Cc as SMTP addresses (this is different from displayto and displaycc because we are putting
     // in the SMTP addresses as well, while displayto and displaycc could just contain the display names
     $message->to = array();
     $message->cc = array();
     $reciptable = mapi_message_getrecipienttable($mapimessage);
     $rows = mapi_table_queryallrows($reciptable, array(PR_RECIPIENT_TYPE, PR_DISPLAY_NAME, PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ENTRYID));
     foreach ($rows as $row) {
         $address = "";
         $fulladdr = "";
         $addrtype = isset($row[PR_ADDRTYPE]) ? $row[PR_ADDRTYPE] : "";
         if (isset($row[PR_SMTP_ADDRESS])) {
             $address = $row[PR_SMTP_ADDRESS];
         } elseif ($addrtype == "SMTP" && isset($row[PR_EMAIL_ADDRESS])) {
             $address = $row[PR_EMAIL_ADDRESS];
         } elseif ($addrtype == "ZARAFA" && isset($row[PR_ENTRYID])) {
             $address = $this->getSMTPAddressFromEntryID($row[PR_ENTRYID]);
         }
         $name = isset($row[PR_DISPLAY_NAME]) ? $row[PR_DISPLAY_NAME] : "";
         if ($name == "" || $name == $address) {
             $fulladdr = w2u($address);
         } else {
             if (substr($name, 0, 1) != '"' && substr($name, -1) != '"') {
                 $fulladdr = "\"" . w2u($name) . "\" <" . w2u($address) . ">";
             } else {
                 $fulladdr = w2u($name) . "<" . w2u($address) . ">";
             }
         }
         if ($row[PR_RECIPIENT_TYPE] == MAPI_TO) {
             array_push($message->to, $fulladdr);
         } else {
             if ($row[PR_RECIPIENT_TYPE] == MAPI_CC) {
                 array_push($message->cc, $fulladdr);
             }
         }
     }
     if (is_array($message->to) && !empty($message->to)) {
         $message->to = implode(", ", $message->to);
     }
     if (is_array($message->cc) && !empty($message->cc)) {
         $message->cc = implode(", ", $message->cc);
     }
     // without importance some mobiles assume "0" (low) - Mantis #439
     if (!isset($message->importance)) {
         $message->importance = IMPORTANCE_NORMAL;
     }
     //TODO contentclass and nativebodytype and internetcpid
     if (!isset($message->internetcpid)) {
         $message->internetcpid = defined('STORE_INTERNET_CPID') ? constant('STORE_INTERNET_CPID') : INTERNET_CPID_WINDOWS1252;
     }
     $this->setFlag($mapimessage, $message);
     if (!isset($message->contentclass)) {
         $message->contentclass = DEFAULT_EMAIL_CONTENTCLASS;
     }
     if (!isset($message->nativebodytype)) {
         $message->nativebodytype = $this->getNativeBodyType($messageprops);
     }
     // reply, reply to all, forward flags
     if (isset($message->lastverbexecuted) && $message->lastverbexecuted) {
         $message->lastverbexecuted = Utils::GetLastVerbExecuted($message->lastverbexecuted);
     }
     return $message;
 }
Example #22
0
 /**
  * UnLinks all states from a folder id
  * Old states are removed assisting the StateMachine to get rid of old data.
  * The UUID is then removed from the device
  *
  * @param ASDevice  $device
  * @param string    $folderid
  * @param boolean   $removeFromDevice       indicates if the device should be
  *                                          notified that the state was removed
  * @param boolean   $retrieveUUIDFromDevice indicates if the UUID should be retrieved from
  *                                          device. If not true this parameter will be used as UUID.
  *
  * @access public
  * @return boolean
  */
 public static function UnLinkState(&$device, $folderid, $removeFromDevice = true, $retrieveUUIDFromDevice = true)
 {
     if ($retrieveUUIDFromDevice === true) {
         $savedUuid = $device->GetFolderUUID($folderid);
     } else {
         $savedUuid = $retrieveUUIDFromDevice;
     }
     if ($savedUuid) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("StateManager::UnLinkState('%s'): saved state '%s' will be deleted.", $folderid, $savedUuid));
         ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::DEFTYPE, $savedUuid, self::FIXEDHIERARCHYCOUNTER * 2);
         ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::FOLDERDATA, $savedUuid);
         // CPO
         ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::FAILSAVE, $savedUuid, self::FIXEDHIERARCHYCOUNTER * 2);
         ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::BACKENDSTORAGE, $savedUuid, self::FIXEDHIERARCHYCOUNTER * 2);
         // remove all messages which could not be synched before
         $device->RemoveIgnoredMessage($folderid, false);
         if ($folderid === false && $savedUuid !== false) {
             ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::HIERARCHY, $savedUuid, self::FIXEDHIERARCHYCOUNTER * 2);
         }
     }
     // delete this id from the uuid cache
     if ($removeFromDevice) {
         return $device->SetFolderUUID(false, $folderid);
     } else {
         return true;
     }
 }
Example #23
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();
     }
 }
 /**
  * Handles the GetItemEstimate command
  * Returns an estimation of how many items will be synchronized at the next sync
  * This is mostly used to show something in the progress bar
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     $sc = new SyncCollections();
     if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE)) {
         return false;
     }
     if (!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERS)) {
         return false;
     }
     while (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDER)) {
         $spa = new SyncParameters();
         $spastatus = false;
         if (Request::GetProtocolVersion() >= 14.0) {
             if (self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
                 try {
                     $spa->SetSyncKey(self::$decoder->getElementContent());
                 } catch (StateInvalidException $siex) {
                     $spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED;
                 }
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) {
                 $spa->SetFolderId(self::$decoder->getElementContent());
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             // conversation mode requested
             if (self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) {
                 $spa->SetConversationMode(true);
                 if (($conversationmode = self::$decoder->getElementContent()) !== false) {
                     $spa->SetConversationMode((bool) $conversationmode);
                     if (!self::$decoder->getElementEndTag()) {
                         return false;
                     }
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_OPTIONS)) {
                 while (1) {
                     if (self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
                         $spa->SetFilterType(self::$decoder->getElementContent());
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) {
                         $spa->SetContentClass(self::$decoder->getElementContent());
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     if (self::$decoder->getElementStartTag(SYNC_MAXITEMS)) {
                         $spa->SetWindowSize($maxitems = self::$decoder->getElementContent());
                         if (!self::$decoder->getElementEndTag()) {
                             return false;
                         }
                     }
                     $e = self::$decoder->peek();
                     if ($e[EN_TYPE] == EN_TYPE_ENDTAG) {
                         self::$decoder->getElementEndTag();
                         break;
                     }
                 }
             }
         } else {
             //get items estimate does not necessarily send the folder type
             if (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERTYPE)) {
                 $spa->SetContentClass(self::$decoder->getElementContent());
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if (self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) {
                 $spa->SetFolderId(self::$decoder->getElementContent());
                 if (!self::$decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if (!self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) {
                 return false;
             }
             $spa->SetFilterType(self::$decoder->getElementContent());
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
             if (!self::$decoder->getElementStartTag(SYNC_SYNCKEY)) {
                 return false;
             }
             try {
                 $spa->SetSyncKey(self::$decoder->getElementContent());
             } catch (StateInvalidException $siex) {
                 $spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED;
             }
             if (!self::$decoder->getElementEndTag()) {
                 return false;
             }
         }
         if (!self::$decoder->getElementEndTag()) {
             return false;
         }
         //SYNC_GETITEMESTIMATE_FOLDER
         // Process folder data
         //In AS 14 request only collectionid is sent, without class
         if (!$spa->HasContentClass() && $spa->HasFolderId()) {
             try {
                 $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()));
             } catch (NoHierarchyCacheAvailableException $nhca) {
                 $spastatus = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID;
             }
         }
         // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy()
         if (!$spa->HasFolderId() && $spa->HasContentClass()) {
             $spa->SetFolderId(self::$deviceManager->GetFolderIdFromCacheByClass($spa->GetContentClass()));
         }
         // Add collection to SC and load state
         $sc->AddCollection($spa);
         if ($spastatus) {
             // the CPO has a folder id now, so we can set the status
             $sc->AddParameter($spa, "status", $spastatus);
         } else {
             try {
                 $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey()));
                 // if this is an additional folder the backend has to be setup correctly
                 if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) {
                     throw new StatusException(sprintf("HandleGetItemEstimate() could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
                 }
             } catch (StateNotFoundException $snfex) {
                 // ok, the key is invalid. Question is, if the hierarchycache is still ok
                 //if not, we have to issue SYNC_GETITEMESTSTATUS_COLLECTIONINVALID which triggers a FolderSync
                 try {
                     self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId());
                     // we got here, so the HierarchyCache is ok
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCKKEYINVALID);
                 } catch (NoHierarchyCacheAvailableException $nhca) {
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
                 }
                 self::$topCollector->AnnounceInformation("StateNotFoundException " . $sc->GetParameter($spa, "status"), true);
             } catch (StatusException $stex) {
                 if ($stex->getCode() == SYNC_GETITEMESTSTATUS_COLLECTIONINVALID) {
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID);
                 } else {
                     $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED);
                 }
                 self::$topCollector->AnnounceInformation("StatusException " . $sc->GetParameter($spa, "status"), true);
             }
         }
     }
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     //SYNC_GETITEMESTIMATE_FOLDERS
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     //SYNC_GETITEMESTIMATE_GETITEMESTIMATE
     self::$encoder->startWBXML();
     self::$encoder->startTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE);
     $status = SYNC_GETITEMESTSTATUS_SUCCESS;
     // look for changes in all collections
     try {
         $sc->CountChanges();
     } catch (StatusException $ste) {
         $status = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID;
     }
     $changes = $sc->GetChangedFolderIds();
     foreach ($sc as $folderid => $spa) {
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_RESPONSE);
         if ($sc->GetParameter($spa, "status")) {
             $status = $sc->GetParameter($spa, "status");
         }
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDER);
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERTYPE);
         self::$encoder->content($spa->GetContentClass());
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERID);
         self::$encoder->content($spa->GetFolderId());
         self::$encoder->endTag();
         if (isset($changes[$folderid]) && $changes[$folderid] !== false) {
             self::$encoder->startTag(SYNC_GETITEMESTIMATE_ESTIMATE);
             self::$encoder->content($changes[$folderid]);
             self::$encoder->endTag();
             if ($changes[$folderid] > 0) {
                 self::$topCollector->AnnounceInformation(sprintf("%s %d changes", $spa->GetContentClass(), $changes[$folderid]), true);
             }
         }
         self::$encoder->endTag();
         self::$encoder->endTag();
     }
     if (array_sum($changes) == 0) {
         self::$topCollector->AnnounceInformation("No changes found", true);
     }
     self::$encoder->endTag();
     return true;
 }
 /**
  * Execute the migration
  *
  * @access public
  * @return true
  */
 public function DoMigration()
 {
     // go through all files
     $files = glob(STATE_DIR . "/*/*/*", GLOB_NOSORT);
     $filetotal = count($files);
     $filecount = 0;
     $rencount = 0;
     $igncount = 0;
     foreach ($files as $file) {
         $filecount++;
         $newfile = strtolower($file);
         echo "";
         if ($file !== $newfile) {
             $rencount++;
             rename($file, $newfile);
         } else {
             $igncount++;
         }
         printf("Migrating file %d/%d\t%s", $filecount, $filetotal, $file);
     }
     echo "" . sprintf("Migrated total of %d files, %d renamed and %d ignored (as already correct)%s\n\n", $filetotal, $rencount, $igncount, str_repeat(" ", 50));
     // get all states of synchronized devices
     $alldevices = $this->sm->GetAllDevices(false);
     foreach ($alldevices as $devid) {
         $lowerDevid = strtolower($devid);
         echo "Processing device: " . $devid . "\t";
         // update device data
         $devState = ZPush::GetStateMachine()->GetState($lowerDevid, IStateMachine::DEVICEDATA);
         $newdata = array();
         foreach ($devState->devices as $user => $dev) {
             if (!isset($dev->deviceidOrg)) {
                 $dev->deviceidOrg = $dev->deviceid;
             }
             $dev->deviceid = strtolower($dev->deviceid);
             $dev->useragenthistory = array_unique($dev->useragenthistory);
             $newdata[$user] = $dev;
         }
         $devState->devices = $newdata;
         $this->sm->SetState($devState, $lowerDevid, IStateMachine::DEVICEDATA);
         // go through the users again: device was updated sucessfully, now we change the global user <-> device link
         foreach ($devState->devices as $user => $dev) {
             printf("\n\tUn-linking %s with old device id %s", $user, $dev->deviceidOrg);
             $this->sm->UnLinkUserDevice($user, $dev->deviceidOrg);
             printf("\n\tRe-linking %s with new device id %s", $user, $dev->deviceid);
             $this->sm->LinkUserDevice($user, $dev->deviceid);
         }
         echo "\n\tcompleted\n";
     }
     echo "\nSetting new StateVersion\n";
     $this->sm->SetStateVersion(self::TOVERSION);
     echo "Migration completed!\n\n";
     return true;
 }
 /**
  * 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;
 }
Example #27
0
 /**
  * Handles the MoveItems command
  *
  * @param int       $commandCode
  *
  * @access public
  * @return boolean
  */
 public function Handle($commandCode)
 {
     if (!self::$decoder->getElementStartTag(SYNC_MOVE_MOVES)) {
         return false;
     }
     $moves = array();
     while (self::$decoder->getElementStartTag(SYNC_MOVE_MOVE)) {
         $move = array();
         if (self::$decoder->getElementStartTag(SYNC_MOVE_SRCMSGID)) {
             $move["srcmsgid"] = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 break;
             }
         }
         if (self::$decoder->getElementStartTag(SYNC_MOVE_SRCFLDID)) {
             $move["srcfldid"] = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 break;
             }
         }
         if (self::$decoder->getElementStartTag(SYNC_MOVE_DSTFLDID)) {
             $move["dstfldid"] = self::$decoder->getElementContent();
             if (!self::$decoder->getElementEndTag()) {
                 break;
             }
         }
         array_push($moves, $move);
         if (!self::$decoder->getElementEndTag()) {
             return false;
         }
     }
     if (!self::$decoder->getElementEndTag()) {
         return false;
     }
     self::$encoder->StartWBXML();
     self::$encoder->startTag(SYNC_MOVE_MOVES);
     foreach ($moves as $move) {
         self::$encoder->startTag(SYNC_MOVE_RESPONSE);
         self::$encoder->startTag(SYNC_MOVE_SRCMSGID);
         self::$encoder->content($move["srcmsgid"]);
         self::$encoder->endTag();
         $status = SYNC_MOVEITEMSSTATUS_SUCCESS;
         $result = false;
         try {
             // if the source folder is an additional folder the backend has to be setup correctly
             if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($move["srcfldid"]))) {
                 throw new StatusException(sprintf("HandleMoveItems() could not Setup() the backend for folder id '%s'", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
             }
             $importer = self::$backend->GetImporter($move["srcfldid"]);
             if ($importer === false) {
                 throw new StatusException(sprintf("HandleMoveItems() could not get an importer for folder id '%s'", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
             }
             $result = $importer->ImportMessageMove($move["srcmsgid"], $move["dstfldid"]);
             // We discard the importer state for now.
         } catch (StatusException $stex) {
             if ($stex->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) {
                 // same as SYNC_FSSTATUS_CODEUNKNOWN
                 $status = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID;
             } else {
                 $status = $stex->getCode();
             }
         }
         self::$topCollector->AnnounceInformation(sprintf("Operation status: %s", $status), true);
         self::$encoder->startTag(SYNC_MOVE_STATUS);
         self::$encoder->content($status);
         self::$encoder->endTag();
         self::$encoder->startTag(SYNC_MOVE_DSTMSGID);
         self::$encoder->content($result !== false ? $result : $move["srcmsgid"]);
         self::$encoder->endTag();
         self::$encoder->endTag();
     }
     self::$encoder->endTag();
     return true;
 }
Example #28
0
 /**
  * Loads the states and writes them into the SyncCollection Object and the actiondata failstate
  *
  * @param SyncCollection    $sc             SyncCollection object
  * @param SyncParameters    $spa            SyncParameters object
  * @param array             $actiondata     Actiondata array
  * @param boolean           $loadFailsave   (opt) default false - indicates if the failsave states should be loaded
  *
  * @access private
  * @return status           indicating if there were errors. If no errors, status is SYNC_STATUS_SUCCESS
  */
 private function loadStates($sc, $spa, &$actiondata, $loadFailsave = false)
 {
     $status = SYNC_STATUS_SUCCESS;
     if ($sc->GetParameter($spa, "state") == null) {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync->loadStates(): loading states for folder '%s'", $spa->GetFolderId()));
         try {
             $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey()));
             if ($loadFailsave) {
                 // if this request was made before, there will be a failstate available
                 $actiondata["failstate"] = self::$deviceManager->GetStateManager()->GetSyncFailState();
             }
             // if this is an additional folder the backend has to be setup correctly
             if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) {
                 throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED);
             }
         } catch (StateNotFoundException $snfex) {
             $status = SYNC_STATUS_INVALIDSYNCKEY;
             self::$topCollector->AnnounceInformation("StateNotFoundException", true);
         } catch (StatusException $stex) {
             $status = $stex->getCode();
             self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true);
         }
     }
     return $status;
 }
Example #29
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) {
         }
     }
 }
Example #30
0
 /**
  * Imports a move of a message. This occurs when a user moves an item to another folder
  *
  * Normally, we would implement this via the 'offical' importmessagemove() function on the ICS importer,
  * but the Zarafa importer does not support this. Therefore we currently implement it via a standard mapi
  * call. This causes a mirror 'add/delete' to be sent to the PDA at the next sync.
  * Manfred, 2010-10-21. For some mobiles import was causing duplicate messages in the destination folder
  * (Mantis #202). Therefore we will create a new message in the destination folder, copy properties
  * of the source message to the new one and then delete the source message.
  *
  * @param string        $id
  * @param string        $newfolder      destination folder
  *
  * @access public
  * @return boolean
  * @throws StatusException
  */
 public function ImportMessageMove($id, $newfolder)
 {
     if (strtolower($newfolder) == strtolower(bin2hex($this->folderid))) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST);
     }
     // Get the entryid of the message we're moving
     $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($id));
     if (!$entryid) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source message id", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
     }
     //open the source message
     $srcmessage = mapi_msgstore_openentry($this->store, $entryid);
     if (!$srcmessage) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source message: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
     }
     // get correct mapi store for the destination folder
     $dststore = ZPush::GetBackend()->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder);
     if ($dststore === false) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open store of destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
     }
     $dstentryid = mapi_msgstore_entryidfromsourcekey($dststore, hex2bin($newfolder));
     if (!$dstentryid) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
     }
     $dstfolder = mapi_msgstore_openentry($dststore, $dstentryid);
     if (!$dstfolder) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
     }
     $newmessage = mapi_folder_createmessage($dstfolder);
     if (!$newmessage) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to create message in destination folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
     }
     // Copy message
     mapi_copyto($srcmessage, array(), array(), $newmessage);
     if (mapi_last_hresult()) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, copy to destination message failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE);
     }
     $srcfolderentryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid);
     if (!$srcfolderentryid) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
     }
     $srcfolder = mapi_msgstore_openentry($this->store, $srcfolderentryid);
     if (!$srcfolder) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
     }
     // Save changes
     mapi_savechanges($newmessage);
     if (mapi_last_hresult()) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, mapi_savechanges() failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE);
     }
     // Delete the old message
     if (!mapi_folder_deletemessages($srcfolder, array($entryid))) {
         throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, delete of source message failed: 0x%X. Possible duplicates.", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_SOURCEORDESTLOCKED);
     }
     $sourcekeyprops = mapi_getprops($newmessage, array(PR_SOURCE_KEY));
     if (isset($sourcekeyprops[PR_SOURCE_KEY]) && $sourcekeyprops[PR_SOURCE_KEY]) {
         return bin2hex($sourcekeyprops[PR_SOURCE_KEY]);
     }
     return false;
 }