61a2fe9816
some how kopano core throw mapi error while getting hierarchy table for favourites folder So added try catch to handle it Allow to get sub folders of public store
842 lines
28 KiB
PHP
Executable File
842 lines
28 KiB
PHP
Executable File
<?php
|
|
|
|
require_once(BASE_PATH . 'server/includes/core/class.encryptionstore.php');
|
|
require_once('zpushprops.php');
|
|
|
|
/**
|
|
* PluginMDMModule Module
|
|
*/
|
|
class PluginMDMModule extends Module
|
|
{
|
|
private $server = '';
|
|
private $username = '';
|
|
private $password = '';
|
|
|
|
// content data
|
|
const FOLDERUUID = 1;
|
|
const FOLDERTYPE = 2;
|
|
const FOLDERBACKENDID = 5;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param int $id unique id.
|
|
* @param array $data list of all actions.
|
|
*/
|
|
function __construct($id, $data)
|
|
{
|
|
parent::__construct($id, $data);
|
|
|
|
$this->server = (PLUGIN_MDM_SERVER_SSL ? 'https://' : 'http://') . PLUGIN_MDM_SERVER;
|
|
|
|
// Get the username and password from the Encryption store
|
|
$encryptionStore = EncryptionStore::getInstance();
|
|
$this->username = $encryptionStore->get('username');
|
|
$this->password = $encryptionStore->get('password');
|
|
|
|
$this->url = $this->server .'/Microsoft-Server-ActiveSync?Cmd=WebserviceDevice&DeviceId=webservice&DeviceType=webservice&User=' . $this->username;
|
|
}
|
|
|
|
/**
|
|
* Returns the version of the Z-Push server
|
|
* @return String
|
|
*/
|
|
function getServerVersion()
|
|
{
|
|
// Make a call to the service, so we can read the version
|
|
// from the response headers
|
|
try {
|
|
$url = $this->server .'/Microsoft-Server-ActiveSync?Cmd=WebserviceInfo&DeviceId=webservice&DeviceType=webservice&User=' . $this->username;
|
|
$client = $this->getSoapClient($url);
|
|
return $client->About();
|
|
} catch(Exception $e){}
|
|
|
|
// If we can't find a version, we will simply return not available
|
|
return dgettext('plugin_mdm', 'version not available');
|
|
}
|
|
|
|
/**
|
|
* Helper to setup a client.
|
|
*/
|
|
function getSoapClient($url='')
|
|
{
|
|
if ( empty($url) ){
|
|
$url = $this->url;
|
|
}
|
|
|
|
return new SoapClient(null, array(
|
|
'location' => $url,
|
|
'uri' => $this->server,
|
|
'trace' => 1,
|
|
'login' => $this->username,
|
|
'password' => $this->password
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Function which calls the soap call to do a full resync
|
|
* @param int $deviceid of phone which has to be resynced
|
|
* @return json $response object contains the response of the soap request from Z-Push
|
|
*/
|
|
function resyncDevice($deviceid)
|
|
{
|
|
$client = $this->getSoapClient();
|
|
return $client->ResyncDevice($deviceid);
|
|
}
|
|
|
|
/**
|
|
* Function which calls the wipeDevice soap call
|
|
* @param int $deviceid of phone which has to be wiped
|
|
* @param string $password user password
|
|
* @return json $response object contains the response of the soap request from Z-Push
|
|
*/
|
|
function wipeDevice($deviceid, $password)
|
|
{
|
|
if ($password == $this->password) {
|
|
$client = $this->getSoapClient();
|
|
return $client->WipeDevice($deviceid);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* function which calls the ListDeviceDetails soap call
|
|
* @return array $response array contains a list of devices connected to the users account
|
|
*/
|
|
function getDevices()
|
|
{
|
|
$client = $this->getSoapClient();
|
|
return $client->ListDevicesDetails();
|
|
}
|
|
|
|
|
|
/**
|
|
* function which calls the wipeDevice soap call
|
|
* @param int $deviceid of phone which has to be wiped
|
|
* @return json $response object contains the response of the soap request from Z-Push
|
|
*/
|
|
function removeDevice($deviceid)
|
|
{
|
|
$client = $this->getSoapClient();
|
|
return $client->RemoveDevice($deviceid);
|
|
}
|
|
|
|
|
|
/**
|
|
* Function witch is use get details of given device.
|
|
* @param string $deviceid id of device.
|
|
* @return array contains device props.
|
|
*/
|
|
function getDeviceDetails($deviceid)
|
|
{
|
|
$client = $this->getSoapClient();
|
|
$deviceRawData = $client->GetDeviceDetails($deviceid);
|
|
$device = array();
|
|
$device['props'] = $this->getDeviceProps($deviceRawData->data);
|
|
$device["sharedfolders"] = array("item" => $this->getAdditionalFolderList($deviceid));
|
|
return $device;
|
|
}
|
|
|
|
/**
|
|
* Executes all the actions in the $data variable.
|
|
* @return boolean true on success of false on fialure.
|
|
*/
|
|
function execute()
|
|
{
|
|
foreach($this->data as $actionType => $actionData)
|
|
{
|
|
if(isset($actionType)) {
|
|
try {
|
|
switch($actionType)
|
|
{
|
|
case 'wipe':
|
|
$this->wipeDevice($actionData['deviceid'], $actionData['password']);
|
|
$this->addActionData('wipe', array(
|
|
'type' => 3,
|
|
'wipe' => $this->wipeDevice($actionData['deviceid'], $actionData['password'])
|
|
));
|
|
$GLOBALS['bus']->addData($this->getResponseData());
|
|
break;
|
|
case 'resync':
|
|
$this->addActionData('resync', array(
|
|
'type' => 3,
|
|
'resync' => $this->resyncDevice($actionData['deviceid'])
|
|
));
|
|
$GLOBALS['bus']->addData($this->getResponseData());
|
|
break;
|
|
case 'remove':
|
|
$this->addActionData('remove', array(
|
|
'type' => 3,
|
|
'remove' => $this->removeDevice($actionData['deviceid'])
|
|
));
|
|
$GLOBALS['bus']->addData($this->getResponseData());
|
|
break;
|
|
case 'list':
|
|
$items = array();
|
|
$date['page'] = array();
|
|
|
|
$rawData = $this->getDevices();
|
|
foreach($rawData as $device){
|
|
array_push($items, array('props' => $this->getDeviceProps($device->data)));
|
|
}
|
|
$data['page']['start'] = 0;
|
|
$data['page']['rowcount'] = count($rawData);
|
|
$data['page']['totalrowcount'] = $data['page']['rowcount'];
|
|
$data = array_merge($data, array('item' => $items));
|
|
|
|
$this->addActionData('list', $data);
|
|
$GLOBALS['bus']->addData($this->getResponseData());
|
|
break;
|
|
|
|
case 'open':
|
|
$device = $this->getDeviceDetails($actionData["entryid"]);
|
|
$item = array("item" => $device);
|
|
$this->addActionData('item', $item);
|
|
$GLOBALS['bus']->addData($this->getResponseData());
|
|
break;
|
|
case 'save':
|
|
$this ->saveDevice($actionData);
|
|
$device = $this->getDeviceDetails($actionData["entryid"]);
|
|
$item = array("item" => $device);
|
|
$this->addActionData('update', $item);
|
|
$GLOBALS['bus']->addData($this->getResponseData());
|
|
break;
|
|
default:
|
|
$this->handleUnknownActionType($actionType);
|
|
}
|
|
} catch (SoapFault $fault) {
|
|
$display_message = dgettext('plugin_mdm', 'Something went wrong.');
|
|
if ($fault->faultcode === 'HTTP') {
|
|
if ($fault->getMessage() === "Unauthorized") {
|
|
$display_message = dgettext('plugin_mdm', 'Unable to connect to Z-Push Server. Unauthorized.');
|
|
}
|
|
if ($fault->getMessage() === "Could not connect to host") {
|
|
$display_message = dgettext('plugin_mdm', 'Unable to connect to Z-Push Server. Could not connect to host.');
|
|
}
|
|
if ($fault->getMessage() === "Not Found") {
|
|
$display_message = dgettext('plugin_mdm', 'Unable to connect to Z-Push Server. Not found.');
|
|
}
|
|
} else if ($fault->faultcode === "ERROR") {
|
|
$errors = (explode(": ", $fault->getMessage()));
|
|
switch ($errors[0]) {
|
|
case "ASDevice->AddAdditionalFolder()":
|
|
case "ZPushAdmin::AdditionalFolderAdd()":
|
|
$display_message = dgettext('plugin_mdm', "Folder can not be added because there is already an additional folder with the same name");
|
|
break;
|
|
|
|
case "ASDevice->RemoveAdditionalFolder()":
|
|
case "ZPushAdmin::AdditionalFolderRemove()":
|
|
$display_message = dgettext('plugin_mdm', "Folder can not be removed because there is no folder known with given folder id");
|
|
break;
|
|
default:
|
|
$display_message = dgettext('plugin_mdm', "Device ID could not be found");
|
|
}
|
|
}
|
|
$this->sendFeedback(false, array("type" => ERROR_GENERAL, "info" => array('display_message' => $display_message)));
|
|
}
|
|
catch (Exception $e) {
|
|
$this->sendFeedback(true, array("type" => ERROR_GENERAL, "info" => array('display_message' => dgettext('plugin_mdm', 'Something went wrong'))));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Function which is use to get device properties.
|
|
*
|
|
* @param array $device array of device properties
|
|
* @return array
|
|
*/
|
|
function getDeviceProps($device)
|
|
{
|
|
$item = array();
|
|
$propsList = ['devicetype', 'deviceos', 'devicefriendlyname', 'useragent', 'asversion', 'firstsynctime',
|
|
'lastsynctime', 'lastupdatetime', 'wipestatus', 'policyname', 'koeversion', 'koebuild', 'koebuilddate'];
|
|
|
|
$item['entryid'] = $device['deviceid'];
|
|
$item['message_class'] = "IPM.MDM";
|
|
foreach ($propsList as $prop) {
|
|
if (isset($device[$prop])) {
|
|
$item[$prop] = $device[$prop];
|
|
}
|
|
}
|
|
|
|
$item = array_merge($item, $this->getSyncFoldersProps($device));
|
|
return $item;
|
|
}
|
|
|
|
/**
|
|
* Function which is use to gather some statistics about synchronized folders.
|
|
* @param array $device array of device props
|
|
* @return array $syncFoldersProps has list of properties related to synchronized folders
|
|
*/
|
|
function getSyncFoldersProps($device)
|
|
{
|
|
$contentData = $device['contentdata'];
|
|
$folders = array_keys($contentData);
|
|
$synchedFolderTypes = array();
|
|
$synchronizedFolders = 0;
|
|
$hierarchyCache = isset($device['hierarchycache']) ? $device['hierarchycache'] : false;
|
|
|
|
foreach ($folders as $folderid) {
|
|
if (isset($contentData[$folderid][self::FOLDERUUID]) || isset($device['hierarchyuuid'])) {
|
|
$type = $contentData[$folderid][self::FOLDERTYPE];
|
|
$name = "unknown";
|
|
if ($hierarchyCache) {
|
|
if (array_key_exists($folderid, $hierarchyCache->cacheById)) {
|
|
$folder = $hierarchyCache->cacheById[$folderid];
|
|
} else {
|
|
$folder = $hierarchyCache->cacheByIdOld[$folderid];
|
|
}
|
|
if ($folder) {
|
|
$name = $folder->displayname;
|
|
}
|
|
}
|
|
|
|
$folderType = $this->getSyncFolderType($type, $name);
|
|
|
|
if (isset($contentData[$folderid][self::FOLDERUUID])) {
|
|
if(isset($synchedFolderTypes[$folderType])){
|
|
$synchedFolderTypes[$folderType]++;
|
|
} else {
|
|
$synchedFolderTypes[$folderType] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$syncFoldersProps = array();
|
|
foreach ($synchedFolderTypes as $key => $value) {
|
|
$synchronizedFolders += $value;
|
|
$syncFoldersProps[strtolower($key) . 'folder'] = $value;
|
|
}
|
|
$client = $this->getSoapClient();
|
|
$items = $client->AdditionalFolderList($device['deviceid']);
|
|
$syncFoldersProps['sharedfolders'] = count($items);
|
|
$syncFoldersProps["shortfolderids"] = $device['hasfolderidmapping'] ? dgettext('plugin_mdm', "Yes") : dgettext('plugin_mdm', "No");
|
|
$syncFoldersProps['synchronizedfolders'] = $synchronizedFolders + count($items);
|
|
|
|
return $syncFoldersProps;
|
|
}
|
|
|
|
|
|
/**
|
|
* Function which is use to get general type like Mail,Calendar,Contacts,etc. from folder type.
|
|
* @param int $type foldertype for a folder already known to the mobile
|
|
* @param string $name folder name
|
|
* @return string general folder type
|
|
*/
|
|
function getSyncFolderType($type, $name)
|
|
{
|
|
switch ($type) {
|
|
case SYNC_FOLDER_TYPE_APPOINTMENT:
|
|
case SYNC_FOLDER_TYPE_USER_APPOINTMENT:
|
|
if (KOE_GAB_NAME != "" && $name == KOE_GAB_NAME) {
|
|
$folderType = "GAB";
|
|
|
|
} else {
|
|
$folderType = "Calendars";
|
|
}
|
|
break;
|
|
case SYNC_FOLDER_TYPE_CONTACT:
|
|
case SYNC_FOLDER_TYPE_USER_CONTACT:
|
|
$folderType = "Contacts";
|
|
break;
|
|
case SYNC_FOLDER_TYPE_TASK:
|
|
case SYNC_FOLDER_TYPE_USER_TASK:
|
|
$folderType = "Tasks";
|
|
break;
|
|
case SYNC_FOLDER_TYPE_NOTE:
|
|
case SYNC_FOLDER_TYPE_USER_NOTE:
|
|
$folderType = "Notes";
|
|
break;
|
|
default:
|
|
$folderType = "Emails";
|
|
break;
|
|
}
|
|
return $folderType;
|
|
}
|
|
|
|
/**
|
|
* Function which is use to get list of additional folders which was shared with given device
|
|
* @param string $devid device id
|
|
* @return array has list of properties related to shared folders
|
|
*/
|
|
function getAdditionalFolderList($devid)
|
|
{
|
|
$stores = $GLOBALS["mapisession"]->getAllMessageStores();
|
|
$client = $this->getSoapClient();
|
|
$items = $client->AdditionalFolderList($devid);
|
|
$data = array();
|
|
foreach ($items as $item)
|
|
{
|
|
foreach ($stores as $store)
|
|
{
|
|
try {
|
|
$entryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($item->folderid));
|
|
} catch (MAPIException $me) {
|
|
continue;
|
|
}
|
|
}
|
|
if (isset($entryid)) {
|
|
$item->entryid = bin2hex($entryid);
|
|
}
|
|
array_push($data, array("props" => $item));
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Function which is use to remove additional folder which was shared with given device.
|
|
* @param string $entryId id of device.
|
|
* @param string $folderid id of folder which will remove from device.
|
|
*/
|
|
function additionalFolderRemove($entryId, $folderid)
|
|
{
|
|
$client = $this->getSoapClient();
|
|
$client->AdditionalFolderRemove($entryId, $folderid);
|
|
}
|
|
|
|
/**
|
|
* Function which is use to add additional folder which will share with given device.
|
|
* @param string $entryId id of device.
|
|
* @param array $folder folder which will share with device.
|
|
*/
|
|
function additionalFolderAdd($entryId, $folder)
|
|
{
|
|
$client = $this->getSoapClient();
|
|
$containerClass = isset($folder[PR_CONTAINER_CLASS]) ? $folder[PR_CONTAINER_CLASS] : "IPF.Note";
|
|
$folderId = bin2hex($folder[PR_SOURCE_KEY]);
|
|
$userName = $folder["user"];
|
|
$folderName = $userName === "SYSTEM" ? $folder[PR_DISPLAY_NAME] : $folder[PR_DISPLAY_NAME]." - ".$userName;
|
|
$folderType = $this->getFolderTypeFromContainerClass($containerClass);
|
|
$client->AdditionalFolderAdd($entryId, $userName, $folderId, $folderName, $folderType, FLD_FLAGS_REPLYASUSER);
|
|
}
|
|
|
|
/**
|
|
* Function which use to save the device.
|
|
* It will use to add or remove folders in the device.
|
|
* @param array $data array of added and removed folders.
|
|
*/
|
|
function saveDevice($data)
|
|
{
|
|
$entryid = $data["entryid"];
|
|
if (isset($data['sharedfolders'])) {
|
|
if (isset($data['sharedfolders']['remove'])) {
|
|
$deletedFolders = $data['sharedfolders']['remove'];
|
|
foreach ($deletedFolders as $folder) {
|
|
$this->additionalFolderRemove($entryid, $folder["folderid"]);
|
|
}
|
|
}
|
|
if (isset($data['sharedfolders']['add'])) {
|
|
$addFolders = $data['sharedfolders']['add'];
|
|
$hierarchyFolders = $this->getHierarchyList();
|
|
foreach ($addFolders as $folder) {
|
|
foreach ($hierarchyFolders as $hierarchyFolder) {
|
|
$folderEntryid = bin2hex($hierarchyFolder[PR_ENTRYID]);
|
|
if ($folderEntryid === $folder["entryid"]) {
|
|
$this->additionalFolderAdd($entryid, $hierarchyFolder);
|
|
continue 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the hierarchy list of all required stores.
|
|
* Function which is use to get the hierarchy list with PR_SOURCE_KEY.
|
|
* @return array the array of all hierarchy folders.
|
|
*/
|
|
function getHierarchyList()
|
|
{
|
|
$storeList = $GLOBALS["mapisession"]->getAllMessageStores();
|
|
$properties = $GLOBALS["properties"]->getFolderListProperties();
|
|
$otherUsers = $GLOBALS["mapisession"]->retrieveOtherUsersFromSettings();
|
|
$properties["source_key"] = PR_SOURCE_KEY;
|
|
$openWholeStore = true;
|
|
$storeData = array();
|
|
|
|
foreach ($storeList as $store) {
|
|
$msgstore_props = mapi_getprops($store, array(PR_MDB_PROVIDER, PR_ENTRYID, PR_IPM_SUBTREE_ENTRYID, PR_USER_NAME));
|
|
$storeType = $msgstore_props[PR_MDB_PROVIDER];
|
|
|
|
if ($storeType == ZARAFA_SERVICE_GUID) {
|
|
continue;
|
|
} else if ($storeType == ZARAFA_STORE_DELEGATE_GUID) {
|
|
$storeUserName = $GLOBALS["mapisession"]->getUserNameOfStore($msgstore_props[PR_ENTRYID]);
|
|
} else if ($storeType == ZARAFA_STORE_PUBLIC_GUID) {
|
|
$storeUserName = "SYSTEM";
|
|
} else {
|
|
$storeUserName = $msgstore_props[PR_USER_NAME];
|
|
}
|
|
|
|
if (is_array($otherUsers)) {
|
|
if (isset($otherUsers[$storeUserName])) {
|
|
$sharedFolders = $otherUsers[$storeUserName];
|
|
if (!isset($otherUsers[$storeUserName]['all'])) {
|
|
$openWholeStore = false;
|
|
$a = $this->getSharedFolderList($store, $sharedFolders, $properties, $storeUserName);
|
|
$storeData = array_merge($storeData, $a);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($openWholeStore) {
|
|
if (isset($msgstore_props[PR_IPM_SUBTREE_ENTRYID])) {
|
|
$subtreeFolderEntryID = $msgstore_props[PR_IPM_SUBTREE_ENTRYID];
|
|
try {
|
|
$subtreeFolder = mapi_msgstore_openentry($store, $subtreeFolderEntryID);
|
|
} catch (MAPIException $e) {
|
|
// We've handled the event
|
|
$e->setHandled();
|
|
}
|
|
|
|
if ($storeType != ZARAFA_STORE_PUBLIC_GUID) {
|
|
$this->getSubFolders($subtreeFolder, $store, $properties, $storeData, $storeUserName);
|
|
} else {
|
|
$this->getSubFoldersPublic($subtreeFolder, $store, $properties, $storeData, $storeUserName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $storeData;
|
|
}
|
|
|
|
/**
|
|
* Helper function to get the shared folder list.
|
|
* @param object $store Message Store Object.
|
|
* @param object $sharedFolders Mapi Folder Object.
|
|
* @param array $properties MAPI property mappings for folders.
|
|
* @param string $storeUserName owner name of store.
|
|
* @return array shared folders list.
|
|
*/
|
|
function getSharedFolderList($store, $sharedFolders, $properties, $storeUserName)
|
|
{
|
|
$msgstore_props = mapi_getprops($store, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_IPM_SUBTREE_ENTRYID, PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_MDB_PROVIDER, PR_IPM_PUBLIC_FOLDERS_ENTRYID, PR_IPM_FAVORITES_ENTRYID, PR_OBJECT_TYPE, PR_STORE_SUPPORT_MASK, PR_MAILBOX_OWNER_ENTRYID, PR_MAILBOX_OWNER_NAME, PR_USER_ENTRYID, PR_USER_NAME, PR_QUOTA_WARNING_THRESHOLD, PR_QUOTA_SEND_THRESHOLD, PR_QUOTA_RECEIVE_THRESHOLD, PR_MESSAGE_SIZE_EXTENDED, PR_MAPPING_SIGNATURE, PR_COMMON_VIEWS_ENTRYID, PR_FINDER_ENTRYID));
|
|
$storeData = array();
|
|
$folders = array();
|
|
try {
|
|
$inbox = mapi_msgstore_getreceivefolder($store);
|
|
$inboxProps = mapi_getprops($inbox, array(PR_ENTRYID));
|
|
} catch (MAPIException $e) {
|
|
// don't propogate this error to parent handlers, if store doesn't support it
|
|
if ($e->getCode() === MAPI_E_NO_SUPPORT) {
|
|
$e->setHandled();
|
|
}
|
|
}
|
|
|
|
$root = mapi_msgstore_openentry($store, null);
|
|
$rootProps = mapi_getprops($root, array(PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_JOURNAL_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_TASK_ENTRYID, PR_ADDITIONAL_REN_ENTRYIDS));
|
|
|
|
$additional_ren_entryids = array();
|
|
if (isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS])) {
|
|
$additional_ren_entryids = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS];
|
|
}
|
|
|
|
$defaultfolders = array(
|
|
"default_folder_inbox" => array("inbox" => PR_ENTRYID),
|
|
"default_folder_outbox" => array("store" => PR_IPM_OUTBOX_ENTRYID),
|
|
"default_folder_sent" => array("store" => PR_IPM_SENTMAIL_ENTRYID),
|
|
"default_folder_wastebasket" => array("store" => PR_IPM_WASTEBASKET_ENTRYID),
|
|
"default_folder_favorites" => array("store" => PR_IPM_FAVORITES_ENTRYID),
|
|
"default_folder_publicfolders" => array("store" => PR_IPM_PUBLIC_FOLDERS_ENTRYID),
|
|
"default_folder_calendar" => array("root" => PR_IPM_APPOINTMENT_ENTRYID),
|
|
"default_folder_contact" => array("root" => PR_IPM_CONTACT_ENTRYID),
|
|
"default_folder_drafts" => array("root" => PR_IPM_DRAFTS_ENTRYID),
|
|
"default_folder_journal" => array("root" => PR_IPM_JOURNAL_ENTRYID),
|
|
"default_folder_note" => array("root" => PR_IPM_NOTE_ENTRYID),
|
|
"default_folder_task" => array("root" => PR_IPM_TASK_ENTRYID),
|
|
"default_folder_junk" => array("additional" => 4),
|
|
"default_folder_syncissues" => array("additional" => 1),
|
|
"default_folder_conflicts" => array("additional" => 0),
|
|
"default_folder_localfailures" => array("additional" => 2),
|
|
"default_folder_serverfailures" => array("additional" => 3),
|
|
);
|
|
|
|
foreach ($defaultfolders as $key => $prop) {
|
|
$tag = reset($prop);
|
|
$from = key($prop);
|
|
switch ($from) {
|
|
case "inbox":
|
|
if (isset($inboxProps[$tag])) {
|
|
$storeData["props"][$key] = bin2hex($inboxProps[$tag]);
|
|
}
|
|
break;
|
|
case "store":
|
|
if (isset($msgstore_props[$tag])) {
|
|
$storeData["props"][$key] = bin2hex($msgstore_props[$tag]);
|
|
}
|
|
break;
|
|
case "root":
|
|
if (isset($rootProps[$tag])) {
|
|
$storeData["props"][$key] = bin2hex($rootProps[$tag]);
|
|
}
|
|
break;
|
|
case "additional":
|
|
if (isset($additional_ren_entryids[$tag])) {
|
|
$storeData["props"][$key] = bin2hex($additional_ren_entryids[$tag]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
$store_access = true;
|
|
$openSubFolders = false;
|
|
foreach ($sharedFolders as $type => $sharedFolder) {
|
|
$openSubFolders = ($sharedFolder["show_subfolders"] == true);
|
|
$folderEntryID = hex2bin($storeData["props"]["default_folder_" . $sharedFolder["folder_type"]]);
|
|
try {
|
|
// load folder props
|
|
$folder = mapi_msgstore_openentry($store, $folderEntryID);
|
|
} catch (MAPIException $e) {
|
|
// Indicate that we don't have access to the store,
|
|
// so no more attempts to read properties or open entries.
|
|
$store_access = false;
|
|
|
|
// We've handled the event
|
|
$e->setHandled();
|
|
}
|
|
}
|
|
|
|
if ($store_access === true) {
|
|
$folderProps = mapi_getprops($folder, $properties);
|
|
$folderProps["user"] = $storeUserName;
|
|
array_push($folders, $folderProps);
|
|
|
|
//If folder has sub folders then add its.
|
|
if ($openSubFolders === true) {
|
|
if ($folderProps[PR_SUBFOLDERS] != false) {
|
|
$subFoldersData = array();
|
|
$this->getSubFolders($folder, $store, $properties, $subFoldersData, $storeUserName);
|
|
$folders =array_merge($folders, $subFoldersData);
|
|
}
|
|
}
|
|
}
|
|
return $folders;
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function to get the sub folders of a given folder.
|
|
*
|
|
* @access private
|
|
* @param object $folder Mapi Folder Object.
|
|
* @param object $store Message Store Object
|
|
* @param array $properties MAPI property mappings for folders
|
|
* @param array $storeData Reference to an array. The folder properties are added to this array.
|
|
* @param string $storeUserName owner name of store.
|
|
*/
|
|
function getSubFolders($folder, $store, $properties, &$storeData, $storeUserName)
|
|
{
|
|
/**
|
|
* remove hidden folders, folders with PR_ATTR_HIDDEN property set
|
|
* should not be shown to the client
|
|
*/
|
|
$restriction = Array(RES_OR, Array(
|
|
Array(RES_PROPERTY,
|
|
Array(
|
|
RELOP => RELOP_EQ,
|
|
ULPROPTAG => PR_ATTR_HIDDEN,
|
|
VALUE => Array(PR_ATTR_HIDDEN => false)
|
|
)
|
|
),
|
|
Array(RES_NOT,
|
|
Array(
|
|
Array(RES_EXIST,
|
|
Array(
|
|
ULPROPTAG => PR_ATTR_HIDDEN
|
|
)
|
|
)
|
|
)
|
|
)
|
|
));
|
|
|
|
|
|
$expand = Array(
|
|
Array(
|
|
'folder' => $folder,
|
|
'props' => mapi_getprops($folder, Array(PR_ENTRYID, PR_SUBFOLDERS))
|
|
)
|
|
);
|
|
|
|
// Start looping through the $expand array, during each loop we grab the first item in
|
|
// the array and obtain the hierarchy table for that particular folder. If one of those
|
|
// subfolders has subfolders of its own, it will be appended to $expand again to ensure
|
|
// it will be expanded later.
|
|
while (!empty($expand)) {
|
|
$item = array_shift($expand);
|
|
$columns = $properties;
|
|
|
|
$hierarchyTable = mapi_folder_gethierarchytable($item['folder'], MAPI_DEFERRED_ERRORS);
|
|
mapi_table_restrict($hierarchyTable, $restriction, TBL_BATCH);
|
|
|
|
mapi_table_setcolumns($hierarchyTable, $columns);
|
|
$columns = null;
|
|
|
|
// Load the hierarchy in small batches
|
|
$batchcount = 100;
|
|
do {
|
|
$rows = mapi_table_queryrows($hierarchyTable, $columns, 0, $batchcount);
|
|
|
|
foreach ($rows as $subfolder) {
|
|
|
|
// If the subfolders has subfolders of its own, append the folder
|
|
// to the $expand array, so it can be expanded in the next loop.
|
|
if ($subfolder[PR_SUBFOLDERS]) {
|
|
$folderObject = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
|
|
array_push($expand, array('folder' => $folderObject, 'props' => $subfolder));
|
|
}
|
|
$subfolder["user"] = $storeUserName;
|
|
// Add the folder to the return list.
|
|
array_push($storeData, $subfolder);
|
|
}
|
|
|
|
// When the server returned a different number of rows then was requested,
|
|
// we have reached the end of the table and we should exit the loop.
|
|
} while (count($rows) === $batchcount);
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper function to get the subfolders of a Public Store
|
|
*
|
|
* @access private
|
|
* @param object $folder Mapi Folder Object.
|
|
* @param object $store Message Store Object
|
|
* @param array $properties MAPI property mappings for folders
|
|
* @param array $storeData Reference to an array. The folder properties are added to this array.
|
|
* @param string $storeUserName owner name of store.
|
|
*/
|
|
function getSubFoldersPublic($folder, $store, $properties, &$storeData, $storeUserName)
|
|
{
|
|
$expand = Array(
|
|
Array(
|
|
'folder' => $folder,
|
|
'props' => mapi_getprops($folder, Array(PR_ENTRYID, PR_SUBFOLDERS))
|
|
)
|
|
);
|
|
|
|
/**
|
|
* remove hidden folders, folders with PR_ATTR_HIDDEN property set
|
|
* should not be shown to the client
|
|
*/
|
|
$restriction = Array(RES_OR, Array(
|
|
Array(RES_PROPERTY,
|
|
Array(
|
|
RELOP => RELOP_EQ,
|
|
ULPROPTAG => PR_ATTR_HIDDEN,
|
|
VALUE => Array( PR_ATTR_HIDDEN => false )
|
|
)
|
|
),
|
|
Array(RES_NOT,
|
|
Array(
|
|
Array(RES_EXIST,
|
|
Array(
|
|
ULPROPTAG => PR_ATTR_HIDDEN
|
|
)
|
|
)
|
|
)
|
|
)
|
|
));
|
|
|
|
// CONVENIENT_DEPTH doesn't work on the IPM_SUBTREE, hence we will be recursivly
|
|
// walking through the hierarchy. However, we have some special folders like the
|
|
// "Favorites" and "Public Folders" from where we can switch to using
|
|
// CONVENIENT_DEPTH. Obtain these special cases here.
|
|
$specialEntryids = mapi_getprops($store, array(
|
|
PR_IPM_FAVORITES_ENTRYID,
|
|
PR_IPM_PUBLIC_FOLDERS_ENTRYID
|
|
));
|
|
|
|
// Start looping through the $expand array, during each loop we grab the first item in
|
|
// the array and obtain the hierarchy table for that particular folder. If one of those
|
|
// subfolders has subfolders of its own, it will be appended to $expand again to ensure
|
|
// it will be expanded later.
|
|
while (!empty($expand)) {
|
|
$item = array_shift($expand);
|
|
$columns = $properties;
|
|
$hierarchyTable = mapi_folder_gethierarchytable($item['folder'], MAPI_DEFERRED_ERRORS);
|
|
|
|
mapi_table_restrict($hierarchyTable, $restriction, TBL_BATCH);
|
|
|
|
mapi_table_setcolumns($hierarchyTable, $columns);
|
|
$columns = null;
|
|
|
|
// Load the hierarchy in small batches
|
|
$batchcount = 100;
|
|
do {
|
|
$rows = mapi_table_queryrows($hierarchyTable, $columns, 0, $batchcount);
|
|
|
|
foreach($rows as $subfolder) {
|
|
$specialFolder = false;
|
|
|
|
// Check if this folder is special...
|
|
if (!empty($specialEntryids)) {
|
|
foreach ($specialEntryids as $key => $value) {
|
|
// No need to do compareEntryId(), the special folders have static
|
|
// entryids, and can be compared using ===.
|
|
if (bin2hex($subfolder[PR_ENTRYID]) === bin2hex($value)) {
|
|
$specialFolder = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
|
|
$subfolder = mapi_getprops($specialFolder, $properties);
|
|
|
|
// We found the folder, no need to loop over it next time.
|
|
unset($specialEntryids[$key]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the subfolders has subfolders of its own, append the folder
|
|
// to the $expand array, so it can be expanded in the next loop.
|
|
if ($subfolder[PR_SUBFOLDERS]) {
|
|
if ($specialFolder) {
|
|
// Special folders can be redirected again to getSubFolders(),
|
|
$this->getSubFolders($specialFolder, $store, $properties, $storeData, $subfolder[PR_ENTRYID], $storeUserName);
|
|
} else {
|
|
$folderObject = mapi_msgstore_openentry($store, $subfolder[PR_ENTRYID]);
|
|
array_push($expand, array('folder' => $folderObject, 'props' => $subfolder));
|
|
}
|
|
}
|
|
|
|
$subfolder["user"] = $storeUserName;
|
|
// Add the folder to the return list.
|
|
array_push($storeData, $subfolder);
|
|
}
|
|
|
|
// When the server returned a different number of rows then was requested,
|
|
// we have reached the end of the table and we should exit the loop.
|
|
} while (count($rows) === $batchcount);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function which is use get folder types from the container class
|
|
* @param string $containerClass container class of folder
|
|
* @return int folder type
|
|
*/
|
|
function getFolderTypeFromContainerClass($containerClass)
|
|
{
|
|
switch ($containerClass) {
|
|
case "IPF.Note":
|
|
return SYNC_FOLDER_TYPE_USER_MAIL;
|
|
case "IPF.Appointment":
|
|
return SYNC_FOLDER_TYPE_USER_APPOINTMENT;
|
|
case "IPF.Contact":
|
|
return SYNC_FOLDER_TYPE_USER_CONTACT;
|
|
case "IPF.StickyNote":
|
|
return SYNC_FOLDER_TYPE_USER_NOTE;
|
|
case "IPF.Task":
|
|
return SYNC_FOLDER_TYPE_USER_TASK;
|
|
case "IPF.Journal":
|
|
return SYNC_FOLDER_TYPE_USER_JOURNAL;
|
|
default:
|
|
return SYNC_FOLDER_TYPE_UNKNOWN;
|
|
}
|
|
}
|
|
};
|
|
?>
|