Commit 84e997f4 authored by Sebastian Kummer's avatar Sebastian Kummer

ZP-915 Added -t (target gab) to the options list, pass it to the

actions, added new call to list all GABs (companies), pass gabId and
gabName to the existing calls, cache stores for gabs.

Released under the Affero GNU General Public License (AGPL) version 3.
parent e729b422
......@@ -88,6 +88,7 @@ class GabSyncCLI {
static private $syncWorker;
static private $command;
static private $uniqueId = false;
static private $targetGab = false;
static private $errormessage;
/**
......@@ -101,6 +102,7 @@ class GabSyncCLI {
"\tgab-sync.php -a ACTION [options]" .PHP_EOL.PHP_EOL.
"Parameters:" .PHP_EOL.
"\t-a simulate | sync | sync-one | clear-all | delete-all" .PHP_EOL.
"\t[-t] TARGET-GAB\t\t Target GAB to execute the action / unique-id on. Optional, if not set, executed on all or default gab." .PHP_EOL.
"\t[-u] UNIQUE-ID" .PHP_EOL.PHP_EOL.
"Actions:" .PHP_EOL.
"\tsimulate\t\t Simulates the GAB synchronization and prints out statistics and configuration suggestions." .PHP_EOL.
......@@ -120,7 +122,7 @@ class GabSyncCLI {
static public function SetupSyncWorker() {
$file = "lib/" .strtolower(SYNCWORKER).".php";
@include_once($file);
include_once($file);
if (!class_exists(SYNCWORKER)) {
self::$errormessage = "SyncWorker file loaded, but class '".SYNCWORKER."' can not be found. Check your configuration or implementation.";
......@@ -157,7 +159,7 @@ class GabSyncCLI {
if (self::$errormessage)
return;
$options = getopt("u:a:");
$options = getopt("u:a:t:");
// get 'unique-id'
if (isset($options['u']) && !empty($options['u']))
......@@ -165,6 +167,12 @@ class GabSyncCLI {
else if (isset($options['unique-id']) && !empty($options['unique-id']))
self::$uniqueId = strtolower(trim($options['unique-id']));
// get 'target-gab'
if (isset($options['t']) && !empty($options['t']))
self::$targetGab = strtolower(trim($options['t']));
else if (isset($options['target-gab']) && !empty($options['target-gab']))
self::$targetGab = strtolower(trim($options['target-gab']));
// get 'action'
$action = false;
if (isset($options['a']) && !empty($options['a']))
......@@ -240,31 +248,31 @@ class GabSyncCLI {
echo PHP_EOL;
switch(self::$command) {
case self::COMMAND_SIMULATE:
self::$syncWorker->Simulate();
self::$syncWorker->Simulate(self::$targetGab);
break;
case self::COMMAND_SYNC:
self::$syncWorker->Sync();
self::$syncWorker->Sync(self::$targetGab);
break;
case self::COMMAND_SYNC_ONE:
self::$syncWorker->SyncOne(self::$uniqueId);
self::$syncWorker->SyncOne(self::$uniqueId, self::$targetGab);
break;
case self::COMMAND_CLEARALL:
echo "Are you sure you want to remove all chunks and data from the public folder. ALL GAB data will be removed from ALL KOE instances [y/N]: ";
echo "Are you sure you want to remove all chunks and data from the hidden GAB folder. ALL GAB data will be removed from ALL KOE instances [y/N]: ";
$confirm = strtolower(trim(fgets(STDIN)));
if ( $confirm === 'y' || $confirm === 'yes')
self::$syncWorker->ClearAll();
self::$syncWorker->ClearAll(self::$targetGab);
else
echo "Aborted!".PHP_EOL;
break;
case self::COMMAND_DELETEALL:
echo "Are you sure you want to remove all chunks and data from the public folder and delete it? ALL GAB data will be removed from ALL KOE instances [y/N]: ";
echo "Are you sure you want to remove all chunks and data from the hidden GAB folder and delete it? ALL GAB data will be removed from ALL KOE instances [y/N]: ";
$confirm = strtolower(trim(fgets(STDIN)));
if ( $confirm === 'y' || $confirm === 'yes')
self::$syncWorker->DeleteAll();
self::$syncWorker->DeleteAll(self::$targetGab);
else
echo "Aborted!".PHP_EOL;
break;
......
......@@ -53,11 +53,12 @@ define('PR_EMS_AB_THUMBNAIL_PHOTO', mapi_prop_tag(PT_BINARY, 0x8C9E));
class Kopano extends SyncWorker {
const NAME = "Z-Push Kopano GAB Sync";
const VERSION = "1.0";
const VERSION = "1.1";
private $session;
private $store;
private $mainUser;
private $folderCache;
private $storeCache;
private $mapiprops;
/**
......@@ -70,8 +71,10 @@ class Kopano extends SyncWorker {
$this->Terminate(sprintf("Kopano: login failed with error code: 0x%08X", mapi_last_hresult()));
}
$this->mainUser = USERNAME;
$this->targetStore = HIDDEN_FOLDERSTORE;
$this->store = $this->openMessageStore(HIDDEN_FOLDERSTORE);
$this->folderCache = array();
$this->storeCache = array();
$this->mapiprops = array(
"chunktype" => "PT_STRING8:PSETID_Appointment:0x6822", // custom property
......@@ -92,11 +95,15 @@ class Kopano extends SyncWorker {
/**
* Creates the hidden folder.
*
* @param string $gabId the id of the gab where the hidden folder should be created. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be created. If not set (null) the default gab is used.
*
* @access protected
* @return string
*/
protected function CreateHiddenFolder() {
$parentfolder = $this->getRootFolder();
protected function CreateHiddenFolder($gabId = null, $gabName = 'default') {
$store = $this->getStore($gabId, $gabName);
$parentfolder = $this->getRootFolder($store);
// mapi_folder_createfolder() fails if a folder with this name already exists -> MAPI_E_COLLISION
$newfolder = mapi_folder_createfolder($parentfolder, HIDDEN_FOLDERNAME, "");
......@@ -120,14 +127,17 @@ class Kopano extends SyncWorker {
* Deletes the hidden folder.
*
* @param string $folderid
* @param string $gabId the id of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
*
* @access protected
* @return boolean
*/
protected function DeleteHiddenFolder($folderid) {
$parentfolder = $this->getRootFolder();
protected function DeleteHiddenFolder($folderid, $gabId = null, $gabName = 'default') {
$store = $this->getStore($gabId, $gabName);
$parentfolder = $this->getRootFolder($store);
$folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
$folderentryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($folderid));
if (mapi_last_hresult())
$this->Terminate(sprintf("Kopano->DeleteHiddenFolder(): Error, could not get PR_ENTRYID for hidden folder: 0x%08X", mapi_last_hresult()));
......@@ -141,11 +151,15 @@ class Kopano extends SyncWorker {
/**
* Returns the internal identifier (folder-id) of the hidden folder.
*
* @param string $gabId the id of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
*
* @access protected
* @return string|boolean on error
*/
protected function GetHiddenFolderId() {
$parentfolder = $this->getRootFolder();
protected function GetHiddenFolderId($gabId = null, $gabName = 'default') {
$store = $this->getStore($gabId, $gabName);
$parentfolder = $this->getRootFolder($store);
$table = mapi_folder_gethierarchytable($parentfolder);
$restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => PR_DISPLAY_NAME, VALUE => HIDDEN_FOLDERNAME));
......@@ -165,19 +179,22 @@ class Kopano extends SyncWorker {
* Removes all messages that have not the same chunkType (chunk configuration changed!).
*
* @param string $folderid
* @param string $gabId the id of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
*
* @access protected
* @return boolean
*/
protected function ClearFolderContents($folderid) {
$this->Log("Kopano->ClearFolderContents: emptying folder");
$folder = $this->getFolder($folderid);
protected function ClearFolderContents($folderid, $gabId = null, $gabName = 'default') {
$this->Log(sprintf("Kopano->ClearFolderContents: emptying folder in GAB '%s': %s", $gabName, $folderid));
$store = $this->getStore($gabId, $gabName);
$folder = $this->getFolder($store, $folderid);
// empty folder!
$flags = 0;
mapi_folder_emptyfolder($folder, $flags);
if (mapi_last_hresult())
$this->Terminate("Kopano->ClearFolderContents: Error, mapi_folder_emptyfolder() failed: 0x%08X");
$this->Terminate(sprintf("Kopano->ClearFolderContents: Error, mapi_folder_emptyfolder() failed on '%s': 0x%08X", $gabName, mapi_last_hresult()));
return true;
}
......@@ -186,15 +203,18 @@ class Kopano extends SyncWorker {
* Removes all messages that do not match the current ChunkType.
*
* @param string $folderid
* @param string $gabId the id of the gab where the hidden folder should be cleared. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be cleared. If not set (null) the default gab is used.
*
* @access protected
* @return boolean
*/
protected function ClearAllNotCurrentChunkType($folderid) {
$folder = $this->getFolder($folderid);
protected function ClearAllNotCurrentChunkType($folderid, $gabId = null, $gabName = 'default') {
$store = $this->getStore($gabId, $gabName);
$folder = $this->getFolder($store, $folderid);
$table = mapi_folder_getcontentstable($folder);
if (!$table)
$this->Terminate(sprintf("Kopano->ClearAllNotCurrentChunkType: Error, unable to read contents table: 0x%08X", mapi_last_hresult()));
$this->Terminate(sprintf("Kopano->ClearAllNotCurrentChunkType: Error, unable to read contents table on '%s': 0x%08X", $gabName, mapi_last_hresult()));
$restriction = array(RES_PROPERTY, array(RELOP => RELOP_NE, ULPROPTAG => $this->mapiprops['chunktype'], VALUE => $this->chunkType));
mapi_table_restrict($table, $restriction);
......@@ -216,6 +236,21 @@ class Kopano extends SyncWorker {
return true;
}
/**
* Returns a list of Global Address Books with their name and ids.
*
* @access protected
* @return array
*/
protected function GetGABs() {
$names = array();
$companies = mapi_zarafa_getcompanylist($this->store);
foreach($companies as $c) {
$names[$c['companyname']] = bin2hex($c['companyid']);
}
return $names;
}
/**
* Returns a list with all GAB entries or a single entry specified by $uniqueId.
* The search for that single entry is done using the configured UNIQUEID parameter.
......@@ -224,28 +259,38 @@ class Kopano extends SyncWorker {
* @param string $uniqueId A value to be found in the configured UNIQUEID.
* If set, only one item is returned. If false or not set, the entire GAB is returned.
* Default: false
* @param string $gabId Id that uniquely identifies the GAB. If not set or null the default GAB is assumed.
* @param string $gabName String that uniquely identifies the GAB. If not set the default GAB is assumed.
*
* @access protected
* @return array of GABEntry
*/
protected function GetGAB($uniqueId = false) {
// get all the groups
$groups = mapi_zarafa_getgrouplist($this->store);
protected function GetGAB($uniqueId = false, $gabId = null, $gabName = 'default') {
$data = array();
$addrbook = mapi_openaddressbook($this->session);
if (mapi_last_hresult())
$this->Terminate(sprintf("Kopano->GetGAB: Error opening addressbook 0x%08X", mapi_last_hresult()));
$ab_entryid = mapi_ab_getdefaultdir($addrbook);
if (mapi_last_hresult())
$this->Terminate(sprintf("Kopano->GetGAB: Error, could not get default address directory: 0x%08X", mapi_last_hresult()));
if ($gabId == null) {
$ab_entryid = mapi_ab_getdefaultdir($addrbook);
if (mapi_last_hresult())
$this->Terminate(sprintf("Kopano->GetGAB: Error, could not get '%s' address directory: 0x%08X", $gabName, mapi_last_hresult()));
}
else {
$ab_entryid = hex2bin($gabId);
}
$ab_dir = mapi_ab_openentry($addrbook, $ab_entryid);
if (mapi_last_hresult())
$this->Terminate(sprintf("Kopano->GetGAB: Error, could not open default address directory: 0x%08X", mapi_last_hresult()));
$this->Terminate(sprintf("Kopano->GetGAB: Error, could not open '%s' address directory: 0x%08X", $gabName, mapi_last_hresult()));
$table = mapi_folder_getcontentstable($ab_dir);
if (mapi_last_hresult())
$this->Terminate(sprintf("Kopano->GetGAB: error, could not open addressbook content table: 0x%08X", mapi_last_hresult()));
$this->Terminate(sprintf("Kopano->GetGAB: error, could not open '%s' addressbook content table: 0x%08X", $gabName, mapi_last_hresult()));
// get all the groups
$groups = mapi_zarafa_getgrouplist($this->store, $ab_entryid);
// restrict the table if we should only return one
if ($uniqueId) {
......@@ -261,7 +306,6 @@ class Kopano extends SyncWorker {
}
}
$gabentries = mapi_table_queryallrows($table, array(PR_ENTRYID,
PR_ACCOUNT,
PR_DISPLAY_NAME,
......@@ -293,21 +337,29 @@ class Kopano extends SyncWorker {
if (is_array($memberOf)) {
$a->memberOf = array_keys($memberOf);
}
// the company name is 'Everyone'
if ($gabId != null && $entry[PR_DISPLAY_NAME] == $gabName) {
$entry[PR_ACCOUNT] = "Everyone";
$entry[PR_DISPLAY_NAME] = "Everyone";
}
// is this a group?
if (array_key_exists($entry[PR_ACCOUNT], $groups)) {
$a->type = GABEntry::GROUP;
$users = mapi_zarafa_getuserlistofgroup($this->store, $groups[$entry[PR_ACCOUNT]]['groupid']);
if (isset($users[$entry[PR_ACCOUNT]]['emailaddress'])) {
$a->smtpAddress = $users[$entry[PR_ACCOUNT]]['emailaddress'];
}
$groupentry = mapi_ab_openentry($addrbook, $entry[PR_ENTRYID]);
$grouptable = mapi_folder_getcontentstable($groupentry, MAPI_DEFERRED_ERRORS);
$users = mapi_table_queryallrows($grouptable, array(PR_ENTRYID, PR_ACCOUNT, PR_SMTP_ADDRESS));
$a->members = array();
if (is_array($users)) {
$a->members = array_keys($users);
// remove the group from itself
$key = array_search($entry[PR_ACCOUNT], $a->members);
if ($key !== false) {
unset($a->members[$key]);
foreach($users as $user) {
if ($user[PR_ENTRYID] == $entry[PR_ENTRYID]) {
if (isset($user[PR_SMTP_ADDRESS])) {
$a->smtpAddress = $user[PR_SMTP_ADDRESS];
}
// don't add the group recursively
continue;
}
$a->members[] = $user[PR_ACCOUNT];
}
}
}
......@@ -353,16 +405,20 @@ class Kopano extends SyncWorker {
* @param string $folderid
* @param string $chunkName The name of the chunk (used to find the chunk message).
* The name is saved in the 'subject' of the chunk message.
* @param string $gabId Id that uniquely identifies the GAB. If not set or null the default GAB is assumed.
* @param string $gabName String that uniquely identifies the GAB. If not set the default GAB is assumed.
*
*
* @access protected
* @return json string
*/
protected function GetChunkData($folderid, $chunkName) {
protected function GetChunkData($folderid, $chunkName, $gabId = null, $gabName = 'default') {
// find the chunk message in the folder
$chunkdata = $this->findChunk($folderid, $chunkName);
$store = $this->getStore($gabId, $gabName);
$chunkdata = $this->findChunk($store, $folderid, $chunkName);
if ($chunkdata[PR_ENTRYID]) {
$message = mapi_msgstore_openentry($this->store, $chunkdata[PR_ENTRYID]);
$message = mapi_msgstore_openentry($store, $chunkdata[PR_ENTRYID]);
return $this->readPropStream($message, PR_BODY);
}
else {
......@@ -382,20 +438,23 @@ class Kopano extends SyncWorker {
* @param string $chunkData The data containing all the data.
* @param string $chunkCRC A checksum of the chunk data. To be saved in the 'location' of
* the chunk message. Used to identify changed chunks.
* @param string $gabId Id that uniquely identifies the GAB. If not set or null the default GAB is assumed.
* @param string $gabName String that uniquely identifies the GAB. If not set the default GAB is assumed.
*
* @access protected
* @return boolean
*/
protected function SetChunkData($folderid, $chunkName, $amountEntries, $chunkData, $chunkCRC) {
protected function SetChunkData($folderid, $chunkName, $amountEntries, $chunkData, $chunkCRC, $gabId = null, $gabName = 'default') {
$log = sprintf("Kopano->SetChunkData: %s\tEntries: %d\t Size: %d B\tCRC: %s - ", $chunkName, $amountEntries, strlen($chunkData), $chunkCRC);
// find the chunk message in the folder
$chunkdata = $this->findChunk($folderid, $chunkName);
// find the chunk message in the folder
$store = $this->getStore($gabId, $gabName);
$chunkdata = $this->findChunk($store, $folderid, $chunkName);
$message = false;
// message not found, create it
if (empty($chunkdata)) {
$folder = $this->getFolder($folderid);
$folder = $this->getFolder($store, $folderid);
$message = mapi_folder_createmessage($folder);
mapi_setprops($message, array(
PR_MESSAGE_CLASS => "IPM.Appointment",
......@@ -412,7 +471,7 @@ class Kopano extends SyncWorker {
else{
// we need to update the chunk if the CRC does not match!
if ($chunkdata[$this->mapiprops['chunkCRC']] != $chunkCRC) {
$message = mapi_msgstore_openentry($this->store, $chunkdata[PR_ENTRYID]);
$message = mapi_msgstore_openentry($store, $chunkdata[PR_ENTRYID]);
$log .= "opening - ";
}
else {
......@@ -444,15 +503,16 @@ class Kopano extends SyncWorker {
/**
* Finds the chunk and returns a property array.
*
* @param ressoure $store
* @param string $folderid
* @param string $chunkName
*
* @access private
* @return array
*/
private function findChunk($folderid, $chunkName) {
private function findChunk($store, $folderid, $chunkName) {
// search for the chunk message
$folder = $this->getFolder($folderid);
$folder = $this->getFolder($store, $folderid);
$table = mapi_folder_getcontentstable($folder);
if (!$table)
$this->Log(sprintf("Kopano->findChunk: Error, unable to read contents table to find chunk '%d': 0x%08X", $chunkId, mapi_last_hresult()));
......@@ -520,26 +580,53 @@ class Kopano extends SyncWorker {
return $store;
}
/**
* Returns the store for a gab id and name
*
* @param string $gabId
* @param string $gabName
*
* @access private
* @return ressource
*/
private function getStore($gabId, $gabName) {
if (!$gabId) {
return $this->store;
}
if (!isset($this->storeCache[$gabId])) {
$user = (strtoupper($this->targetStore) == 'SYSTEM') ? $gabName : $this->targetStore . "@" . $gabName;
$store_entryid = mapi_msgstore_createentryid($this->store, $user);
$store = mapi_openmsgstore($this->session, $store_entryid);
$this->Log(sprintf("Kopano->getStore(): Found store of user '%s': '%s'", $user, $store));
$this->storeCache[$gabId] = $store;
}
return $this->storeCache[$gabId];
}
/**
* Opens the root folder, either in a user's store or of the public folder.
*
* @param ressource $store
*
* @access private
* @return ressource
*/
private function getRootFolder() {
private function getRootFolder($store) {
$rootId = "root";
if (!isset($this->folderCache[$rootId])) {
$parentfentryid = false;
// the default store root
if ($this->mainUser == HIDDEN_FOLDERSTORE) {
$parentprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
$parentprops = mapi_getprops($store, array(PR_IPM_SUBTREE_ENTRYID));
if (isset($parentprops[PR_IPM_SUBTREE_ENTRYID]))
$parentfentryid = $parentprops[PR_IPM_SUBTREE_ENTRYID];
}
// get the main public folder
else {
$parentprops = mapi_getprops($this->store, array(PR_IPM_PUBLIC_FOLDERS_ENTRYID));
$parentprops = mapi_getprops($store, array(PR_IPM_PUBLIC_FOLDERS_ENTRYID));
if (isset($parentprops[PR_IPM_PUBLIC_FOLDERS_ENTRYID]))
$parentfentryid = $parentprops[PR_IPM_PUBLIC_FOLDERS_ENTRYID];
}
......@@ -547,7 +634,7 @@ class Kopano extends SyncWorker {
if (!$parentfentryid)
$this->Terminate(sprintf("Kopano->getRootFolder(): Error, unable to open parent folder (no entry id): 0x%08X", mapi_last_hresult()));
$parentfolder = mapi_msgstore_openentry($this->store, $parentfentryid);
$parentfolder = mapi_msgstore_openentry($store, $parentfentryid);
if (!$parentfolder)
$this->Terminate(sprintf("Kopano->getRootFolder(): Error, unable to open parent folder (open entry): 0x%08X", mapi_last_hresult()));
......@@ -559,17 +646,19 @@ class Kopano extends SyncWorker {
/**
* Opens a folder.
*
* @param string $folderid
* @param ressource $store
* @param string $folderid
*
* @access private
* @return ressource
*/
private function getFolder($folderid) {
private function getFolder($store, $folderid) {
if (!isset($this->folderCache[$folderid])) {
$folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid));
$folderentryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($folderid));
if (!$folderentryid)
$this->Terminate(sprintf("Kopano->getFolder: Error, unable to open folder (no entry id): 0x%08X", mapi_last_hresult()));
$this->Terminate(sprintf("Kopano->getFolder(): Error, unable to open folder (no entry id): 0x%08X", mapi_last_hresult()));
$this->folderCache[$folderid] = mapi_msgstore_openentry($this->store, $folderentryid);
$this->folderCache[$folderid] = mapi_msgstore_openentry($store, $folderentryid);
}
return $this->folderCache[$folderid];
......
......@@ -58,12 +58,14 @@ abstract class SyncWorker {
/**
* Simulates the synchronization, showing statistics but without touching any data.
*
* @param string $targetGab
*
* @access public
* @return void
*/
public function Simulate() {
public function Simulate($targetGab) {
$this->Log("Simulating the synchronization. NO DATA IS GOING TO BE WRITTEN.".PHP_EOL);
$this->Sync(false);
$this->Sync($targetGab, false);
}
/**
......@@ -76,22 +78,54 @@ abstract class SyncWorker {
* - sends it to SetChunkData() to be written
* - shows some stats
*
* @param string $targetGab the gab name id that should be synchronized, if not set 'default' or all are used.
* @param string $doWrite if set to false, no data will be written (simulated mode). Default: true.
*
* @access public
* @return void
*/
public function Sync($doWrite = true) {
public function Sync($targetGab = false, $doWrite = true) {
// gets a list of GABs
$gabs = $this->GetGABs();
if (empty($gabs)) {
if(!$targetGab) {
$this->Terminate("Multiple GABs not found, target should not be set. Aborting.");
}
// no multi-GABs, just go default
$this->doSync(null, 'default', $doWrite);
}
else {
foreach($gabs as $gabName => $gabId) {
if (!$targetGab || $targetGab == $gabName || $targetGab == $gabId) {
$this->doSync($gabId, $gabName, $doWrite);
}
}
}
}
/**
* Performs the actual synchronization for a single GAB.
*
* @param string $gabId the id of the gab to be synchronized. If not set (null) the default gab is synchronized.
* @param string $gabName the name of the gab to be synchronized. If not set (null) the default gab is synchronized.
* @param string $doWrite if set to false, no data will be written (simulated mode). Default: true.
*
* @access private
* @return void
*/
private function doSync($gabId = null, $gabName = 'default', $doWrite = true) {
// get the folderid of the hidden folder - will be created if not yet available
$folderid = $this->getFolderId($doWrite);
$folderid = $this->getFolderId($gabId, $gabName, $doWrite);
$this->Log(sprintf("Starting %sGAB sync to store '%s' on id '%s'", (!$doWrite?"simulated ":""), HIDDEN_FOLDERSTORE, $folderid));
$this->Log(sprintf("Starting %sGAB sync to store '%s' %s on id '%s'", (!$doWrite?"simulated ":""), HIDDEN_FOLDERSTORE, ($gabId?"of '".$gabName."'":""), $folderid));
// remove all messages that do not match the current $chunkType
if ($doWrite)
$this->ClearAllNotCurrentChunkType($folderid);
$this->ClearAllNotCurrentChunkType($folderid, $gabId, $gabName);
// get all GAB entries
$gab = $this->GetGAB();
$gab = $this->GetGAB(false, $gabId, $gabName);
// build the chunks
$chunks = array();
......@@ -136,7 +170,7 @@ abstract class SyncWorker {
if ($doWrite) {
$chunkName = $this->chunkType . "/". $chunkId;
$chunkCRC = md5($chunkData);
$this->SetChunkData($folderid, $chunkName, $amountEntries, $chunkData, $chunkCRC);
$this->SetChunkData($folderid, $chunkName, $amountEntries, $chunkData, $chunkCRC, $gabId, $gabName);
}
}
......@@ -157,15 +191,37 @@ abstract class SyncWorker {
* Updates a single entry of the GAB in the respective chunk.
*
* @param string $uniqueId
* @param string $targetGab
*
* @access public
* @return void
*/
public function SyncOne($uniqueId) {
$this->Log(sprintf("Sync-one: %s = '%s'", UNIQUEID, $uniqueId));
public function SyncOne($uniqueId, $targetGab) {
$this->Log(sprintf("Sync-one: %s = '%s'%s", UNIQUEID, $uniqueId, ($targetGab) ? " of '".$targetGab."'":''));
// gets a list of GABs
$gabs = $this->GetGABs();
if (empty($gabs)) {
if(!$targetGab) {
$this->Terminate("Multiple GABs not found, target should not be set. Aborting.");
}
// default case, no multi-GABs, just go default
$gabId = null;
$gabName = 'default';
}
else {
foreach($gabs as $testGabName => $testGabId) {
if ($targetGab == $testGabName || $targetGab == $testGabId) {
$gabId = $testGabId;
$gabName = $testGabName;
break;
}
}
}
// search for the entry in the GAB
$entries = $this->GetGAB($uniqueId);
$entries = $this->GetGAB($uniqueId, $gabId, $gabName);
// if an entry is found, update the chunk
// if the entry is NOT found, we should remove it from the chunk (entry deleted)
......@@ -182,10 +238,10 @@ abstract class SyncWorker {
}
// get the data for the chunkId
$folderid = $this->getFolderId();
$folderid = $this->getFolderId($gabId, $gabName);
$chunkId = $this->calculateChunkId($key);
$chunkName = $this->chunkType . "/". $chunkId;
$chunkdata = $this->GetChunkData($folderid, $chunkName);
$chunkdata = $this->GetChunkData($folderid, $chunkName, $gabId, $gabName);
$chunk = json_decode($chunkdata, true);
// update or remove the entry
......@@ -213,50 +269,119 @@ abstract class SyncWorker {
// update the chunk data
$chunkCRC = md5($chunkData);
$status = $this->SetChunkData($folderid, $chunkName, $amountEntries, $chunkData, $chunkCRC);
$status = $this->SetChunkData($folderid, $chunkName, $amountEntries, $chunkData, $chunkCRC, $gabId, $gabName);
if ($status) {
$this->Log("Success!");
}
}
/**
* Clears all data from the hidden folder (without removing it).
* Clears all data from the hidden folder (without removing it) for a target GAB.
* This will cause a serverside clearing of all user gabs.
*
* @param string $targetGab A gab where the data should be cleared. If not set, it's 'default' or all.
*
* @access public
* @return void
*/
public function ClearAll() {
$folderid = $this->GetHiddenFolderId();
public function ClearAll($targetGab) {
// gets a list of GABs
$gabs = $this->GetGABs();
if (empty($gabs)) {
if(!$targetGab) {
$this->Terminate("Multiple GABs not found, target should not be set. Aborting.");
}
// no multi-GABs, just go default
$this->doClearAll(null, 'default');
}
else {
foreach($gabs as $gabName => $gabId) {
if (!$targetGab || $targetGab == $gabName || $targetGab == $gabId) {
$this->doClearAll($gabId, $gabName);
}
}
}
}
/**
* Clears all data from the hidden folder (without removing it) for a specific gabId and gabName
* This will cause a serverside clearing of all user gabs.
*
* @param string $gabId the id of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
*
* @access private
* @return boolean
*/
private function doClearAll($gabId = null, $gabName = 'default') {
$folderid = $this->GetHiddenFolderId($gabId, $gabName);
if (!$folderid) {
$this->Terminate("Could not locate folder. Aborting.");
$this->Log(sprintf("Could not locate folder in '%s'. Aborting.", $gabName));
return false;
}
$status = $this->ClearFolderContents($folderid);
$status = $this->ClearFolderContents($folderid, $gabId, $gabName);
if ($status) {
$this->Log("Success!");
$this->Log(sprintf("Success for '%s'!", $gabName));
}
return !!$status;
}
/**
* Clears all data from the hidden folder and removes it.
* This will cause a serverside clearing of all user gabs and a synchronization stop.
*
* @param string $targetGab A gab where the data should be cleared. If not set, it's 'default' or all.
*
* @access public
* @return void
*/
public function DeleteAll() {
$folderid = $this->GetHiddenFolderId();
public function DeleteAll($targetGab) {
// gets a list of GABs
$gabs = $this->GetGABs();
if (empty($gabs)) {
if(!$targetGab) {
$this->Terminate("Multiple GABs not found, target should not be set. Aborting.");
}
// no multi-GABs, just go default
$this->doDeleteAll(null, 'default');
}
else {
foreach($gabs as $gabName => $gabId) {
if (!$targetGab || $targetGab == $gabName || $targetGab == $gabId) {
$this->doDeleteAll($gabId, $gabName);
}
}
}
}
/**
* Clears all data from the hidden folder and removes it for the gab id and Name.
* This will cause a serverside clearing of all user gabs and a synchronization stop.
*
* @param string $gabId the id of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
*
* @access private
* @return boolean
*/
private function doDeleteAll($gabId = null, $gabName = 'default') {
$folderid = $this->GetHiddenFolderId($gabId, $gabName);
if (!$folderid) {
$this->Terminate("Could not locate folder. Aborting.");
$this->Log(sprintf("Could not locate folder in '%s'", $gabName));
return false;
}
$emptystatus = $this->ClearFolderContents($folderid);
$emptystatus = $this->ClearFolderContents($folderid, $gabId, $gabName);
if ($emptystatus) {
$status = $this->DeleteHiddenFolder($folderid);
$status = $this->DeleteHiddenFolder($folderid, $gabId, $gabName);
if ($status) {
$this->Log("Success!");
$this->Log(sprintf("Success for '%s'!", $gabName));
return true;
}
}
return false;
}
/**
......@@ -289,16 +414,18 @@ abstract class SyncWorker {
* Gets the ID of the hidden folder. If $doCreate is true (or not set) the
* folder will be created if it does not yet exist.
*
* @param string $gabId the id of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
* @param boolean $doCreate Creates the folder if it does not exist, default: true
*
* @access protected
* @return string
*/
protected function getFolderId($doCreate = true) {
$id = $this->GetHiddenFolderId();
protected function getFolderId($gabId = null, $gabName = 'default', $doCreate = true) {
$id = $this->GetHiddenFolderId($gabId, $gabName);
if (!$id) {
if ($doCreate)
$id = $this->CreateHiddenFolder();
$id = $this->CreateHiddenFolder($gabId, $gabName);
else
$id = "<does not yet exist>";
}
......@@ -345,61 +472,84 @@ abstract class SyncWorker {
/**
* Creates the hidden folder.
*
* @param string $gabId the id of the gab where the hidden folder should be created. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be created. If not set (null) the default gab is used.
*
* @access protected
* @return boolean
*/
protected abstract function CreateHiddenFolder();
protected abstract function CreateHiddenFolder($gabId = null, $gabName = 'default');
/**
* Deletes the hidden folder.
*
* @param string $folderid
* @param string $gabId the id of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
*
* @access protected
* @return boolean
*/
protected abstract function DeleteHiddenFolder($folderid);
protected abstract function DeleteHiddenFolder($folderid, $gabId = null, $gabName = 'default');
/**
* Returns the internal identifier (folder-id) of the hidden folder.
*
* @param string $gabId the id of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be searched. If not set (null) the default gab is used.
*
* @access protected
* @return string
*/
protected abstract function GetHiddenFolderId();
protected abstract function GetHiddenFolderId($gabId = null, $gabName = 'default');
/**
* Removes all messages that have not the same chunkType (chunk configuration changed!)
* Removes all messages that have not the same chunkType (chunk configuration changed!).
*
* @param string $folderid
* @param string $gabId the id of the gab where the hidden folder should be cleared. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be cleared. If not set (null) the default gab is used.
*
* @access protected
* @return boolean
*/
protected abstract function ClearFolderContents($folderid);
protected abstract function ClearFolderContents($folderid, $gabId = null, $gabName = 'default');
/**
* Removes all messages that do not match the current ChunkType.
*
* @param string $folderid
* @param string $gabId the id of the gab where the hidden folder should be cleared. If not set (null) the default gab is used.
* @param string $gabName the name of the gab where the hidden folder should be cleared. If not set (null) the default gab is used.
*
* @access protected
* @return boolean
*/
protected abstract function ClearAllNotCurrentChunkType($folderid);
protected abstract function ClearAllNotCurrentChunkType($folderid, $gabId = null, $gabName = 'default');
/**
* Returns a list of Global Address Books with their name and ids.
*
* @access public
* @return array
*/
protected abstract function GetGABs();
/**
* Returns a list with all GAB entries or a single entry specified by $uniqueId.
* The search for that single entry is done using the configured UNIQUEID parameter.
* If no entry is found for a $uniqueId an empty array() must be returned.
*
* @param string $uniqueId A value to be found in the configured UNIQUEID.
* If set, only one item is returned. If false or not set, the entire GAB is returned.
* Default: false
* @param string $gabId Id that uniquely identifies the GAB. If not set or null the default GAB is assumed.
* @param string $gabName String that uniquely identifies the GAB. If not set the default GAB is assumed.
*
* @access protected
* @return array of GABEntry
*/
protected abstract function GetGAB($uniqueId = false);
protected abstract function GetGAB($uniqueId = false, $gabId = null, $gabName = 'default');
/**
* Returns the chunk data of the chunkId of the hidden folder.
......@@ -407,11 +557,14 @@ abstract class SyncWorker {
* @param string $folderid
* @param string $chunkName The name of the chunk (used to find the chunk message).
* The name is saved in the 'subject' of the chunk message.
* @param string $gabId Id that uniquely identifies the GAB. If not set or null the default GAB is assumed.
* @param string $gabName String that uniquely identifies the GAB. If not set the default GAB is assumed.
*
*
* @access protected
* @return json string
*/
protected abstract function GetChunkData($folderid, $chunkName);
protected abstract function GetChunkData($folderid, $chunkName, $gabId = null, $gabName = 'default');
/**
* Updates the chunk data in the hidden folder if it changed.
......@@ -424,9 +577,11 @@ abstract class SyncWorker {
* @param string $chunkData The data containing all the data.
* @param string $chunkCRC A checksum of the chunk data. To be saved in the 'location' of
* the chunk message. Used to identify changed chunks.
* @param string $gabId Id that uniquely identifies the GAB. If not set or null the default GAB is assumed.
* @param string $gabName String that uniquely identifies the GAB. If not set the default GAB is assumed.
*
* @access protected
* @return boolean
*/
protected abstract function SetChunkData($folderid, $chunkName, $amountEntries, $chunkData, $chunkCRC);
protected abstract function SetChunkData($folderid, $chunkName, $amountEntries, $chunkData, $chunkCRC, $gabId = null, $gabName = 'default');
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment