⚝
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
/
Edit File: Imsp.php
<?php /** * Turba directory driver implementation for an IMSP server. * * Copyright 2010-2017 Horde LLC (http://www.horde.org/) * * See the enclosed file LICENSE for license information (ASL). If you did * did not receive this file, see http://www.horde.org/licenses/apache. * * @author Michael Rubinsky <mrubinsk@horde.org> * @category Horde * @license http://www.horde.org/licenses/apache ASL * @package Turba */ class Turba_Driver_Imsp extends Turba_Driver { /** * Horde_Imsp object * * @var Horde_Imsp */ protected $_imsp; /** * The name of the addressbook. * * @var string */ protected $_bookName = ''; /** * Holds if we are authenticated. * * @var boolean */ protected $_authenticated = ''; /** * Holds name of the field indicating an IMSP group. * * @var string */ protected $_groupField = ''; /** * Holds value that $_groupField will have if entry is an IMSP group. * * @var string */ protected $_groupValue = ''; /** * Used to set if the current search is for contacts only. * * @var boolean */ protected $_noGroups = ''; /** * Driver capabilities. * * @var array */ protected $_capabilities = array( 'delete_all' => true, 'delete_addressbook' => true ); /** * Constructs a new Turba imsp driver object. * * @param array $params Hash containing additional configuration * parameters. */ public function __construct($name = '', $params) { parent::__construct($name, $params); $this->params = $params; $this->_groupField = $params['group_id_field']; $this->_groupValue = $params['group_id_value']; $this->_myRights = $params['my_rights']; $this->_perms = $this->_aclToHordePerms($params['my_rights']); $this->_bookName = $this->getContactOwner(); try { $this->_imsp = $GLOBALS['injector'] ->getInstance('Horde_Core_Factory_Imsp') ->create('Book', $this->params); } catch (Horde_Exception $e) { $this->_authenticated = false; throw new Turba_Exception($e); } $this->_authenticated = true; } /** * Returns all entries matching $critera. * * @param array $criteria Array containing the search criteria. * @param array $fields List of fields to return. * * @return array Hash containing the search results. */ protected function _search(array $criteria, array $fields, array $blobFields = array(), $count_only) { $query = $results = array(); if (!$this->_authenticated) { return $query; } /* Get the search criteria. */ if (count($criteria)) { foreach ($criteria as $key => $vals) { $names = (strval($key) == 'OR') ? $this->_doSearch($vals, 'OR') : $this->_doSearch($vals, 'AND'); } } /* Now we have a list of names, get the rest. */ $result = $this->_read('name', $names, null, $fields); if (is_array($result)) { $results = $result; } Horde::log(sprintf('IMSP returned %s results', count($results)), 'DEBUG'); return $count_only ? count($results) : array_values($results); } /** * Reads the given data from the address book and returns the results. * * @param string $key The primary key field to use (always 'name' * for IMSP). * @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. * * @return array Hash containing the search results. * @throws Turba_Exception */ protected function _read($key, $ids, $owner, array $fields, array $blobFields = array()) { $results = array(); if (!$this->_authenticated) { return $results; } $ids = array_values($ids); $idCount = count($ids); $IMSPGroups = $members = $tmembers = array(); for ($i = 0; $i < $idCount; ++$i) { $result = array(); try { $temp = isset($IMSPGroups[$ids[$i]]) ? $IMSPGroups[$ids[$i]] : $this->_imsp->getEntry($this->_bookName, $ids[$i]); } catch (Horde_Imsp_Exception $e) { continue; } $temp['fullname'] = $temp['name']; $isIMSPGroup = false; if (!isset($temp['__owner'])) { $temp['__owner'] = $GLOBALS['registry']->getAuth(); } if ((isset($temp[$this->_groupField])) && ($temp[$this->_groupField] == $this->_groupValue)) { if ($this->_noGroups) { continue; } if (!isset($IMSPGroups[$ids[$i]])) { $IMSPGroups[$ids[$i]] = $temp; } // move group ids to end of list if ($idCount > count($IMSPGroups) && $idCount - count($IMSPGroups) > $i) { $ids[] = $ids[$i]; unset($ids[$i]); $ids = array_values($ids); --$i; continue; } $isIMSPGroup = true; } // Get the group members that might have been added from other // IMSP applications, but only if we need more information than // the group name if ($isIMSPGroup && array_search('__members', $fields) !== false) { if (isset($temp['email'])) { $emailList = $this->_getGroupEmails($temp['email']); $count = count($emailList); for ($j = 0; $j < $count; ++$j) { $needMember = true; foreach ($results as $curResult) { if (!empty($curResult['email']) && strtolower($emailList[$j]) == strtolower(trim($curResult['email']))) { $members[] = $curResult['name']; $needMember = false; } } if ($needMember) { $memberName = $this->_imsp->search ($this->_bookName, array('email' => trim($emailList[$j]))); if (count($memberName)) { $members[] = $memberName[0]; } } } } if (!empty($temp['__members'])) { $tmembers = @unserialize($temp['__members']); } // TODO: Make sure that we are using the correct naming // convention for members regardless of if we are using // shares or not. This is needed to assure groups created // while not using shares won't be lost when transitioning // to shares and visa versa. //$tmembers = $this->_checkMemberFormat($tmembers); $temp['__members'] = serialize($this->_removeDuplicated( array($members, $tmembers))); $temp['__type'] = 'Group'; $temp['email'] = null; $result = $temp; } else { // IMSP contact. $count = count($fields); for ($j = 0; $j < $count; ++$j) { if (isset($temp[$fields[$j]])) { $result[$fields[$j]] = $temp[$fields[$j]]; } } } $results[] = $result; } 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()) { /* We need to map out Turba_Object_Groups back to IMSP groups before * writing out to the server. We need to array_values() it in * case an entry was deleted from the group. */ if ($attributes['__type'] == 'Group') { /* We may have a newly created group. */ $attributes[$this->_groupField] = $this->_groupValue; if (!isset($attributes['__members'])) { $attributes['__members'] = ''; $attributes['email'] = ' '; } $temp = unserialize($attributes['__members']); if (is_array($temp)) { $members = array_values($temp); } else { $members = array(); } // This searches the current IMSP address book to see if // we have a match for this member before adding to email // attribute since IMSP groups in other IMSP aware apps // generally require an existing conact entry in the current // address book for each group member (this is necessary for // those sources that may be used both in AND out of Horde). try { $result = $this->_read('name', $members, null, array('email')); $count = count($result); for ($i = 0; $i < $count; ++$i) { if (isset($result[$i]['email'])) { $contact = sprintf("%s<%s>\n", $members[$i], $result[$i]['email']); $attributes['email'] .= $contact; } } } catch (Turba_Exception $e) {} } unset($attributes['__type'], $attributes['fullname']); if (!$this->params['contact_ownership']) { unset($attributes['__owner']); } return $this->_imsp->addEntry($this->_bookName, $attributes); } /** * TODO */ protected function _canAdd() { return true; } /** * Deletes the specified object from the IMSP server. * * @throws Turba_Exception */ protected function _delete($object_key, $object_id) { try { $this->_imsp->deleteEntry($this->_bookName, $object_id); } catch (Horde_Imsp_Exception $e) { throw new Turba_Exception($e); } } /** * Deletes the address book represented by this driver from the IMSP server. * * @throws Turba_Exception */ protected function _deleteAll() { try { $this->_imsp->deleteAddressbook($this->_bookName); } catch (Horde_Imsp_Exception $e) { throw new Turba_Exception($e); } } /** * Saves the specified object to the IMSP server. * * @param Turba_Object $object The object to save/update. * * @return string The object id, possibly updated. * @throws Turba_Exception */ protected function _save($object) { $object_keys = $this->toDriverKeys(array('__key' => $object->getValue('__key'))); $object_id = reset($object_keys); $object_key = key($object_keys); $attributes = $this->toDriverKeys($object->getAttributes()); /* Check if the key changed, because IMSP will just write out * a new entry without removing the previous one. */ if ($attributes['name'] != $this->_makeKey($attributes)) { $this->_delete($object_key, $attributes['name']); $attributes['name'] = $this->_makeKey($attributes); $object_id = $attributes['name']; } $this->_add($attributes); return $object_id; } /** * 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($attributes) { return $attributes['fullname']; } /** * Parses out $emailText into an array of pure email addresses * suitable for searching the IMSP datastore with. * * @param string $emailText Single string containing email addressses. * * @return array Pure email address. */ protected function _getGroupEmails($emailText) { preg_match_all("(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})", $emailText, $matches); return $matches[0]; } /** * Parses the search criteria, requests the individual searches from the * server and performs any necessary ANDs / ORs on the results. * * @param array $criteria Array containing the search criteria. * @param string $glue Type of search to perform (AND / OR). * * @return array Array containing contact names that match $criteria. */ protected function _doSearch($criteria, $glue) { $results = array(); foreach ($criteria as $vals) { if (!empty($vals['OR'])) { $results[] = $this->_doSearch($vals['OR'], 'OR'); } elseif (!empty($vals['AND'])) { $results[] = $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'])) { $results[] = $this->_sendSearch($vals); } else { foreach ($vals as $test) { if (!empty($test['OR'])) { $results[] = $this->_doSearch($test['OR'], 'OR'); } elseif (!empty($test['AND'])) { $results[] = $this->_doSearch($test['AND'], 'AND'); } else { $results[] = $this->_doSearch(array($test), $glue); } } } } } return ($glue == 'AND') ? $this->_getDuplicated($results) : $this->_removeDuplicated($results); } /** * Sends a search request to the server. * * @param array $criteria Array containing the search critera. * * @return array Array containing a list of names that match the search. */ function _sendSearch($criteria) { $names = ''; $imspSearch = array(); $searchkey = $criteria['field']; $searchval = $criteria['test']; $searchop = $criteria['op']; $hasName = false; $this->_noGroups = false; $cache = $GLOBALS['injector']->getInstance('Horde_Cache'); $key = implode(".", array_merge($criteria, array($this->_bookName))); /* Now make sure we aren't searching on a dynamically created * field. */ switch ($searchkey) { case 'fullname': if (!$hasName) { $searchkey = 'name'; $hasName = true; } else { $searchkey = ''; } break; case '__owner': if (!$this->params['contact_ownership']) { $searchkey = ''; $hasName = true; } break; } /* Are we searching for only Turba_Object_Groups or Turba_Objects? * This is needed so the 'Show Lists' and 'Show Contacts' * links work correctly in Turba. */ if ($searchkey == '__type') { switch ($searchval) { case 'Group': $searchkey = $this->_groupField; $searchval = $this->_groupValue; break; case 'Object': if (!$hasName) { $searchkey = 'name'; $searchval = ''; $hasName = true; } else { $searchkey = ''; } $this->_noGroups = true; break; } } if (!$searchkey == '') { // Check $searchval for content and for strict matches. if (strlen($searchval) > 0) { if ($searchop == 'LIKE') { $searchval = '*' . $searchval . '*'; } } else { $searchval = '*'; } $imspSearch[$searchkey] = $searchval; } if (!count($imspSearch)) { $imspSearch['name'] = '*'; } /* Finally get to the command. Check the cache first, since each * 'Turba' search may consist of a number of identical IMSP * searchaddress calls in order for the AND and OR parts to work * correctly. 15 Second lifetime should be reasonable for this. This * should reduce load on IMSP server somewhat.*/ $results = $cache->get($key, 15); if ($results) { $names = unserialize($results); } if (!$names) { try { $names = $this->_imsp->search($this->_bookName, $imspSearch); $cache->set($key, serialize($names)); return $names; } catch (Horde_Imsp_Exception $e) { $GLOBALS['notification']->push($names, 'horde.error'); } } else { return $names; } } /** * Returns only those names that are duplicated in $names * * @param array $names A nested array of arrays containing names * * @return array Array containing the 'AND' of all arrays in $names */ protected function _getDuplicated($names) { $matched = $results = array(); /* If there is only 1 array, simply return it. */ if (count($names) < 2) { return $names[0]; } for ($i = 0; $i < count($names); ++$i) { if (is_array($names[$i])) { $results = array_merge($results, $names[$i]); } } $search = array_count_values($results); foreach ($search as $key => $value) { if ($value > 1) { $matched[] = $key; } } return $matched; } /** * Returns an array with all duplicate names removed. * * @param array $names Nested array of arrays containing names. * * @return array Array containg the 'OR' of all arrays in $names. */ protected function _removeDuplicated($names) { $unames = array(); for ($i = 0; $i < count($names); ++$i) { if (is_array($names[$i])) { $unames = array_merge($unames, $names[$i]); } } return array_unique($unames); } /** * Checks if the current user has the requested permission * on this source. * * @param integer $perm The permission to check for. * * @return boolean true if user has permission, false otherwise. */ public function hasPermission($perm) { return $this->_perms & $perm; } /** * Converts an acl string to a Horde Permissions bitmask. * * @param string $acl A standard, IMAP style acl string. * * @return integer Horde Permissions bitmask. */ protected function _aclToHordePerms($acl) { $hPerms = 0; if (strpos($acl, 'w') !== false) { $hPerms |= Horde_Perms::EDIT; } if (strpos($acl, 'r') !== false) { $hPerms |= Horde_Perms::READ; } if (strpos($acl, 'd') !== false) { $hPerms |= Horde_Perms::DELETE; } if (strpos($acl, 'l') !== false) { $hPerms |= Horde_Perms::SHOW; } return $hPerms; } /** * Creates a new Horde_Share and creates the address book * on the IMSP server. * * @param array The params for the share. * * @return Horde_Share The share object. * @throws Turba_Exception */ public function createShare($share_id, $params) { $params['params']['name'] = $this->params['username']; if (!isset($params['default']) || $params['default'] !== true) { $params['params']['name'] .= '.' . $params['name']; } $result = Turba::createShare($share_id, $params); try { Horde_Core_Imsp_Utils::createBook($GLOBALS['cfgSources']['imsp'], $params['params']['name']); } catch (Horde_Imsp_Exception $e) { throw new Turba_Exception($e); } return $result; } /** * Helper function to count the occurances of the ':' * delimiter in group * member entries. * * @param string $in The group member entry. * * @return integer The number of ':' in $in. */ protected function _countDelimiters($in) { $cnt = $pos = 0; while (($pos = strpos($in, ':', $pos + 1)) !== false) { ++$cnt; } return $cnt; } /** * Returns the owner for this contact. For an IMSP source, this should be * the name of the address book. * * @return string TODO */ protected function _getContactOwner() { return $this->params['name']; } /** * Check if the passed in share is the default share for this source. * * @see turba/lib/Turba_Driver#checkDefaultShare($share, $srcconfig) * * @return TODO */ public function checkDefaultShare($share, $srcConfig) { $params = @unserialize($share->get('params')); if (!isset($params['default'])) { $params['default'] = ($params['name'] == $srcConfig['params']['username']); $share->set('params', serialize($params)); $share->save(); } return $params['default']; } }
Simpan