894 lines
30 KiB
PHP
894 lines
30 KiB
PHP
<?php
|
|
/**
|
|
* Horde_Ldap_Entry represents an LDAP entry.
|
|
*
|
|
* Copyright 2003-2007 Tarjej Huse, Jan Wagner, Benedikt Hallinger
|
|
* Copyright 2009-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* @package Ldap
|
|
* @author Jan Wagner <wagner@netsols.de>
|
|
* @author Tarjej Huse <tarjei@bergfald.no>
|
|
* @author Benedikt Hallinger <beni@php.net>
|
|
* @author Ben Klang <ben@alkaloid.net>
|
|
* @author Jan Schneider <jan@horde.org>
|
|
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL-3.0
|
|
*/
|
|
class Horde_Ldap_Entry
|
|
{
|
|
/**
|
|
* Entry resource identifier.
|
|
*
|
|
* @var resource
|
|
*/
|
|
protected $_entry;
|
|
|
|
/**
|
|
* LDAP resource identifier.
|
|
*
|
|
* @var resource
|
|
*/
|
|
protected $_link;
|
|
|
|
/**
|
|
* Horde_Ldap object.
|
|
*
|
|
* This object will be used for updating and schema checking.
|
|
*
|
|
* @var Horde_Ldap
|
|
*/
|
|
protected $_ldap;
|
|
|
|
/**
|
|
* Distinguished name of the entry.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_dn;
|
|
|
|
/**
|
|
* Attributes.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_attributes = array();
|
|
|
|
/**
|
|
* Original attributes before any modification.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_original = array();
|
|
|
|
/**
|
|
* Map of attribute names.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_map = array();
|
|
|
|
/**
|
|
* Is this a new entry?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_new = true;
|
|
|
|
/**
|
|
* New distinguished name.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_newdn;
|
|
|
|
/**
|
|
* Shall the entry be deleted?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_delete = false;
|
|
|
|
/**
|
|
* Map with changes to the entry.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_changes = array('add' => array(),
|
|
'delete' => array(),
|
|
'replace' => array());
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* Sets up the distinguished name and the entries attributes.
|
|
*
|
|
* Use {@link Horde_Ldap_Entry::createFresh()} or {@link
|
|
* Horde_Ldap_Entry::createConnected()} to create Horde_Ldap_Entry objects.
|
|
*
|
|
* @param Horde_Ldap|resource|array $ldap Horde_Ldap object, LDAP
|
|
* connection resource or
|
|
* array of attributes.
|
|
* @param string|resource $entry Either a DN or a LDAP entry
|
|
* resource.
|
|
*/
|
|
protected function __construct($ldap, $entry = null)
|
|
{
|
|
/* Set up entry resource or DN. */
|
|
if (is_resource($entry)) {
|
|
$this->_entry = $entry;
|
|
} else {
|
|
$this->_dn = $entry;
|
|
}
|
|
|
|
/* Set up LDAP link. */
|
|
if ($ldap instanceof Horde_Ldap) {
|
|
$this->_ldap = $ldap;
|
|
$this->_link = $ldap->getLink();
|
|
} elseif (is_resource($ldap)) {
|
|
$this->_link = $ldap;
|
|
} elseif (is_array($ldap)) {
|
|
/* Special case: here $ldap is an array of attributes, this means,
|
|
* we have no link. This is a "virtual" entry. We just set up the
|
|
* attributes so one can work with the object as expected, but an
|
|
* update() fails unless setLDAP() is called. */
|
|
$this->_loadAttributes($ldap);
|
|
}
|
|
|
|
/* If this is an entry existing in the directory, then set up as old
|
|
* and fetch attributes. */
|
|
if (is_resource($this->_entry) && is_resource($this->_link)) {
|
|
$this->_new = false;
|
|
$this->_dn = @ldap_get_dn($this->_link, $this->_entry);
|
|
/* Fetch attributes from server. */
|
|
$this->_loadAttributes();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a fresh entry that may be added to the directory later.
|
|
*
|
|
* You should put a 'objectClass' attribute into the $attrs so the
|
|
* directory server knows which object you want to create. However, you may
|
|
* omit this in case you don't want to add this entry to a directory
|
|
* server.
|
|
*
|
|
* The attributes parameter is as following:
|
|
* <code>
|
|
* $attrs = array('attribute1' => array('value1', 'value2'),
|
|
* 'attribute2' => 'single value');
|
|
* </code>
|
|
*
|
|
* @param string $dn DN of the entry.
|
|
* @param array $attrs Attributes of the entry.
|
|
*
|
|
* @return Horde_Ldap_Entry
|
|
* @throws Horde_Ldap_Exception
|
|
*/
|
|
public static function createFresh($dn, array $attrs = array())
|
|
{
|
|
return new Horde_Ldap_Entry($attrs, $dn);
|
|
}
|
|
|
|
/**
|
|
* Creates an entry object out of an LDAP entry resource.
|
|
*
|
|
* Use this method, if you want to initialize an entry object that is
|
|
* already present in some directory and that you have read manually.
|
|
*
|
|
* @param Horde_Ldap $ldap Horde_Ldap object.
|
|
* @param resource $entry PHP LDAP entry resource.
|
|
*
|
|
* @return Horde_Ldap_Entry
|
|
* @throws Horde_Ldap_Exception
|
|
*/
|
|
public static function createConnected(Horde_Ldap $ldap, $entry)
|
|
{
|
|
if (!is_resource($entry)) {
|
|
throw new Horde_Ldap_Exception('Unable to create connected entry: Parameter $entry needs to be a ldap entry resource!');
|
|
}
|
|
|
|
return new Horde_Ldap_Entry($ldap, $entry);
|
|
}
|
|
|
|
/**
|
|
* Creates an entry object that is considered to exist already.
|
|
*
|
|
* Use this method, if you want to modify an already existing entry without
|
|
* fetching it first. In most cases however, it is better to fetch the
|
|
* entry via Horde_Ldap::getEntry().
|
|
*
|
|
* You should take care if you construct entries manually with this because
|
|
* you may get weird synchronisation problems. The attributes and values
|
|
* as well as the entry itself are considered existent which may produce
|
|
* errors if you try to modify an entry which doesn't really exist or if
|
|
* you try to overwrite some attribute with an value already present.
|
|
*
|
|
* The attributes parameter is as following:
|
|
* <code>
|
|
* $attrs = array('attribute1' => array('value1', 'value2'),
|
|
* 'attribute2' => 'single value');
|
|
* </code>
|
|
*
|
|
* @param string $dn DN of the entry.
|
|
* @param array $attrs Attributes of the entry.
|
|
*
|
|
* @return Horde_Ldap_Entry
|
|
* @throws Horde_Ldap_Exception
|
|
*/
|
|
public static function createExisting($dn, array $attrs = array())
|
|
{
|
|
$entry = self::createFresh($dn, $attrs);
|
|
$entry->markAsNew(false);
|
|
return $entry;
|
|
}
|
|
|
|
/**
|
|
* Returns or sets the distinguished name of the entry.
|
|
*
|
|
* If called without an argument the current (or the new DN if set) DN gets
|
|
* returned.
|
|
*
|
|
* If you provide an DN, this entry is moved to the new location specified
|
|
* if a DN existed.
|
|
*
|
|
* If the DN was not set, the DN gets initialized. Call {@link update()} to
|
|
* actually create the new entry in the directory.
|
|
*
|
|
* To fetch the current active DN after setting a new DN but before an
|
|
* update(), you can use {@link currentDN()} to retrieve the DN that is
|
|
* currently active.
|
|
*
|
|
* @todo expect utf-8 data.
|
|
* Please note that special characters (eg german umlauts) should be encoded using utf8_encode().
|
|
* You may use {@link Horde_Ldap_Util::canonicalDN()} for properly encoding of the DN.
|
|
*
|
|
* @param string $dn New distinguished name.
|
|
*
|
|
* @return string Distinguished name.
|
|
*/
|
|
public function dn($dn = null)
|
|
{
|
|
if (!is_null($dn)) {
|
|
if (is_null($this->_dn)) {
|
|
$this->_dn = $dn;
|
|
} else {
|
|
$this->_newdn = $dn;
|
|
}
|
|
return $dn;
|
|
}
|
|
return isset($this->_newdn) ? $this->_newdn : $this->currentDN();
|
|
}
|
|
|
|
/**
|
|
* Sets the internal attributes array.
|
|
*
|
|
* This method fetches the values for the attributes from the server. The
|
|
* attribute syntax will be checked so binary attributes will be returned
|
|
* as binary values.
|
|
*
|
|
* Attributes may be passed directly via the $attributes parameter to setup
|
|
* this entry manually. This overrides attribute fetching from the server.
|
|
*
|
|
* @param array $attributes Attributes to set for this entry.
|
|
*/
|
|
protected function _loadAttributes(array $attributes = null)
|
|
{
|
|
/* Fetch attributes from the server. */
|
|
if (is_null($attributes) &&
|
|
is_resource($this->_entry) &&
|
|
is_resource($this->_link)) {
|
|
/* Fetch schema. */
|
|
if ($this->_ldap instanceof Horde_Ldap) {
|
|
try {
|
|
$schema = $this->_ldap->schema();
|
|
} catch (Horde_Ldap_Exception $e) {
|
|
$schema = null;
|
|
}
|
|
}
|
|
|
|
/* Fetch attributes. */
|
|
$attributes = array();
|
|
for ($attr = @ldap_first_attribute($this->_link, $this->_entry);
|
|
$attr;
|
|
$attr = @ldap_next_attribute($this->_link, $this->_entry)) {
|
|
/* Standard function to fetch value. */
|
|
$func = 'ldap_get_values';
|
|
|
|
/* Try to get binary values as binary data. */
|
|
if ($schema instanceof Horde_Ldap_Schema &&
|
|
$schema->isBinary($attr)) {
|
|
$func = 'ldap_get_values_len';
|
|
}
|
|
|
|
/* Fetch attribute value (needs error checking?) . */
|
|
$attributes[$attr] = $func($this->_link, $this->_entry, $attr);
|
|
}
|
|
}
|
|
|
|
/* Set attribute data directly, if passed. */
|
|
if (is_array($attributes) && count($attributes) > 0) {
|
|
if (isset($attributes['count']) &&
|
|
is_numeric($attributes['count'])) {
|
|
unset($attributes['count']);
|
|
}
|
|
foreach ($attributes as $k => $v) {
|
|
/* Attribute names should not be numeric. */
|
|
if (is_numeric($k)) {
|
|
continue;
|
|
}
|
|
|
|
/* Map generic attribute name to real one. */
|
|
$this->_map[Horde_String::lower($k)] = $k;
|
|
|
|
/* Attribute values should be in an array. */
|
|
if (false == is_array($v)) {
|
|
$v = array($v);
|
|
}
|
|
|
|
/* Remove the value count (comes from LDAP server). */
|
|
if (isset($v['count'])) {
|
|
unset($v['count']);
|
|
}
|
|
$this->_attributes[$k] = $v;
|
|
}
|
|
}
|
|
|
|
/* Save a copy for later use. */
|
|
$this->_original = $this->_attributes;
|
|
}
|
|
|
|
/**
|
|
* Returns the values of all attributes in a hash.
|
|
*
|
|
* The returned hash has the form
|
|
* <code>
|
|
* array('attributename' => 'single value',
|
|
* 'attributename' => array('value1', value2', value3'))
|
|
* </code>
|
|
*
|
|
* @return array Hash of all attributes with their values.
|
|
* @throws Horde_Ldap_Exception
|
|
*/
|
|
public function getValues()
|
|
{
|
|
$attrs = array();
|
|
foreach (array_keys($this->_attributes) as $attr) {
|
|
$attrs[$attr] = $this->getValue($attr);
|
|
}
|
|
return $attrs;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of a specific attribute.
|
|
*
|
|
* The first parameter is the name of the attribute.
|
|
*
|
|
* The second parameter influences the way the value is returned:
|
|
* - 'single': only the first value is returned as string.
|
|
* - 'all': all values are returned in an array.
|
|
* In all other cases an attribute value with a single value is returned as
|
|
* string, if it has multiple values it is returned as an array.
|
|
*
|
|
* @param string $attr Attribute name.
|
|
* @param string $option Option.
|
|
*
|
|
* @return string|array Attribute value(s).
|
|
* @throws Horde_Ldap_Exception
|
|
*/
|
|
public function getValue($attr, $option = null)
|
|
{
|
|
$attr = $this->_getAttrName($attr);
|
|
|
|
if (!array_key_exists($attr, $this->_attributes)) {
|
|
throw new Horde_Ldap_Exception('Unknown attribute (' . $attr . ') requested');
|
|
}
|
|
|
|
$value = $this->_attributes[$attr];
|
|
|
|
if ($option == 'single' || (count($value) == 1 && $option != 'all')) {
|
|
$value = array_shift($value);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Returns an array of attributes names.
|
|
*
|
|
* @return array Array of attribute names.
|
|
*/
|
|
public function attributes()
|
|
{
|
|
return array_keys($this->_attributes);
|
|
}
|
|
|
|
/**
|
|
* Returns whether an attribute exists or not.
|
|
*
|
|
* @param string $attr Attribute name.
|
|
*
|
|
* @return boolean True if the attribute exists.
|
|
*/
|
|
public function exists($attr)
|
|
{
|
|
$attr = $this->_getAttrName($attr);
|
|
return array_key_exists($attr, $this->_attributes);
|
|
}
|
|
|
|
/**
|
|
* Adds new attributes or a new values to existing attributes.
|
|
*
|
|
* The paramter has to be an array of the form:
|
|
* <code>
|
|
* array('attributename' => 'single value',
|
|
* 'attributename' => array('value1', 'value2'))
|
|
* </code>
|
|
*
|
|
* When the attribute already exists the values will be added, otherwise
|
|
* the attribute will be created. These changes are local to the entry and
|
|
* do not affect the entry on the server until update() is called.
|
|
*
|
|
* You can add values of attributes that you haven't originally selected,
|
|
* but if you do so, {@link getValue()} and {@link getValues()} will only
|
|
* return the values you added, *NOT* all values present on the server. To
|
|
* avoid this, just refetch the entry after calling {@link update()} or
|
|
* select the attribute.
|
|
*
|
|
* @param array $attr Attributes to add.
|
|
*/
|
|
public function add(array $attr = array())
|
|
{
|
|
foreach ($attr as $k => $v) {
|
|
$k = $this->_getAttrName($k);
|
|
if (!is_array($v)) {
|
|
/* Do not add empty values. */
|
|
if ($v == null) {
|
|
continue;
|
|
} else {
|
|
$v = array($v);
|
|
}
|
|
}
|
|
|
|
/* Add new values to existing attribute or add new attribute. */
|
|
if ($this->exists($k)) {
|
|
$this->_attributes[$k] = array_unique(array_merge($this->_attributes[$k], $v));
|
|
} else {
|
|
$this->_map[Horde_String::lower($k)] = $k;
|
|
$this->_attributes[$k] = $v;
|
|
}
|
|
|
|
/* Save changes for update(). */
|
|
if (empty($this->_changes['add'][$k])) {
|
|
$this->_changes['add'][$k] = array();
|
|
}
|
|
$this->_changes['add'][$k] = array_unique(array_merge($this->_changes['add'][$k], $v));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes an attribute, a value or the whole entry.
|
|
*
|
|
* The parameter can be one of the following:
|
|
*
|
|
* - 'attributename': the attribute as a whole will be deleted.
|
|
* - array('attributename1', 'attributename2'): all specified attributes
|
|
* will be deleted.
|
|
* - array('attributename' => 'value'): the specified attribute value will
|
|
* be deleted.
|
|
* - array('attributename' => array('value1', 'value2'): The specified
|
|
* attribute values
|
|
* will be deleted.
|
|
* - null: the whole entry will be deleted.
|
|
*
|
|
* These changes are local to the entry and do not affect the entry on the
|
|
* server until {@link update()} is called.
|
|
*
|
|
* You must select the attribute (at $ldap->search() for example) to be
|
|
* able to delete values of it, Otherwise {@link update()} will silently
|
|
* fail and remove nothing.
|
|
*
|
|
* @param string|array $attr Attributes to delete.
|
|
*/
|
|
public function delete($attr = null)
|
|
{
|
|
if (is_null($attr)) {
|
|
$this->_delete = true;
|
|
return;
|
|
}
|
|
|
|
if (is_string($attr)) {
|
|
$attr = array($attr);
|
|
}
|
|
|
|
/* Make the assumption that attribute names cannot be numeric,
|
|
* therefore this has to be a simple list of attribute names to
|
|
* delete. */
|
|
reset($attr);
|
|
if (is_numeric(key($attr))) {
|
|
foreach ($attr as $name) {
|
|
if (is_array($name)) {
|
|
/* Mixed modes (list mode but specific values given!). */
|
|
$del_attr_name = array_search($name, $attr);
|
|
$this->delete(array($del_attr_name => $name));
|
|
} else {
|
|
/* Mark for update() if this attribute was not marked
|
|
before. */
|
|
$name = $this->_getAttrName($name);
|
|
if ($this->exists($name)) {
|
|
$this->_changes['delete'][$name] = null;
|
|
unset($this->_attributes[$name]);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
/* We have a hash with 'attributename' => 'value to delete'. */
|
|
foreach ($attr as $name => $values) {
|
|
if (is_int($name)) {
|
|
/* Mixed modes and gave us just an attribute name. */
|
|
$this->delete($values);
|
|
} else {
|
|
/* Mark for update() if this attribute was not marked
|
|
* before; this time it must consider the selected values
|
|
* too. */
|
|
$name = $this->_getAttrName($name);
|
|
if ($this->exists($name)) {
|
|
if (!is_array($values)) {
|
|
$values = array($values);
|
|
}
|
|
/* Save values to be deleted. */
|
|
if (empty($this->_changes['delete'][$name])) {
|
|
$this->_changes['delete'][$name] = array();
|
|
}
|
|
$this->_changes['delete'][$name] =
|
|
array_unique(array_merge($this->_changes['delete'][$name], $values));
|
|
foreach ($values as $value) {
|
|
/* Find the key for the value that should be
|
|
* deleted. */
|
|
$key = array_search($value, $this->_attributes[$name]);
|
|
if (false !== $key) {
|
|
/* Delete the value. */
|
|
unset($this->_attributes[$name][$key]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replaces attributes or their values.
|
|
*
|
|
* The parameter has to an array of the following form:
|
|
* <code>
|
|
* array('attributename' => 'single value',
|
|
* 'attribute2name' => array('value1', 'value2'),
|
|
* 'deleteme1' => null,
|
|
* 'deleteme2' => '')
|
|
* </code>
|
|
*
|
|
* If the attribute does not yet exist it will be added instead (see also
|
|
* $force). If the attribue value is null, the attribute will de deleted.
|
|
*
|
|
* These changes are local to the entry and do not affect the entry on the
|
|
* server until {@link update()} is called.
|
|
*
|
|
* In some cases you are not allowed to read the attributes value (for
|
|
* example the ActiveDirectory attribute unicodePwd) but are allowed to
|
|
* replace the value. In this case replace() would assume that the
|
|
* attribute is not in the directory yet and tries to add it which will
|
|
* result in an LDAP_TYPE_OR_VALUE_EXISTS error. To force replace mode
|
|
* instead of add, you can set $force to true.
|
|
*
|
|
* @param array $attr Attributes to replace.
|
|
* @param boolean $force Force replacing mode in case we can't read the
|
|
* attribute value but are allowed to replace it.
|
|
*/
|
|
public function replace(array $attr = array(), $force = false)
|
|
{
|
|
foreach ($attr as $k => $v) {
|
|
$k = $this->_getAttrName($k);
|
|
if (!is_array($v)) {
|
|
/* Delete attributes with empty values; treat integers as
|
|
* string. */
|
|
if (is_int($v)) {
|
|
$v = (string)$v;
|
|
}
|
|
if ($v == null) {
|
|
$this->delete($k);
|
|
continue;
|
|
} else {
|
|
$v = array($v);
|
|
}
|
|
}
|
|
/* Existing attributes will get replaced. */
|
|
if ($this->exists($k) || $force) {
|
|
$this->_changes['replace'][$k] = $v;
|
|
$this->_attributes[$k] = $v;
|
|
} else {
|
|
/* New ones just get added. */
|
|
$this->add(array($k => $v));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the entry on the directory server.
|
|
*
|
|
* This will evaluate all changes made so far and send them to the
|
|
* directory server.
|
|
*
|
|
* If you make changes to objectclasses wich have mandatory attributes set,
|
|
* update() will currently fail. Remove the entry from the server and readd
|
|
* it as new in such cases. This also will deal with problems with setting
|
|
* structural object classes.
|
|
*
|
|
* @todo Entry rename with a DN containing special characters needs testing!
|
|
*
|
|
* @throws Horde_Ldap_Exception
|
|
*/
|
|
public function update()
|
|
{
|
|
/* Ensure we have a valid LDAP object. */
|
|
$ldap = $this->getLDAP();
|
|
|
|
/* Get and check link. */
|
|
$link = $ldap->getLink();
|
|
if (!is_resource($link)) {
|
|
throw new Horde_Ldap_Exception('Could not update entry: internal LDAP link is invalid');
|
|
}
|
|
|
|
/* Delete the entry. */
|
|
if ($this->_delete) {
|
|
return $ldap->delete($this);
|
|
}
|
|
|
|
/* New entry. */
|
|
if ($this->_new) {
|
|
$ldap->add($this);
|
|
$this->_new = false;
|
|
$this->_changes['add'] = array();
|
|
$this->_changes['delete'] = array();
|
|
$this->_changes['replace'] = array();
|
|
$this->_original = $this->_attributes;
|
|
return;
|
|
}
|
|
|
|
/* Rename/move entry. */
|
|
if (!is_null($this->_newdn)) {
|
|
if ($ldap->getVersion() != 3) {
|
|
throw new Horde_Ldap_Exception('Renaming/Moving an entry is only supported in LDAPv3');
|
|
}
|
|
/* Make DN relative to parent (needed for LDAP rename). */
|
|
$parent = Horde_Ldap_Util::explodeDN($this->_newdn, array('casefolding' => 'none', 'reverse' => false, 'onlyvalues' => false));
|
|
$child = array_shift($parent);
|
|
|
|
/* Maybe the DN consist of a multivalued RDN, we must build the DN
|
|
* in this case because the $child RDN is an array. */
|
|
if (is_array($child)) {
|
|
$child = Horde_Ldap_Util::canonicalDN($child);
|
|
}
|
|
$parent = Horde_Ldap_Util::canonicalDN($parent);
|
|
|
|
/* Rename/move. */
|
|
if (!@ldap_rename($link, $this->_dn, $child, $parent, true)) {
|
|
throw new Horde_Ldap_Exception('Entry not renamed: ' . @ldap_error($link), @ldap_errno($link));
|
|
}
|
|
|
|
/* Reflect changes to local copy. */
|
|
$this->_dn = $this->_newdn;
|
|
$this->_newdn = null;
|
|
}
|
|
|
|
/* Carry out modifications to the entry. */
|
|
foreach ($this->_changes['add'] as $attr => $value) {
|
|
/* If attribute exists, add new values. */
|
|
if ($this->exists($attr)) {
|
|
if (!@ldap_mod_add($link, $this->dn(), array($attr => $value))) {
|
|
throw new Horde_Ldap_Exception('Could not add new values to attribute ' . $attr . ': ' . @ldap_error($link), @ldap_errno($link));
|
|
}
|
|
} else {
|
|
/* New attribute. */
|
|
if (!@ldap_modify($link, $this->dn(), array($attr => $value))) {
|
|
throw new Horde_Ldap_Exception('Could not add new attribute ' . $attr . ': ' . @ldap_error($link), @ldap_errno($link));
|
|
}
|
|
}
|
|
unset($this->_changes['add'][$attr]);
|
|
}
|
|
|
|
foreach ($this->_changes['delete'] as $attr => $value) {
|
|
/* In LDAPv3 you need to specify the old values for deleting. */
|
|
if (is_null($value) && $ldap->getVersion() == 3) {
|
|
$value = $this->_original[$attr];
|
|
}
|
|
if (!@ldap_mod_del($link, $this->dn(), array($attr => $value))) {
|
|
throw new Horde_Ldap_Exception('Could not delete attribute ' . $attr . ': ' . @ldap_error($link), @ldap_errno($link));
|
|
}
|
|
unset($this->_changes['delete'][$attr]);
|
|
}
|
|
|
|
foreach ($this->_changes['replace'] as $attr => $value) {
|
|
if (!@ldap_modify($link, $this->dn(), array($attr => $value))) {
|
|
throw new Horde_Ldap_Exception('Could not replace attribute ' . $attr . ' values: ' . @ldap_error($link), @ldap_errno($link));
|
|
}
|
|
unset($this->_changes['replace'][$attr]);
|
|
}
|
|
|
|
/* All went well, so $_attributes (local copy) becomes $_original
|
|
* (server). */
|
|
$this->_original = $this->_attributes;
|
|
}
|
|
|
|
/**
|
|
* Returns the right attribute name.
|
|
*
|
|
* @param string $attr Name of attribute.
|
|
*
|
|
* @return string The right name of the attribute
|
|
*/
|
|
protected function _getAttrName($attr)
|
|
{
|
|
$name = Horde_String::lower($attr);
|
|
return isset($this->_map[$name]) ? $this->_map[$name] : $attr;
|
|
}
|
|
|
|
/**
|
|
* Returns a reference to the LDAP-Object of this entry.
|
|
*
|
|
* @return Horde_Ldap Reference to the Horde_Ldap object (the connection).
|
|
* @throws Horde_Ldap_Exception
|
|
*/
|
|
public function getLDAP()
|
|
{
|
|
if (!($this->_ldap instanceof Horde_Ldap)) {
|
|
throw new Horde_Ldap_Exception('ldap property is not a valid Horde_Ldap object');
|
|
}
|
|
return $this->_ldap;
|
|
}
|
|
|
|
/**
|
|
* Sets a reference to the LDAP object of this entry.
|
|
*
|
|
* After setting a Horde_Ldap object, calling update() will use that object
|
|
* for updating directory contents. Use this to dynamicly switch
|
|
* directories.
|
|
*
|
|
* @param Horde_Ldap $ldap Horde_Ldap object that this entry should be
|
|
* connected to.
|
|
*
|
|
* @throws Horde_Ldap_Exception
|
|
*/
|
|
public function setLDAP(Horde_Ldap $ldap)
|
|
{
|
|
$this->_ldap = $ldap;
|
|
}
|
|
|
|
/**
|
|
* Marks the entry as new or existing.
|
|
*
|
|
* If an entry is marked as new, it will be added to the directory when
|
|
* calling {@link update()}.
|
|
*
|
|
* If the entry is marked as old ($mark = false), then the entry is assumed
|
|
* to be present in the directory server wich results in modification when
|
|
* calling {@link update()}.
|
|
*
|
|
* @param boolean $mark Whether to mark the entry as new.
|
|
*/
|
|
public function markAsNew($mark = true)
|
|
{
|
|
$this->_new = (bool)$mark;
|
|
}
|
|
|
|
/**
|
|
* Applies a regular expression onto a single- or multi-valued attribute
|
|
* (like preg_match()).
|
|
*
|
|
* This method behaves like PHP's preg_match() but with some exception.
|
|
* Since it is possible to have multi valued attributes the $matches
|
|
* array will have a additionally numerical dimension (one for each value):
|
|
* <code>
|
|
* $matches = array(
|
|
* 0 => array (usual preg_match() returned array),
|
|
* 1 => array (usual preg_match() returned array)
|
|
* )
|
|
* </code>
|
|
* $matches will always be initialized to an empty array inside.
|
|
*
|
|
* Usage example:
|
|
* <code>
|
|
* try {
|
|
* if ($entry->pregMatch('/089(\d+)/', 'telephoneNumber', $matches)) {
|
|
* // Match of value 1, content of first bracket
|
|
* echo 'First match: ' . $matches[0][1];
|
|
* } else {
|
|
* echo 'No match found.';
|
|
* }
|
|
* } catch (Horde_Ldap_Exception $e) {
|
|
* echo 'Error: ' . $e->getMessage();
|
|
* }
|
|
* </code>
|
|
*
|
|
* @param string $regex The regular expression.
|
|
* @param string $attr_name The attribute to search in.
|
|
* @param array $matches Array to store matches in.
|
|
*
|
|
* @return boolean True if we had a match in one of the values.
|
|
* @throws Horde_Ldap_Exception
|
|
*/
|
|
public function pregMatch($regex, $attr_name, &$matches = array())
|
|
{
|
|
/* Fetch attribute values. */
|
|
$attr = $this->getValue($attr_name, 'all');
|
|
unset($attr['count']);
|
|
|
|
/* Perform preg_match() on all values. */
|
|
$match = false;
|
|
foreach ($attr as $thisvalue) {
|
|
if (preg_match($regex, $thisvalue, $matches_int)) {
|
|
$match = true;
|
|
$matches[] = $matches_int;
|
|
}
|
|
}
|
|
|
|
return $match;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the entry is considered new (not present in the server).
|
|
*
|
|
* This method doesn't tell you if the entry is really not present on the
|
|
* server. Use {@link Horde_Ldap::exists()} to see if an entry is already
|
|
* there.
|
|
*
|
|
* @return boolean True if this is considered a new entry.
|
|
*/
|
|
public function isNew()
|
|
{
|
|
return $this->_new;
|
|
}
|
|
|
|
/**
|
|
* Is this entry going to be deleted once update() is called?
|
|
*
|
|
* @return boolean True if this entry is going to be deleted.
|
|
*/
|
|
public function willBeDeleted()
|
|
{
|
|
return $this->_delete;
|
|
}
|
|
|
|
/**
|
|
* Is this entry going to be moved once update() is called?
|
|
*
|
|
* @return boolean True if this entry is going to be move.
|
|
*/
|
|
public function willBeMoved()
|
|
{
|
|
return $this->dn() !== $this->currentDN();
|
|
}
|
|
|
|
/**
|
|
* Returns always the original DN.
|
|
*
|
|
* If an entry will be moved but {@link update()} was not called, {@link
|
|
* dn()} will return the new DN. This method however, returns always the
|
|
* current active DN.
|
|
*
|
|
* @return string The current DN
|
|
*/
|
|
public function currentDN()
|
|
{
|
|
return $this->_dn;
|
|
}
|
|
|
|
/**
|
|
* Returns the attribute changes to be carried out once update() is called.
|
|
*
|
|
* @return array The due changes.
|
|
*/
|
|
public function getChanges()
|
|
{
|
|
return $this->_changes;
|
|
}
|
|
}
|