⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.72
Server IP:
41.128.143.86
Server:
Linux host.raqmix.cloud 6.8.0-1025-azure #30~22.04.1-Ubuntu SMP Wed Mar 12 15:28:20 UTC 2025 x86_64
Server Software:
Apache
PHP Version:
8.3.23
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
usr
/
share
/
psa-horde
/
turba
/
lib
/
Driver
/
View File Name :
Kolab.php
* @author Gunnar Wrobel
* @author Stuart Binge
* @category Horde * @license http://www.horde.org/licenses/apache ASL * @package Turba */ class Turba_Driver_Kolab extends Turba_Driver { /** * The Kolab_Storage backend. * * @var Horde_Kolab_Storage */ protected $_kolab; /** * Indicates if the driver has been connected to a specific addressbook or * not. * * @var boolean */ protected $_connected = false; /** * The current addressbook. * * @var Horde_Kolab_Storage_Data */ protected $_data; /** * The current addressbook, serving groups. * * @var Horde_Kolab_Storage_Data */ protected $_listData; /** * The current addressbook represented as share. * * @var Horde_Share_Object */ protected $_share; /** * The cached contacts. * * @var array */ protected $_contacts_cache; /** * What can this backend do? * * @var array */ protected $_capabilities = array( 'delete_addressbook' => true, 'delete_all' => true, ); /** * Any additional options passed to Turba_Object constructors. * * @var array */ protected $_objectOptions = array('removeMissing' => true); /** * Attempts to open a Kolab Groupware folder. */ public function __construct($name = '', $params = array()) { if (empty($params['storage'])) { throw new InvalidArgumentException('Missing required storage handler.'); } $this->_kolab = $params['storage']; unset($params['storage']); if (isset($params['share'])) { $this->_share = $params['share']; } if (isset($params['name'])) { $name = $params['name']; } parent::__construct($name, $params); } /** * Translates the keys of the first hash from the generalized Turba * attributes to the driver-specific fields. The translation is based on * the contents of $this->map. * * @param array $hash Hash using Turba keys. * * @return array Translated version of $hash. */ public function toDriverKeys(array $hash) { if (isset($hash['__tags'])) { if (!is_array($hash['__tags'])) { $hash['__tags'] = $GLOBALS['injector'] ->getInstance('Turba_Tagger') ->split($hash['__tags']); } usort($hash['__tags'], 'strcoll'); $hash['__internaltags'] = serialize($hash['__tags']); } $hash = parent::toDriverKeys($hash); if (isset($hash['name'])) { $hash['name'] = array('full-name' => $hash['name']); } /* TODO: use Horde_Kolab_Format_Xml_Type_Composite_* */ foreach (array('full-name', 'given-name', 'middle-names', 'last-name', 'initials', 'prefix', 'suffix') as $sub) { if (isset($hash[$sub])) { $hash['name'][$sub] = $hash[$sub]; unset($hash[$sub]); } } if (isset($hash['__type']) && $hash['__type'] == 'Group' && isset($hash['name']['full-name'])) { $hash['display-name'] = $hash['name']['full-name']; } if (isset($hash['emails'])) { $list = new Horde_Mail_Rfc822_List($hash['emails']); $hash['email'] = array(); foreach ($list as $address) { $hash['email'][] = array('smtp-address' => $address->bare_address); } unset($hash['emails']); } foreach (array('phone-business1', 'phone-business2', 'phone-businessfax', 'phone-car', 'phone-company', 'phone-home1', 'phone-home2', 'phone-homefax', 'phone-mobile', 'phone-pager', 'phone-radio', 'phone-assistant') as $sub) { if (isset($hash[$sub])) { if (!isset($hash['phone'])) { $hash['phone'] = array(); } $hash['phone'][] = array('type' => substr($sub, 6), 'number' => $hash[$sub]); unset($hash[$sub]); } } $address = array(); foreach (array('addr-business-street', 'addr-business-locality', 'addr-business-region', 'addr-business-postal-code', 'addr-business-country') as $sub) { if (isset($hash[$sub])) { $address[substr($sub, 14)] = $hash[$sub]; unset($hash[$sub]); } } if ($address) { $hash['address'] = array(); $address['type'] = 'business'; $hash['address'][] = $address; } $address = array(); foreach (array('addr-home-street', 'addr-home-locality', 'addr-home-region', 'addr-home-postal-code', 'addr-home-country') as $sub) { if (isset($hash[$sub])) { $address[substr($sub, 10)] = $hash[$sub]; unset($hash[$sub]); } } if ($address) { if (!isset($hash['address'])) { $hash['address'] = array(); } $address['type'] = 'home'; $hash['address'][] = $address; } if (isset($hash['categories'])) { $hash['categories'] = unserialize($hash['categories']); } if (!empty($hash['birthday'])) { $hash['birthday'] = new DateTime($hash['birthday']); } if (!empty($hash['anniversary'])) { $hash['anniversary'] = new DateTime($hash['anniversary']); } return $hash; } /** * Translates a hash from being keyed on driver-specific fields to being * keyed on the generalized Turba attributes. The translation is based on * the contents of $this->map. * * @param array $entry A hash using driver-specific keys. * * @return array Translated version of $entry. */ public function toTurbaKeys(array $entry) { if (isset($entry['__type']) && $entry['__type'] == 'Group' && isset($entry['display-name'])) { $entry['last-name'] = $entry['display-name']; } return parent::toTurbaKeys($entry); } /** * Returns the Kolab data handler for contacts in the current address book. * * @return Horde_Kolab_Storage_Data The data handler. */ protected function _getData() { if ($this->_data === null) { $this->_data = $this->_createData('contact'); } return $this->_data; } /** * Returns the Kolab data handler for distribution lists in the current * address book. * * @return Horde_Kolab_Storage_Data The data handler. */ protected function _getListData() { if ($this->_listData === null) { $this->_listData = $this->_createData('distribution-list'); } return $this->_listData; } /** * Returns a new Kolab data handler for the current address book. * * @param string $type An object type, either 'contact' or * 'distribution-list'. * * @return Horde_Kolab_Storage_Data A data handler. */ protected function _createData($type) { if (!empty($this->_share)) { $share = $this->_share; } else { if (empty($this->_name)) { throw new Turba_Exception( 'The addressbook has been left undefined but is required!' ); } $share = $GLOBALS['injector'] ->getInstance('Turba_Shares') ->getShare($this->_name); } $data = $this->_kolab->getData($share->get('folder'), $type); $this->setContactOwner($share->get('owner')); return $data; } /** * Connect to the Kolab backend. * * @throws Turba_Exception */ public function connect() { if ($this->_connected) { return; } /* Fetch the contacts first */ $raw_contacts = $this->_getData()->getObjects(); if (!$raw_contacts) { $raw_contacts = array(); } $contacts = array(); foreach ($raw_contacts as $id => $contact) { if ($contact->getType() != 'contact') { continue; } $backendId = $contact->getBackendId(); $contact = $contact->getData(); $contact['__type'] = 'Object'; $contact['__key'] = Horde_Url::uriB64Encode($id); if (isset($contact['categories'])) { $contact['categories'] = serialize($contact['categories']); } if (isset($contact['picture'])) { $name = $contact['picture']; $stream = $this->_getData()->getAttachment($backendId, $name); if ($stream) { $contact['photo'] = new Horde_Stream_Existing(array( 'stream' => $stream )); foreach ($contact['_attachments']['type'] as $type => $list) { if (array_search($name, $list) !== false) { $contact['phototype'] = $type; break; } } } } if (isset($contact['name'])) { foreach ($contact['name'] as $detail => $value) { $contact[$detail] = $value; } unset($contact['name']); } if (isset($contact['phone'])) { foreach ($contact['phone'] as $phone) { $contact['phone-' . $phone['type']] = $phone['number']; } unset($contact['phone']); } if (isset($contact['email'])) { $contact['emails'] = array(); foreach ($contact['email'] as $email) { $contact['emails'][] = $email['smtp-address']; } if ($contact['emails']) { $contact['email'] = $contact['emails'][0]; } else { unset($contact['email']); } $contact['emails'] = implode(', ', $contact['emails']); } if (isset($contact['address'])) { foreach ($contact['address'] as $address) { foreach ($address as $detail => $value) { if ($detail != 'type') { $contact['addr-' . $address['type'] . '-' . $detail] = $value; } } } unset($contact['address']); } if (!empty($contact['birthday'])) { $contact['birthday'] = $contact['birthday']->format('Y-m-d'); } if (!empty($contact['anniversary'])) { $contact['anniversary'] = $contact['anniversary']->format('Y-m-d'); } $contacts[Horde_Url::uriB64Encode($id)] = $contact; } /* Now we retrieve distribution-lists */ $raw_groups = $this->_getListData()->getObjects(); $groups = array(); if ($raw_groups) { foreach ($raw_groups as $id => $group) { if ($group->getType() != 'distribution-list') { continue; } $group = $group->getData(); $group['__type'] = 'Group'; $group['__key'] = Horde_Url::uriB64Encode($id); if (isset($group['categories'])) { $group['categories'] = serialize($group['categories']); } $groups[Horde_Url::uriB64Encode($id)] = $group; } } /* Store the results in our cache */ $this->_contacts_cache = array_merge($contacts, $groups); $this->_connected = true; } /** * Searches the address book with the given criteria and returns a * filtered list of results. If the criteria parameter is an empty array, * all records will be returned. * * @param array $criteria Array containing the search criteria. * @param array $fields List of fields to return. * @param array $blobFields Array of fields containing binary data. * * @return array Hash containing the search results. * @throws Turba_Exception */ protected function _search(array $criteria, array $fields, array $blobFields = array(), $count_only = false) { $this->connect(); if (!count($criteria)) { return $count_only ? count($this->_contacts_cache) : array_values($this->_contacts_cache); } // keep only entries matching criteria $ids = array(); foreach ($criteria as $key => $criteria) { $ids[] = $this->_doSearch($criteria, strval($key)); } $ids = $this->_removeDuplicated($ids); /* Now we have a list of names, get the rest. */ if ($ids) { $result = $this->_read( 'uid', array_map(array('Horde_Url', 'uriB64Encode'), $ids), null, $fields ); } else { $result = array(); } Horde::log(sprintf('Kolab returned %s results', count($result)), 'DEBUG'); return $count_only ? count($result) : array_values($result); } /** * Applies the filter criteria to a list of entries * * @param array $criteria Array containing the search criteria. * @param array $fields List of fields to return. * * @return array Array containing the ids of the selected entries. */ protected function _doSearch($criteria, $glue) { $ids = array(); foreach ($criteria as $vals) { if (!empty($vals['OR'])) { $ids[] = $this->_doSearch($vals['OR'], 'OR'); } elseif (!empty($vals['AND'])) { $ids[] = $this->_doSearch($vals['AND'], 'AND'); } else { /* If we are here, and we have a ['field'] then we * must either do the 'AND' or the 'OR' search. */ if (isset($vals['field'])) { $ids[] = $this->_selectEntries($vals); } else { foreach ($vals as $test) { if (!empty($test['OR'])) { $ids[] = $this->_doSearch($test['OR'], 'OR'); } elseif (!empty($test['AND'])) { $ids[] = $this->_doSearch($test['AND'], 'AND'); } else { $ids[] = $this->_doSearch(array($test), $glue); } } } } } if ($glue == 'AND') { $ids = $this->_getAND($ids); } elseif ($glue == 'OR') { $ids = $this->_removeDuplicated($ids); } return $ids; } /** * Applies one filter criterium to a list of entries * * @param $test Test criterium * * @return array Array containing the ids of the selected entries */ protected function _selectEntries($test) { $ids = array(); if (!isset($test['field'])) { Horde::log('Search field not set. Returning all entries.', 'DEBUG'); foreach ($this->_contacts_cache as $entry) { $ids[] = $entry['uid']; } } else { $field = $test['field']; $value = isset($test['test']) ? $test['test'] : ''; // Special emails hack if ($field == 'email') { $field = 'emails'; $test['op'] = 'LIKE'; $test['begin'] = false; } if (!isset($test['op']) || $test['op'] == '=') { foreach ($this->_contacts_cache as $entry) { if (isset($entry[$field]) && $entry[$field] == $value) { $ids[] = $entry['uid']; } } } else { // 'op' is LIKE foreach ($this->_contacts_cache as $entry) { if (empty($value) || (isset($entry[$field]) && ((empty($test['begin']) && stripos($entry[$field], $value) !== false) || (!empty($test['begin']) && stripos($entry[$field], $value) === 0)))) { $ids[] = $entry['uid']; } } } } return $ids; } /** * Returns only those names that are duplicated in $ids * * @param array $ids A nested array of arrays containing names * * @return array Array containing the 'AND' of all arrays in $ids */ protected function _getAND($ids) { $matched = $results = array(); /* If there is only 1 array, simply return it. */ if (count($ids) < 2) { return $ids[0]; } for ($i = 0; $i < count($ids); ++$i) { if (is_array($ids[$i])) { $results = array_merge($results, $ids[$i]); } } $search = array_count_values($results); foreach ($search as $key => $value) { if ($value == count($ids)) { $matched[] = $key; } } return $matched; } /** * Returns an array with all duplicate names removed. * * @param array $ids Nested array of arrays containing names. * * @return array Array containg the 'OR' of all arrays in $ids. */ protected function _removeDuplicated($ids) { $unames = array(); for ($i = 0; $i < count($ids); ++$i) { if (is_array($ids[$i])) { $unames = array_merge($unames, $ids[$i]); } } return array_unique($unames); } /** * Reads the given data from the address book and returns the results. * * @param string $key The primary key field to use. * @param mixed $ids The ids of the contacts to load. * @param string $owner Only return contacts owned by this user. * @param array $fields List of fields to return. * @param array $blobFields Array of fields containing binary data. * @param array $dateFields Array of fields containing date data. * @since 4.2.0 * * @return array Hash containing the search results. * @throws Turba_Exception */ protected function _read($key, $ids, $owner, array $fields, array $blobFields = array(), array $dateFields = array()) { $this->connect(); $results = array(); if (!is_array($ids)) { $ids = array($ids); } $count = count($fields); foreach ($ids as $id) { if (!isset($this->_contacts_cache[$id])) { continue; } $object = $this->_contacts_cache[$id]; if (!isset($object['__type']) || $object['__type'] == 'Object') { if ($count) { $result = array(); foreach ($fields as $field) { if (isset($object[$field])) { $result[$field] = $object[$field]; } } $results[] = $result; } else { $results[] = $object; } } else { $member_ids = array(); if (isset($object['member'])) { foreach ($object['member'] as $member) { if (isset($member['uid'])) { $id = Horde_Url::uriB64Encode($member['uid']); $found = false; try { $this->getObject($id); $found = true; } catch (Horde_Exception_NotFound $e) { foreach (Turba::listShares() as $share) { $driver = $GLOBALS['injector'] ->getInstance('Turba_Factory_Driver') ->create($share->getName()); try { $driver->getObject($id); $id = $share->getName() . ':' . $id; $found = true; break; } catch (Horde_Exception_NotFound $e) { continue; } } } if ($found) { $member_ids[] = $id; } continue; } $display_name = $member['display-name']; $smtp_address = $member['smtp-address']; $criteria = array( 'AND' => array( array( 'field' => 'full-name', 'op' => 'LIKE', 'test' => $display_name, 'begin' => false, ), array( 'field' => 'emails', 'op' => 'LIKE', 'test' => $smtp_address, 'begin' => false, ), ), ); $fields = array('uid'); // we expect only one result here!!! $contacts = $this->_search($criteria, $fields); // and drop everything else except the first search // result $member_ids[] = $contacts[0]['__key']; } $object['__members'] = serialize($member_ids); unset($object['member']); } $results[] = $object; } } if (!$results) { throw new Horde_Exception_NotFound(); } return $results; } /** * Adds the specified contact to the addressbook. * * @param array $attributes The attribute values of the contact. * @param array $blob_fields Fields that represent binary data. * @param array $date_fields Fields that represent dates. @since 4.2.0 * * @throws Turba_Exception */ protected function _add(array $attributes, array $blob_fields = array(), array $date_fields = array()) { $this->connect(); $this->_store($attributes); } protected function _canAdd() { return true; } /** * Removes the specified object from the Kolab message store. */ protected function _delete($object_key, $object_id) { $this->connect(); if ($object_key == 'uid') { $object_id = Horde_Url::uriB64Encode($object_id); } elseif ($object_key != '__key') { throw new Turba_Exception(sprintf('Key for deleting must be a UID or ID not %s!', $object_key)); } if (!isset($this->_contacts_cache[$object_id])) { throw new Turba_Exception(sprintf(_("Object with UID %s does not exist!"), $object_id)); } $data = isset($this->_contacts_cache[$object_id]['__type']) && $this->_contacts_cache[$object_id]['__type'] == 'Group' ? $this->_getListData() : $this->_getData(); $result = $data->delete($this->_contacts_cache[$object_id]['uid']); /* Invalidate cache. */ $this->_connected = false; return $result; } /** * Deletes all contacts from a specific address book. * * @param string $sourceName The source to remove all contacts from. * * @return array An array of UIDs * @throws Turba_Exception */ protected function _deleteAll($sourceName = null) { $this->connect(); $uids = array_map(array('Horde_Url', 'uriB64Decode'), array_keys($this->_contacts_cache)); /* Delete contacts */ $this->_getData()->deleteAll(); $this->_getListData()->deleteAll(); return $uids; } /** * Saves the specified object in the SQL database. * * @param Turba_Object $object The object to save * * @return string The object id, possibly updated. * @throws Turba_Exception */ protected function _save(Turba_Object $object) { $this->connect(); return $this->_store($this->toDriverKeys($object->getAttributes()), $object->getValue('__uid')); } /** * Stores an object in the Kolab message store. * * TODO * * @return string The object id, possibly updated. * @throws Turba_Exception */ protected function _store($attributes, $object_id = null) { if (isset($attributes['__type']) && $attributes['__type'] == 'Group') { $this->_convertMembers($attributes); $data = $this->_getListData(); } else { if (isset($attributes['photo']) && isset($attributes['phototype'])) { $filename = 'photo.' . Horde_Mime_Magic::mimeToExt($attributes['phototype']); $attributes['_attachments'][$filename] = array( 'type' => $attributes['phototype'], 'content' => Horde_Stream_Wrapper_String::getStream($attributes['photo']) ); $attributes['picture'] = $filename; unset($attributes['photo'], $attributes['phototype']); } // EAS sets the date fields to '' instead of null -> fix it up $fix_date_fields = array('birthday', 'anniversary'); foreach ($fix_date_fields as $fix_date) { if (empty($attributes[$fix_date])) { unset($attributes[$fix_date]); } } $data = $this->_getData(); } if ($object_id === null) { $object_id = $data->create($attributes); } else { $data->modify($attributes); } /* Invalidate cache. */ $this->_connected = false; return Horde_Url::uriB64Encode($object_id); } /** * TODO */ protected function _convertMembers(&$attributes) { if (isset($attributes['__members'])) { $member_ids = unserialize($attributes['__members']); $attributes['member'] = array(); foreach ($member_ids as $member_id) { $source_id = null; if (strpos($member_id, ':')) { list($source_id, $member_id) = explode(':', $member_id, 2); } $mail = array('uid' => Horde_Url::uriB64Decode($member_id)); $member = null; if ($source_id) { try { $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($source_id); try { $member = $driver->getObject($member_id); $name = $member->getValue('name'); if (!empty($name)) { $mail['display-name'] = $name; } $emails = $member->getValue('emails'); if ($emails) { $emails = explode(',', $emails); $mail['smtp-address'] = trim($emails[0]); if (!isset($mail['display-name'])) { $mail['display-name'] = $mail['smtp-address']; } } } catch (Horde_Exception_NotFound $e) { } } catch (Turba_Exception $e) { } } elseif (isset($this->_contacts_cache[$member_id])) { $member = $this->_contacts_cache[$member_id]; if (!empty($member['full-name'])) { $mail['display-name'] = $member['full-name']; } if (!empty($member['emails'])) { $emails = explode(',', $member['emails']); $mail['smtp-address'] = trim($emails[0]); if (!isset($mail['display-name'])) { $mail['display-name'] = $mail['smtp-address']; } } } $attributes['member'][] = $mail; } unset($attributes['__members']); } } /** * Create an object key for a new object. * * @param array $attributes The attributes (in driver keys) of the * object being added. * * @return string A unique ID for the new object. */ protected function _makeKey(array $attributes) { return Horde_Url::uriB64Encode( isset($attributes['uid']) ? $attributes['uid'] : $this->_generateUid()); } /** * Creates an object UID for a new object. * * @return string A unique ID for the new object. */ protected function _makeUid() { return $this->_generateUid(); } /** * Create an object key for a new object. * * @return string A unique ID for the new object. */ protected function _generateUid() { return $this->_getData()->generateUID(); } /** * Creates a new Horde_Share for this source type. * * @param string $share_name The share name * @param array $params The params for the share. * * @return Horde_Share The share object. */ public function createShare($share_name, array $params) { if (!isset($params['name'])) { $params['name'] = _('Contacts'); } if (!empty($params['params']['default'])) { $params['default'] = true; unset($params['params']['default']); } return Turba::createShare($share_name, $params); } /** * Check if the passed in share is the default share for this source. * * @param Horde_Share_Object $share The share object. * @param array $srcconfig The cfgSource entry for the share. * * @return boolean TODO */ public function checkDefaultShare(Horde_Share_Object $share, array $srcconfig) { $params = @unserialize($share->get('params')); return isset($params['default']) ? $params['default'] : false; } /** * Runs any actions after setting a new default notepad. * * @param string $share The default share ID. */ public function setDefaultShare($share) { $addressbooks = $GLOBALS['injector']->getInstance('Turba_Shares') ->listShares( $GLOBALS['registry']->getAuth(), array('perm' => Horde_Perms::SHOW, 'attributes' => $GLOBALS['registry']->getAuth())); foreach ($addressbooks as $id => $addressbook) { if ($id == $share) { $addressbook->set('default', true); $addressbook->save(); break; } } } }