Back to index

d-push  2.0
importer.php
Go to the documentation of this file.
00001 <?php
00002 /***********************************************
00003 * File      :   importer.php
00004 * Project   :   Z-Push
00005 * Descr     :   This is a generic class that is
00006 *               used by both the proxy importer
00007 *               (for outgoing messages) and our
00008 *               local importer (for incoming
00009 *               messages). Basically all shared
00010 *               conversion data for converting
00011 *               to and from MAPI objects is in here.
00012 *
00013 * Created   :   14.02.2011
00014 *
00015 * Copyright 2007 - 2011 Zarafa Deutschland GmbH
00016 *
00017 * This program is free software: you can redistribute it and/or modify
00018 * it under the terms of the GNU Affero General Public License, version 3,
00019 * as published by the Free Software Foundation with the following additional
00020 * term according to sec. 7:
00021 *
00022 * According to sec. 7 of the GNU Affero General Public License, version 3,
00023 * the terms of the AGPL are supplemented with the following terms:
00024 *
00025 * "Zarafa" is a registered trademark of Zarafa B.V.
00026 * "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
00027 * The licensing of the Program under the AGPL does not imply a trademark license.
00028 * Therefore any rights, title and interest in our trademarks remain entirely with us.
00029 *
00030 * However, if you propagate an unmodified version of the Program you are
00031 * allowed to use the term "Z-Push" to indicate that you distribute the Program.
00032 * Furthermore you may use our trademarks where it is necessary to indicate
00033 * the intended purpose of a product or service provided you use it in accordance
00034 * with honest practices in industrial or commercial matters.
00035 * If you want to propagate modified versions of the Program under the name "Z-Push",
00036 * you may only do so if you have a written permission by Zarafa Deutschland GmbH
00037 * (to acquire a permission please contact Zarafa at trademark@zarafa.com).
00038 *
00039 * This program is distributed in the hope that it will be useful,
00040 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00041 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00042 * GNU Affero General Public License for more details.
00043 *
00044 * You should have received a copy of the GNU Affero General Public License
00045 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00046 *
00047 * Consult LICENSE file for details
00048 ************************************************/
00049 
00050 
00051 
00060 class ImportChangesICS implements IImportChanges {
00061     private $folderid;
00062     private $store;
00063     private $session;
00064     private $flags;
00065     private $statestream;
00066     private $importer;
00067     private $memChanges;
00068     private $mapiprovider;
00069     private $conflictsLoaded;
00070     private $conflictsContentParameters;
00071     private $conflictsState;
00072 
00083     public function ImportChangesICS($session, $store, $folderid = false) {
00084         $this->session = $session;
00085         $this->store = $store;
00086         $this->folderid = $folderid;
00087         $this->conflictsLoaded = false;
00088 
00089         if ($folderid) {
00090             $entryid = mapi_msgstore_entryidfromsourcekey($store, $folderid);
00091         }
00092         else {
00093             $storeprops = mapi_getprops($store, array(PR_IPM_SUBTREE_ENTRYID));
00094             $entryid = $storeprops[PR_IPM_SUBTREE_ENTRYID];
00095         }
00096 
00097         $folder = false;
00098         if ($entryid)
00099             $folder = mapi_msgstore_openentry($store, $entryid);
00100 
00101         if(!$folder) {
00102             $this->importer = false;
00103 
00104             // We throw an general error SYNC_FSSTATUS_CODEUNKNOWN (12) which is also SYNC_STATUS_FOLDERHIERARCHYCHANGED (12)
00105             // if this happened while doing content sync, the mobile will try to resync the folderhierarchy
00106             throw new StatusException(sprintf("ImportChangesICS('%s','%s','%s'): Error, unable to open folder: 0x%X", $session, $store, Utils::PrintAsString($folderid), mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN);
00107         }
00108 
00109         $this->mapiprovider = new MAPIProvider($this->session, $this->store);
00110 
00111         if ($folderid)
00112             $this->importer = mapi_openproperty($folder, PR_COLLECTOR, IID_IExchangeImportContentsChanges, 0 , 0);
00113         else
00114             $this->importer = mapi_openproperty($folder, PR_COLLECTOR, IID_IExchangeImportHierarchyChanges, 0 , 0);
00115     }
00116 
00127     public function Config($state, $flags = 0) {
00128         $this->flags = $flags;
00129 
00130         // this should never happen
00131         if ($this->importer === false)
00132             throw new StatusException("ImportChangesICS->Config(): Error, importer not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_ERROR);
00133 
00134         // Put the state information in a stream that can be used by ICS
00135         $stream = mapi_stream_create();
00136         if(strlen($state) == 0)
00137             $state = hex2bin("0000000000000000");
00138 
00139         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->Config(): initializing importer with state: 0x%s", bin2hex($state)));
00140 
00141         mapi_stream_write($stream, $state);
00142         $this->statestream = $stream;
00143 
00144         if ($this->folderid !== false) {
00145             // possible conflicting messages will be cached here
00146             $this->memChanges = new ChangesMemoryWrapper();
00147             $stat = mapi_importcontentschanges_config($this->importer, $stream, $flags);
00148         }
00149         else
00150             $stat = mapi_importhierarchychanges_config($this->importer, $stream, $flags);
00151 
00152         if (!$stat)
00153             throw new StatusException(sprintf("ImportChangesICS->Config(): Error, mapi_import_*_changes_config() failed: 0x%X", mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN);
00154         return $stat;
00155     }
00156 
00164     public function GetState() {
00165         $error = false;
00166         if(!isset($this->statestream) || $this->importer === false)
00167             $error = true;
00168 
00169         if ($error === false && $this->folderid !== false && function_exists("mapi_importcontentschanges_updatestate"))
00170             if(mapi_importcontentschanges_updatestate($this->importer, $this->statestream) != true)
00171                 $error = true;
00172 
00173         if ($error == true)
00174             throw new StatusException(sprintf("ImportChangesICS->GetState(): Error, state not available or unable to update: 0x%X", mapi_last_hresult()), (($this->folderid)?SYNC_STATUS_FOLDERHIERARCHYCHANGED:SYNC_FSSTATUS_CODEUNKNOWN), null, LOGLEVEL_WARN);
00175 
00176         mapi_stream_seek($this->statestream, 0, STREAM_SEEK_SET);
00177 
00178         $state = "";
00179         while(true) {
00180             $data = mapi_stream_read($this->statestream, 4096);
00181             if(strlen($data))
00182                 $state .= $data;
00183             else
00184                 break;
00185         }
00186 
00187         return $state;
00188     }
00189 
00205     public function LoadConflicts($contentparameters, $state) {
00206         if (!isset($this->session) || !isset($this->store) || !isset($this->folderid))
00207             throw new StatusException("ImportChangesICS->LoadConflicts(): Error, can not load changes for conflict detection. Session, store or folder information not available", SYNC_STATUS_SERVERERROR);
00208 
00209         // save data to load changes later if necessary
00210         $this->conflictsLoaded = false;
00211         $this->conflictsContentParameters = $contentparameters;
00212         $this->conflictsState = $state;
00213 
00214         ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesICS->LoadConflicts(): will be loaded later if necessary");
00215         return true;
00216     }
00217 
00225     private function lazyLoadConflicts() {
00226         if (!isset($this->session) || !isset($this->store) || !isset($this->folderid) ||
00227             !isset($this->conflictsContentParameters) || $this->conflictsState === false) {
00228             ZLog::Write(LOGLEVEL_WARN, "ImportChangesICS->lazyLoadConflicts(): can not load potential conflicting changes in lazymode for conflict detection. Missing information");
00229             return false;
00230         }
00231 
00232         if (!$this->conflictsLoaded) {
00233             ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesICS->lazyLoadConflicts(): loading..");
00234 
00235             // configure an exporter so we can detect conflicts
00236             $exporter = new ExportChangesICS($this->session, $this->store, $this->folderid);
00237             $exporter->Config($this->conflictsState);
00238             $exporter->ConfigContentParameters($this->conflictsContentParameters);
00239             $exporter->InitializeExporter($this->memChanges);
00240             while(is_array($exporter->Synchronize()));
00241             $this->conflictsLoaded = true;
00242         }
00243     }
00244 
00255     public function ImportMessageChange($id, $message) {
00256         $parentsourcekey = $this->folderid;
00257         if($id)
00258             $sourcekey = hex2bin($id);
00259 
00260         $flags = 0;
00261         $props = array();
00262         $props[PR_PARENT_SOURCE_KEY] = $parentsourcekey;
00263 
00264         // set the PR_SOURCE_KEY if available or mark it as new message
00265         if($id) {
00266             $props[PR_SOURCE_KEY] = $sourcekey;
00267 
00268             // check for conflicts
00269             $this->lazyLoadConflicts();
00270             if($this->memChanges->IsChanged($id)) {
00271                 if ($this->flags & SYNC_CONFLICT_OVERWRITE_PIM) {
00272                     // in these cases the status SYNC_STATUS_CONFLICTCLIENTSERVEROBJECT should be returned, so the mobile client can inform the end user
00273                     throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Conflict detected. Data from PIM will be dropped! Server overwrites PIM. User is informed.", $id, get_class($message)), SYNC_STATUS_CONFLICTCLIENTSERVEROBJECT, null, LOGLEVEL_INFO);
00274                     return false;
00275                 }
00276                 else
00277                     ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Conflict detected. Data from Server will be dropped! PIM overwrites server.", $id, get_class($message)));
00278             }
00279             if($this->memChanges->IsDeleted($id)) {
00280                 ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Conflict detected. Data from PIM will be dropped! Object was deleted on server.", $id, get_class($message)));
00281                 return false;
00282             }
00283         }
00284         else
00285             $flags = SYNC_NEW_MESSAGE;
00286 
00287         if(mapi_importcontentschanges_importmessagechange($this->importer, $props, $flags, $mapimessage)) {
00288             $this->mapiprovider->SetMessage($mapimessage, $message);
00289             mapi_message_savechanges($mapimessage);
00290 
00291             if (mapi_last_hresult())
00292                 throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Error, mapi_message_savechanges() failed: 0x%X", $id, get_class($message), mapi_last_hresult()), SYNC_STATUS_SYNCCANNOTBECOMPLETED);
00293 
00294             $sourcekeyprops = mapi_getprops($mapimessage, array (PR_SOURCE_KEY));
00295             return bin2hex($sourcekeyprops[PR_SOURCE_KEY]);
00296         }
00297         else
00298             throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Error updating object: 0x%X", $id, get_class($message), mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
00299     }
00300 
00310     public function ImportMessageDeletion($id) {
00311         // check for conflicts
00312         $this->lazyLoadConflicts();
00313         if($this->memChanges->IsChanged($id)) {
00314             ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Conflict detected. Data from Server will be dropped! PIM deleted object.", $id));
00315         }
00316         elseif($this->memChanges->IsDeleted($id)) {
00317             ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Conflict detected. Data is already deleted. Request will be ignored.", $id));
00318             return true;
00319         }
00320 
00321         // do a 'soft' delete so people can un-delete if necessary
00322         if(mapi_importcontentschanges_importmessagedeletion($this->importer, 1, array(hex2bin($id))))
00323             throw new StatusException(sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Error updating object: 0x%X", $id, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
00324 
00325         return true;
00326     }
00327 
00339     public function ImportMessageReadFlag($id, $flags) {
00340         // check for conflicts
00341         /*
00342          * Checking for conflicts is correct at this point, but is a very expensive operation.
00343          * If the message was deleted, only an error will be shown.
00344          *
00345         $this->lazyLoadConflicts();
00346         if($this->memChanges->IsDeleted($id)) {
00347             ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageReadFlag('%s'): Conflict detected. Data is already deleted. Request will be ignored.", $id));
00348             return true;
00349         }
00350          */
00351 
00352         $readstate = array ( "sourcekey" => hex2bin($id), "flags" => $flags);
00353 
00354         if(!mapi_importcontentschanges_importperuserreadstatechange($this->importer, array($readstate) ))
00355             throw new StatusException(sprintf("ImportChangesICS->ImportMessageReadFlag('%s','%d'): Error setting read state: 0x%X", $id, $flags, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND);
00356 
00357         return true;
00358     }
00359 
00377     public function ImportMessageMove($id, $newfolder) {
00378         if (strtolower($newfolder) == strtolower(bin2hex($this->folderid)) )
00379             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST);
00380 
00381         // Get the entryid of the message we're moving
00382         $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($id));
00383         if(!$entryid)
00384             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source message id", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
00385 
00386         //open the source message
00387         $srcmessage = mapi_msgstore_openentry($this->store, $entryid);
00388         if (!$srcmessage)
00389             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source message: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
00390 
00391         // get correct mapi store for the destination folder
00392         $dststore = ZPush::GetBackend()->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder);
00393         if ($dststore === false)
00394             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open store of destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
00395 
00396         $dstentryid = mapi_msgstore_entryidfromsourcekey($dststore, hex2bin($newfolder));
00397         if(!$dstentryid)
00398             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
00399 
00400         $dstfolder = mapi_msgstore_openentry($dststore, $dstentryid);
00401         if(!$dstfolder)
00402             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID);
00403 
00404         $newmessage = mapi_folder_createmessage($dstfolder);
00405         if (!$newmessage)
00406             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);
00407 
00408         // Copy message
00409         mapi_copyto($srcmessage, array(), array(), $newmessage);
00410         if (mapi_last_hresult())
00411             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, copy to destination message failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE);
00412 
00413         $srcfolderentryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid);
00414         if(!$srcfolderentryid)
00415             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
00416 
00417         $srcfolder = mapi_msgstore_openentry($this->store, $srcfolderentryid);
00418         if (!$srcfolder)
00419             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID);
00420 
00421         // Save changes
00422         mapi_savechanges($newmessage);
00423         if (mapi_last_hresult())
00424             throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, mapi_savechanges() failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE);
00425 
00426         // Delete the old message
00427         if (!mapi_folder_deletemessages($srcfolder, array($entryid)))
00428             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);
00429 
00430         $sourcekeyprops = mapi_getprops($newmessage, array (PR_SOURCE_KEY));
00431         if (isset($sourcekeyprops[PR_SOURCE_KEY]) && $sourcekeyprops[PR_SOURCE_KEY])
00432             return  bin2hex($sourcekeyprops[PR_SOURCE_KEY]);
00433 
00434         return false;
00435     }
00436 
00437 
00451     public function ImportFolderChange($folder) {
00452         $id = isset($folder->serverid)?$folder->serverid:false;
00453         $parent = $folder->parentid;
00454         $displayname = u2wi($folder->displayname);
00455         $type = $folder->type;
00456 
00457         if (Utils::IsSystemFolder($type))
00458             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, system folder can not be created/modified", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname), SYNC_FSSTATUS_SYSTEMFOLDER);
00459 
00460         // create a new folder if $id is not set
00461         if (!$id) {
00462             // the root folder is "0" - get IPM_SUBTREE
00463             if ($parent == "0") {
00464                 $parentprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
00465                 if (isset($parentprops[PR_IPM_SUBTREE_ENTRYID]))
00466                     $parentfentryid = $parentprops[PR_IPM_SUBTREE_ENTRYID];
00467             }
00468             else
00469                 $parentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($parent));
00470 
00471             if (!$parentfentryid)
00472                 throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent folder (no entry id)", Utils::PrintAsString(false), $folder->parentid, $displayname), SYNC_FSSTATUS_PARENTNOTFOUND);
00473 
00474             $parentfolder = mapi_msgstore_openentry($this->store, $parentfentryid);
00475             if (!$parentfolder)
00476                 throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent folder (open entry)", Utils::PrintAsString(false), $folder->parentid, $displayname), SYNC_FSSTATUS_PARENTNOTFOUND);
00477 
00478             //  mapi_folder_createfolder() fails if a folder with this name already exists -> MAPI_E_COLLISION
00479             $newfolder = mapi_folder_createfolder($parentfolder, $displayname, "");
00480             if (mapi_last_hresult())
00481                 throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, mapi_folder_createfolder() failed: 0x%X", Utils::PrintAsString(false), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_FOLDEREXISTS);
00482 
00483             mapi_setprops($newfolder, array(PR_CONTAINER_CLASS => MAPIUtils::GetContainerClassFromFolderType($type)));
00484 
00485             $props =  mapi_getprops($newfolder, array(PR_SOURCE_KEY));
00486             if (isset($props[PR_SOURCE_KEY])) {
00487                 $sourcekey = bin2hex($props[PR_SOURCE_KEY]);
00488                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("Created folder '%s' with id: '%s'", $displayname, $sourcekey));
00489                 return $sourcekey;
00490             }
00491             else
00492                 throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, folder created but PR_SOURCE_KEY not available: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
00493             return false;
00494         }
00495 
00496         // update folder
00497         $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($id));
00498         if (!$entryid)
00499             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
00500 
00501         $folder = mapi_msgstore_openentry($this->store, $entryid);
00502         if (!$folder)
00503             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND);
00504 
00505         $props =  mapi_getprops($folder, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_DISPLAY_NAME, PR_CONTAINER_CLASS));
00506         if (!isset($props[PR_SOURCE_KEY]) || !isset($props[PR_PARENT_SOURCE_KEY]) || !isset($props[PR_DISPLAY_NAME]) || !isset($props[PR_CONTAINER_CLASS]))
00507             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, folder data not available: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
00508 
00509         if ($parent == "0") {
00510             $parentprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
00511             $parentfentryid = $parentprops[PR_IPM_SUBTREE_ENTRYID];
00512             $mapifolder = mapi_msgstore_openentry($this->store, $parentfentryid);
00513 
00514             $rootfolderprops = mapi_getprops($mapifolder, array(PR_SOURCE_KEY));
00515             $parent = bin2hex($rootfolderprops[PR_SOURCE_KEY]);
00516             ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->ImportFolderChange(): resolved AS parent '0' to sourcekey '%s'", $parent));
00517         }
00518 
00519         // In theory the parent id could change, which means that the folder was moved.
00520         // It is unknown if any device supports this, so we do currently not implement it (no known device is able to do this)
00521         if (bin2hex($props[PR_PARENT_SOURCE_KEY]) !== $parent)
00522             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Folder was moved to another location, which is currently not supported. Please report this to the Z-Push dev team together with the WBXML log and your device details (model, firmware etc).", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_UNKNOWNERROR);
00523 
00524         $props = array(PR_DISPLAY_NAME => $displayname);
00525         mapi_setprops($folder, $props);
00526         mapi_savechanges($folder);
00527         if (mapi_last_hresult())
00528             throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, mapi_savechanges() failed: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
00529 
00530         ZLog::Write(LOGLEVEL_DEBUG, "Imported changes for folder: $id");
00531         return $id;
00532     }
00533 
00544     public function ImportFolderDeletion($id, $parent = false) {
00545         ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): importing folder deletetion", $id, $parent));
00546 
00547         $folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($id));
00548         if(!$folderentryid)
00549             throw new StatusException(sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): Error, unable to resolve folder", $id, $parent, mapi_last_hresult()), SYNC_FSSTATUS_FOLDERDOESNOTEXIST);
00550 
00551         // get the folder type from the MAPIProvider
00552         $type = $this->mapiprovider->GetFolderType($folderentryid);
00553 
00554         if (Utils::IsSystemFolder($type))
00555             throw new StatusException(sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): Error deleting system/default folder", $id, $parent), SYNC_FSSTATUS_SYSTEMFOLDER);
00556 
00557         $ret = mapi_importhierarchychanges_importfolderdeletion ($this->importer, 0, array(PR_SOURCE_KEY => hex2bin($id)));
00558         if (!$ret)
00559             throw new StatusException(sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): Error deleting folder: 0x%X", $id, $parent, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR);
00560 
00561         return $ret;
00562     }
00563 }
00564 ?>