From ba939ab2ad8b08b987f99f24dc9adb1cb45535de Mon Sep 17 00:00:00 2001 From: Ben Klang Date: Thu, 17 Dec 2009 22:08:38 -0500 Subject: [PATCH] Fix directory nesting, comment whitespace --- framework/Ldap/lib/Horde/{Ldap => }/Ldap.php | 0 framework/Ldap/lib/Horde/Ldap/Entry.php | 1040 +++++++++++++++++++ framework/Ldap/lib/Horde/Ldap/Exception.php | 4 + .../Ldap/lib/Horde/Ldap/{Ldap => }/Filter.php | 0 framework/Ldap/lib/Horde/Ldap/{Ldap => }/LDIF.php | 0 framework/Ldap/lib/Horde/Ldap/Ldap/Entry.php | 1055 -------------------- .../Ldap/lib/Horde/Ldap/{Ldap => }/RootDSE.php | 0 .../Ldap/lib/Horde/Ldap/{Ldap => }/Schema.php | 0 .../Ldap/{Ldap => }/SchemaCache.interface.php | 0 .../Ldap/lib/Horde/Ldap/{Ldap => }/Search.php | 0 .../Ldap/{Ldap => }/SimpleFileSchemaCache.php | 0 framework/Ldap/lib/Horde/Ldap/{Ldap => }/Util.php | 0 12 files changed, 1044 insertions(+), 1055 deletions(-) rename framework/Ldap/lib/Horde/{Ldap => }/Ldap.php (100%) create mode 100644 framework/Ldap/lib/Horde/Ldap/Entry.php create mode 100644 framework/Ldap/lib/Horde/Ldap/Exception.php rename framework/Ldap/lib/Horde/Ldap/{Ldap => }/Filter.php (100%) rename framework/Ldap/lib/Horde/Ldap/{Ldap => }/LDIF.php (100%) delete mode 100644 framework/Ldap/lib/Horde/Ldap/Ldap/Entry.php rename framework/Ldap/lib/Horde/Ldap/{Ldap => }/RootDSE.php (100%) rename framework/Ldap/lib/Horde/Ldap/{Ldap => }/Schema.php (100%) rename framework/Ldap/lib/Horde/Ldap/{Ldap => }/SchemaCache.interface.php (100%) rename framework/Ldap/lib/Horde/Ldap/{Ldap => }/Search.php (100%) rename framework/Ldap/lib/Horde/Ldap/{Ldap => }/SimpleFileSchemaCache.php (100%) rename framework/Ldap/lib/Horde/Ldap/{Ldap => }/Util.php (100%) diff --git a/framework/Ldap/lib/Horde/Ldap/Ldap.php b/framework/Ldap/lib/Horde/Ldap.php similarity index 100% rename from framework/Ldap/lib/Horde/Ldap/Ldap.php rename to framework/Ldap/lib/Horde/Ldap.php diff --git a/framework/Ldap/lib/Horde/Ldap/Entry.php b/framework/Ldap/lib/Horde/Ldap/Entry.php new file mode 100644 index 000000000..e2d201ea6 --- /dev/null +++ b/framework/Ldap/lib/Horde/Ldap/Entry.php @@ -0,0 +1,1040 @@ + + * @author Tarjej Huse + * @author Benedikt Hallinger + * @author Ben Klang + * @copyright 2009 The Horde Project, Tarjej Huse, Jan Wagner, Benedikt Hallinger + * @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 + */ + +/** + * Includes + */ +#require_once 'PEAR.php'; +#require_once 'Util.php'; + +class Horde_Ldap_Entry +{ + /** + * Entry ressource identifier + * + * @access protected + * @var ressource + */ + protected $_entry = null; + + /** + * LDAP ressource identifier + * + * @access protected + * @var ressource + */ + protected $_link = null; + + /** + * Horde_Ldap object + * + * This object will be used for updating and schema checking + * + * @access protected + * @var object Horde_Ldap + */ + protected $_ldap = null; + + /** + * Distinguished name of the entry + * + * @access protected + * @var string + */ + protected $_dn = null; + + /** + * Attributes + * + * @access protected + * @var array + */ + protected $_attributes = array(); + + /** + * Original attributes before any modification + * + * @access protected + * @var array + */ + protected $_original = array(); + + + /** + * Map of attribute names + * + * @access protected + * @var array + */ + protected $_map = array(); + + + /** + * Is this a new entry? + * + * @access protected + * @var boolean + */ + protected $_new = true; + + /** + * New distinguished name + * + * @access protected + * @var string + */ + protected $_newdn = null; + + /** + * Shall the entry be deleted? + * + * @access protected + * @var boolean + */ + protected $_delete = false; + + /** + * Map with changes to the entry + * + * @access protected + * @var array + */ + protected $_changes = array("add" => array(), + "delete" => array(), + "replace" => array() + ); + /** + * Internal Constructor + * + * Constructor of the entry. Sets up the distinguished name and the entries + * attributes. + * You should not call this method manually! Use {@link Horde_Ldap_Entry::createFresh()} + * or {@link Horde_Ldap_Entry::createConnected()} instead! + * + * @param Horde_Ldap|ressource|array &$ldap Horde_Ldap object, ldap-link ressource or array of attributes + * @param string|ressource $entry Either a DN or a LDAP-Entry ressource + * + * @access protected + * @return none + */ + protected function __construct(&$ldap, $entry = null) + { + $this->PEAR('Horde_Ldap_Error'); + + // 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->setAttributes($ldap); + } + + // if this is an entry existing in the directory, + // then set up as old and fetch attrs + if (is_resource($this->_entry) && is_resource($this->_link)) { + $this->_new = false; + $this->_dn = @ldap_get_dn($this->_link, $this->_entry); + $this->setAttributes(); // fetch attributes from server + } + } + + /** + * Creates a fresh entry that may be added to the directory later on + * + * Use this method, if you want to initialize a fresh entry. + * + * The method should be called statically: $entry = Horde_Ldap_Entry::createFresh(); + * 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: + * + * $attrs = array( 'attribute1' => array('value1', 'value2'), + * 'attribute2' => 'single value' + * ); + * + * + * @param string $dn DN of the Entry + * @param array $attrs Attributes of the entry + * + * @static + * @return Horde_Ldap_Entry|Horde_Ldap_Error + */ + public static function createFresh($dn, $attrs = array()) + { + if (!is_array($attrs)) { + return PEAR::raiseError("Unable to create fresh entry: Parameter \$attrs needs to be an array!"); + } + + $entry = new Horde_Ldap_Entry($attrs, $dn); + return $entry; + } + + /** + * Creates a Horde_Ldap_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. + * + * Please note, that if you want to create an entry object that represents + * some already existing entry, you should use {@link createExisting()}. + * + * The method should be called statically: $entry = Horde_Ldap_Entry::createConnected(); + * + * @param Horde_Ldap $ldap Horde_LDA2 object + * @param resource $entry PHP LDAP entry resource + * + * @static + * @return Horde_Ldap_Entry|Horde_Ldap_Error + */ + public static function createConnected($ldap, $entry) + { + if (!$ldap instanceof Horde_Ldap) { + return PEAR::raiseError("Unable to create connected entry: Parameter \$ldap needs to be a Horde_Ldap object!"); + } + if (!is_resource($entry)) { + return PEAR::raiseError("Unable to create connected entry: Parameter \$entry needs to be a ldap entry resource!"); + } + + $entry = new Horde_Ldap_Entry($ldap, $entry); + return $entry; + } + + /** + * Creates an Horde_Ldap_Entry object that is considered already existing + * + * 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()! + * + * Please note that 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. + * + * This method is equal to calling createFresh() and after that markAsNew(FALSE). + * + * The method should be called statically: $entry = Horde_Ldap_Entry::createExisting(); + * + * The attributes parameter is as following: + * + * $attrs = array( 'attribute1' => array('value1', 'value2'), + * 'attribute2' => 'single value' + * ); + * + * + * @param string $dn DN of the Entry + * @param array $attrs Attributes of the entry + * + * @static + * @return Horde_Ldap_Entry|Horde_Ldap_Error + */ + public static function createExisting($dn, $attrs = array()) + { + if (!is_array($attrs)) { + return PEAR::raiseError("Unable to create entry object: Parameter \$attrs needs to be an array!"); + } + + $entry = Horde_Ldap_Entry::createFresh($dn, $attrs); + if ($entry instanceof Horde_Ldap_Error) { + return $entry; + } else { + $entry->markAsNew(false); + return $entry; + } + } + + /** + * Get or set 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. + * + * Please note that special characters (eg german umlauts) should be encoded using utf8_encode(). + * You may use {@link Horde_Ldap_Util::canonical_dn()} for properly encoding of the DN. + * + * @param string $dn New distinguished name + * + * @access public + * @return string|true Distinguished name (or true if a new DN was provided) + */ + public function dn($dn = null) + { + if (false == is_null($dn)) { + if (is_null($this->_dn)) { + $this->_dn = $dn; + } else { + $this->_newdn = $dn; + } + return true; + } + return (isset($this->_newdn) ? $this->_newdn : $this->currentDN()); + } + + /** + * Renames or moves the entry + * + * This is just a convinience alias to {@link dn()} + * to make your code more meaningful. + * + * @param string $newdn The new DN + * + * @return true + */ + public function move($newdn) + { + return $this->dn($newdn); + } + + /** + * Sets the internal attributes array + * + * This 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 + * + * @access protected + * @return void + */ + protected function setAttributes($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) { + $schema =& $this->_ldap->schema(); + } + // fetch attributes + $attributes = array(); + do { + if (empty($attr)) { + $ber = null; + $attr = @ldap_first_attribute($this->_link, $this->_entry, $ber); + } else { + $attr = @ldap_next_attribute($this->_link, $this->_entry, $ber); + } + if ($attr) { + $func = 'ldap_get_values'; // standard function to fetch value + + // Try to get binary values as binary data + if ($schema instanceof Horde_Ldap_Schema) { + if ($schema->isBinary($attr)) { + $func = 'ldap_get_values_len'; + } + } + // fetch attribute value (needs error checking?) + $attributes[$attr] = $func($this->_link, $this->_entry, $attr); + } + } while ($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[strtolower($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; + } + + /** + * Get the values of all attributes in a hash + * + * The returned hash has the form + * array('attributename' => 'single value', + * 'attributename' => array('value1', value2', value3')) + * + * @access public + * @return array Hash of all attributes with their values + */ + public function getValues() + { + $attrs = array(); + foreach ($this->_attributes as $attr => $value) { + $attrs[$attr] = $this->getValue($attr); + } + return $attrs; + } + + /** + * Get 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 including the value count are returned in an + * array + * 'default': 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 (without value count) + * + * @param string $attr Attribute name + * @param string $option Option + * + * @access public + * @return string|array|PEAR_Error string, array or PEAR_Error + */ + public function getValue($attr, $option = null) + { + $attr = $this->getAttrName($attr); + + if (false == array_key_exists($attr, $this->_attributes)) { + return PEAR::raiseError("Unknown attribute ($attr) requested"); + } + + $value = $this->_attributes[$attr]; + + if ($option == "single" || (count($value) == 1 && $option != 'all')) { + $value = array_shift($value); + } + + return $value; + } + + /** + * Alias function of getValue for perl-ldap interface + * + * @see getValue() + * @return string|array|PEAR_Error + */ + public function get_value() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'getValue' ), $args); + } + + /** + * Returns an array of attributes names + * + * @access public + * @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 + * + * @access public + * @return boolean + */ + public function exists($attr) + { + $attr = $this->getAttrName($attr); + return array_key_exists($attr, $this->_attributes); + } + + /** + * Adds a new attribute or a new value to an existing attribute + * + * The paramter has to be an array of the form: + * array('attributename' => 'single value', + * 'attributename' => array('value1', 'value2)) + * When the attribute already exists the values will be added, else 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. + * + * Note, that you can add values of attributes that you haven't 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 + * + * @access public + * @return true|Horde_Ldap_Error + */ + public function add($attr = array()) + { + if (false == is_array($attr)) { + return PEAR::raiseError("Parameter must be an array"); + } + foreach ($attr as $k => $v) { + $k = $this->getAttrName($k); + if (false == 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[strtolower($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)); + } + $return = true; + return $return; + } + + /** + * Deletes an whole attribute or 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 given attributes will be + * deleted + * array("attributename" => "value") - The value will be deleted + * array("attributename" => array("value1", "value2") - The given values + * will be deleted + * If $attr is null or omitted , then 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. + * + * Please note that 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 (NULL or missing to delete whole entry) + * + * @access public + * @return true + */ + public function delete($attr = null) + { + if (is_null($attr)) { + $this->_delete = true; + return true; + } + 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 + if (is_numeric(key($attr))) { + foreach ($attr as $name) { + if (is_array($name)) { + // someone 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 attr was not marked before + $name = $this->getAttrName($name); + if ($this->exists($name)) { + $this->_changes["delete"][$name] = null; + unset($this->_attributes[$name]); + } + } + } + } else { + // Here we have a hash with "attributename" => "value to delete" + foreach ($attr as $name => $values) { + if (is_int($name)) { + // someone mixed modes and gave us just an attribute name + $this->delete($values); + } else { + // mark for update() if this attr was not marked before; + // this time it must consider the selected values also + $name = $this->getAttrName($name); + if ($this->exists($name)) { + if (false == 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]); + } + } + } + } + } + } + $return = true; + return $return; + } + + /** + * Replaces attributes or its values + * + * The parameter has to an array of the following form: + * array("attributename" => "single value", + * "attribute2name" => array("value1", "value2"), + * "deleteme1" => null, + * "deleteme2" => "") + * 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 bool $force Force replacing mode in case we can't read the attr value but are allowed to replace it + * + * @access public + * @return true|Horde_Ldap_Error + */ + public function replace($attr = array(), $force = false) + { + if (false == is_array($attr)) { + return PEAR::raiseError("Parameter must be an array"); + } + foreach ($attr as $k => $v) { + $k = $this->getAttrName($k); + if (false == is_array($v)) { + // delete attributes with empty values; treat ints as string + if (is_int($v)) { + $v = "$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)); + } + } + $return = true; + return $return; + } + + /** + * Update the entry on the directory server + * + * This will evaluate all changes made so far and send them + * to the directory server. + * Please note, that 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. + * + * @param Horde_Ldap $ldap If passed, a call to setLDAP() is issued prior update, thus switching the LDAP-server. This is for perl-ldap interface compliance + * + * @access public + * @return true|Horde_Ldap_Error + * @todo Entry rename with a DN containing special characters needs testing! + */ + public function update($ldap = null) + { + if ($ldap) { + $msg = $this->setLDAP($ldap); + if (Horde_Ldap::isError($msg)) { + return PEAR::raiseError('You passed an invalid $ldap variable to update()'); + } + } + + // ensure we have a valid LDAP object + $ldap =& $this->getLDAP(); + if (!$ldap instanceof Horde_Ldap) { + return PEAR::raiseError("The entries LDAP object is not valid"); + } + + // Get and check link + $link = $ldap->getLink(); + if (!is_resource($link)) { + return PEAR::raiseError("Could not update entry: internal LDAP link is invalid"); + } + + /* + * Delete the entry + */ + if (true === $this->_delete) { + return $ldap->delete($this); + } + + /* + * New entry + */ + if (true === $this->_new) { + $msg = $ldap->add($this); + if (Horde_Ldap::isError($msg)) { + return $msg; + } + $this->_new = false; + $this->_changes['add'] = array(); + $this->_changes['delete'] = array(); + $this->_changes['replace'] = array(); + $this->_original = $this->_attributes; + + $return = true; + return $return; + } + + /* + * Rename/move entry + */ + if (false == is_null($this->_newdn)) { + if ($ldap->getLDAPVersion() !== 3) { + return PEAR::raiseError("Renaming/Moving an entry is only supported in LDAPv3"); + } + // make dn relative to parent (needed for ldap rename) + $parent = Horde_Ldap_Util::ldap_explode_dn($this->_newdn, array('casefolding' => 'none', 'reverse' => false, 'onlyvalues' => false)); + if (Horde_Ldap::isError($parent)) { + return $parent; + } + $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::canonical_dn($child); + } + $parent = Horde_Ldap_Util::canonical_dn($parent); + + // rename/move + if (false == @ldap_rename($link, $this->_dn, $child, $parent, true)) { + return PEAR::raiseError("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 + */ + // ADD + foreach ($this->_changes["add"] as $attr => $value) { + // if attribute exists, add new values + if ($this->exists($attr)) { + if (false === @ldap_mod_add($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not add new values to attribute $attr: " . + @ldap_error($link), @ldap_errno($link)); + } + } else { + // new attribute + if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not add new attribute $attr: " . + @ldap_error($link), @ldap_errno($link)); + } + } + // all went well here, I guess + unset($this->_changes["add"][$attr]); + } + + // DELETE + foreach ($this->_changes["delete"] as $attr => $value) { + // In LDAPv3 you need to specify the old values for deleting + if (is_null($value) && $ldap->getLDAPVersion() === 3) { + $value = $this->_original[$attr]; + } + if (false === @ldap_mod_del($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not delete attribute $attr: " . + @ldap_error($link), @ldap_errno($link)); + } + unset($this->_changes["delete"][$attr]); + } + + // REPLACE + foreach ($this->_changes["replace"] as $attr => $value) { + if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) { + return PEAR::raiseError("Could not replace attribute $attr values: " . + @ldap_error($link), @ldap_errno($link)); + } + unset($this->_changes["replace"][$attr]); + } + + // all went well, so _original (server) becomes _attributes (local copy) + $this->_original = $this->_attributes; + + $return = true; + return $return; + } + + /** + * Returns the right attribute name + * + * @param string $attr Name of attribute + * + * @access protected + * @return string The right name of the attribute + */ + protected function getAttrName($attr) + { + $name = strtolower($attr); + if (array_key_exists($name, $this->_map)) { + $attr = $this->_map[$name]; + } + return $attr; + } + + /** + * Returns a reference to the LDAP-Object of this entry + * + * @access public + * @return Horde_Ldap|Horde_Ldap_Error Reference to the Horde_Ldap Object (the connection) or Horde_Ldap_Error + */ + public function &getLDAP() + { + if (!$this->_ldap instanceof Horde_Ldap) { + $err = new PEAR_Error('LDAP is not a valid Horde_Ldap object'); + return $err; + } else { + 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 directorys. + * + * @param Horde_Ldap &$ldap Horde_Ldap object that this entry should be connected to + * + * @access public + * @return true|Horde_Ldap_Error + */ + public function setLDAP(&$ldap) + { + if (!$ldap instanceof Horde_Ldap) { + return PEAR::raiseError("LDAP is not a valid Horde_Ldap object"); + } else { + $this->_ldap =& $ldap; + return true; + } + } + + /** + * Marks the entry as new/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 Value to set, defaults to "true" + * + * @return void + */ + public function markAsNew($mark = true) + { + $this->_new = ($mark)? true : false; + } + + /** + * Applies a regular expression onto a single- or multivalued attribute (like preg_match()) + * + * This method behaves like PHPs preg_match() but with some exceptions. + * If you want to retrieve match information, then you MUST pass the + * $matches parameter via reference! otherwise you will get no matches. + * Since it is possible to have multi valued attributes the $matches + * array will have a additionally numerical dimension (one for each value): + * + * $matches = array( + * 0 => array (usual preg_match() returnarray), + * 1 => array (usual preg_match() returnarray) + * ) + * + * Please note, that $matches will be initialized to an empty array inside. + * + * Usage example: + * + * $result = $entry->preg_match('/089(\d+)/', 'telephoneNumber', &$matches); + * if ( $result === true ){ + * echo "First match: ".$matches[0][1]; // Match of value 1, content of first bracket + * } else { + * if ( Horde_Ldap::isError($result) ) { + * echo "Error: ".$result->getMessage(); + * } else { + * echo "No match found."; + * } + * } + * + * + * Please note that it is important to test for an Horde_Ldap_Error, because objects are + * evaluating to true by default, thus if an error occured, and you only check using "==" then + * you get misleading results. Use the "identical" (===) operator to test for matches to + * avoid this as shown above. + * + * @param string $regex The regular expression + * @param string $attr_name The attribute to search in + * @param array $matches (optional, PASS BY REFERENCE!) Array to store matches in + * + * @return boolean|Horde_Ldap_Error TRUE, if we had a match in one of the values, otherwise false. Horde_Ldap_Error in case something went wrong + */ + public function pregMatch($regex, $attr_name, $matches = array()) + { + $matches = array(); + + // fetch attribute values + $attr = $this->getValue($attr_name, 'all'); + if (Horde_Ldap::isError($attr)) { + return $attr; + } else { + unset($attr['count']); + } + + // perform preg_match() on all values + $match = false; + foreach ($attr as $thisvalue) { + $matches_int = array(); + if (preg_match($regex, $thisvalue, $matches_int)) { + $match = true; + array_push($matches, $matches_int); // store matches in reference + } + } + return $match; + } + + /** + * Alias of {@link pregMatch()} for compatibility to Horde_LDAP 1 + * + * @see pregMatch() + * @return boolean|Horde_Ldap_Error + */ + public function preg_match() + { + $args = func_get_args(); + return call_user_func_array(array( &$this, 'pregMatch' ), $args); + } + + /** + * Tells if the entry is consiedered as new (not present in the server) + * + * Please note, that this doesn't tell you if the entry is present on the server. + * Use {@link Horde_Ldap::dnExists()} to see if an entry is already there. + * + * @return boolean + */ + public function isNew() + { + return $this->_new; + } + + + /** + * Is this entry going to be deleted once update() is called? + * + * @return boolean + */ + public function willBeDeleted() + { + return $this->_delete; + } + + /** + * Is this entry going to be moved once update() is called? + * + * @return boolean + */ + 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 + */ + public function currentDN() + { + return $this->_dn; + } + + /** + * Returns the attribute changes to be carried out once update() is called + * + * @return array + */ + public function getChanges() + { + return $this->_changes; + } +} +?> diff --git a/framework/Ldap/lib/Horde/Ldap/Exception.php b/framework/Ldap/lib/Horde/Ldap/Exception.php new file mode 100644 index 000000000..1b4598c04 --- /dev/null +++ b/framework/Ldap/lib/Horde/Ldap/Exception.php @@ -0,0 +1,4 @@ + -* @author Tarjej Huse -* @author Benedikt Hallinger -* @copyright 2009 Tarjej Huse, Jan Wagner, Benedikt Hallinger -* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 -* @version SVN: $Id: Entry.php 286787 2009-08-04 06:03:12Z beni $ -* @link http://pear.php.net/package/Net_LDAP2/ -*/ - -/** -* Includes -*/ -require_once 'PEAR.php'; -require_once 'Util.php'; - -/** -* Object representation of a directory entry -* -* This class represents a directory entry. You can add, delete, replace -* attributes and their values, rename the entry, delete the entry. -* -* @category Net -* @package Net_LDAP2 -* @author Jan Wagner -* @author Tarjej Huse -* @author Benedikt Hallinger -* @license http://www.gnu.org/copyleft/lesser.html LGPL -* @link http://pear.php.net/package/Net_LDAP2/ -*/ -class Net_LDAP2_Entry extends PEAR -{ - /** - * Entry ressource identifier - * - * @access protected - * @var ressource - */ - protected $_entry = null; - - /** - * LDAP ressource identifier - * - * @access protected - * @var ressource - */ - protected $_link = null; - - /** - * Net_LDAP2 object - * - * This object will be used for updating and schema checking - * - * @access protected - * @var object Net_LDAP2 - */ - protected $_ldap = null; - - /** - * Distinguished name of the entry - * - * @access protected - * @var string - */ - protected $_dn = null; - - /** - * Attributes - * - * @access protected - * @var array - */ - protected $_attributes = array(); - - /** - * Original attributes before any modification - * - * @access protected - * @var array - */ - protected $_original = array(); - - - /** - * Map of attribute names - * - * @access protected - * @var array - */ - protected $_map = array(); - - - /** - * Is this a new entry? - * - * @access protected - * @var boolean - */ - protected $_new = true; - - /** - * New distinguished name - * - * @access protected - * @var string - */ - protected $_newdn = null; - - /** - * Shall the entry be deleted? - * - * @access protected - * @var boolean - */ - protected $_delete = false; - - /** - * Map with changes to the entry - * - * @access protected - * @var array - */ - protected $_changes = array("add" => array(), - "delete" => array(), - "replace" => array() - ); - /** - * Internal Constructor - * - * Constructor of the entry. Sets up the distinguished name and the entries - * attributes. - * You should not call this method manually! Use {@link Net_LDAP2_Entry::createFresh()} - * or {@link Net_LDAP2_Entry::createConnected()} instead! - * - * @param Net_LDAP2|ressource|array &$ldap Net_LDAP2 object, ldap-link ressource or array of attributes - * @param string|ressource $entry Either a DN or a LDAP-Entry ressource - * - * @access protected - * @return none - */ - protected function __construct(&$ldap, $entry = null) - { - $this->PEAR('Net_LDAP2_Error'); - - // set up entry resource or DN - if (is_resource($entry)) { - $this->_entry = &$entry; - } else { - $this->_dn = $entry; - } - - // set up LDAP link - if ($ldap instanceof Net_LDAP2) { - $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->setAttributes($ldap); - } - - // if this is an entry existing in the directory, - // then set up as old and fetch attrs - if (is_resource($this->_entry) && is_resource($this->_link)) { - $this->_new = false; - $this->_dn = @ldap_get_dn($this->_link, $this->_entry); - $this->setAttributes(); // fetch attributes from server - } - } - - /** - * Creates a fresh entry that may be added to the directory later on - * - * Use this method, if you want to initialize a fresh entry. - * - * The method should be called statically: $entry = Net_LDAP2_Entry::createFresh(); - * 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: - * - * $attrs = array( 'attribute1' => array('value1', 'value2'), - * 'attribute2' => 'single value' - * ); - * - * - * @param string $dn DN of the Entry - * @param array $attrs Attributes of the entry - * - * @static - * @return Net_LDAP2_Entry|Net_LDAP2_Error - */ - public static function createFresh($dn, $attrs = array()) - { - if (!is_array($attrs)) { - return PEAR::raiseError("Unable to create fresh entry: Parameter \$attrs needs to be an array!"); - } - - $entry = new Net_LDAP2_Entry($attrs, $dn); - return $entry; - } - - /** - * Creates a Net_LDAP2_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. - * - * Please note, that if you want to create an entry object that represents - * some already existing entry, you should use {@link createExisting()}. - * - * The method should be called statically: $entry = Net_LDAP2_Entry::createConnected(); - * - * @param Net_LDAP2 $ldap Net_LDA2 object - * @param resource $entry PHP LDAP entry resource - * - * @static - * @return Net_LDAP2_Entry|Net_LDAP2_Error - */ - public static function createConnected($ldap, $entry) - { - if (!$ldap instanceof Net_LDAP2) { - return PEAR::raiseError("Unable to create connected entry: Parameter \$ldap needs to be a Net_LDAP2 object!"); - } - if (!is_resource($entry)) { - return PEAR::raiseError("Unable to create connected entry: Parameter \$entry needs to be a ldap entry resource!"); - } - - $entry = new Net_LDAP2_Entry($ldap, $entry); - return $entry; - } - - /** - * Creates an Net_LDAP2_Entry object that is considered already existing - * - * 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 Net_LDAP2->getEntry()! - * - * Please note that 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. - * - * This method is equal to calling createFresh() and after that markAsNew(FALSE). - * - * The method should be called statically: $entry = Net_LDAP2_Entry::createExisting(); - * - * The attributes parameter is as following: - * - * $attrs = array( 'attribute1' => array('value1', 'value2'), - * 'attribute2' => 'single value' - * ); - * - * - * @param string $dn DN of the Entry - * @param array $attrs Attributes of the entry - * - * @static - * @return Net_LDAP2_Entry|Net_LDAP2_Error - */ - public static function createExisting($dn, $attrs = array()) - { - if (!is_array($attrs)) { - return PEAR::raiseError("Unable to create entry object: Parameter \$attrs needs to be an array!"); - } - - $entry = Net_LDAP2_Entry::createFresh($dn, $attrs); - if ($entry instanceof Net_LDAP2_Error) { - return $entry; - } else { - $entry->markAsNew(false); - return $entry; - } - } - - /** - * Get or set 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. - * - * Please note that special characters (eg german umlauts) should be encoded using utf8_encode(). - * You may use {@link Net_LDAP2_Util::canonical_dn()} for properly encoding of the DN. - * - * @param string $dn New distinguished name - * - * @access public - * @return string|true Distinguished name (or true if a new DN was provided) - */ - public function dn($dn = null) - { - if (false == is_null($dn)) { - if (is_null($this->_dn)) { - $this->_dn = $dn; - } else { - $this->_newdn = $dn; - } - return true; - } - return (isset($this->_newdn) ? $this->_newdn : $this->currentDN()); - } - - /** - * Renames or moves the entry - * - * This is just a convinience alias to {@link dn()} - * to make your code more meaningful. - * - * @param string $newdn The new DN - * - * @return true - */ - public function move($newdn) - { - return $this->dn($newdn); - } - - /** - * Sets the internal attributes array - * - * This 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 - * - * @access protected - * @return void - */ - protected function setAttributes($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 Net_LDAP2) { - $schema =& $this->_ldap->schema(); - } - // fetch attributes - $attributes = array(); - do { - if (empty($attr)) { - $ber = null; - $attr = @ldap_first_attribute($this->_link, $this->_entry, $ber); - } else { - $attr = @ldap_next_attribute($this->_link, $this->_entry, $ber); - } - if ($attr) { - $func = 'ldap_get_values'; // standard function to fetch value - - // Try to get binary values as binary data - if ($schema instanceof Net_LDAP2_Schema) { - if ($schema->isBinary($attr)) { - $func = 'ldap_get_values_len'; - } - } - // fetch attribute value (needs error checking?) - $attributes[$attr] = $func($this->_link, $this->_entry, $attr); - } - } while ($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[strtolower($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; - } - - /** - * Get the values of all attributes in a hash - * - * The returned hash has the form - * array('attributename' => 'single value', - * 'attributename' => array('value1', value2', value3')) - * - * @access public - * @return array Hash of all attributes with their values - */ - public function getValues() - { - $attrs = array(); - foreach ($this->_attributes as $attr => $value) { - $attrs[$attr] = $this->getValue($attr); - } - return $attrs; - } - - /** - * Get 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 including the value count are returned in an - * array - * 'default': 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 (without value count) - * - * @param string $attr Attribute name - * @param string $option Option - * - * @access public - * @return string|array|PEAR_Error string, array or PEAR_Error - */ - public function getValue($attr, $option = null) - { - $attr = $this->getAttrName($attr); - - if (false == array_key_exists($attr, $this->_attributes)) { - return PEAR::raiseError("Unknown attribute ($attr) requested"); - } - - $value = $this->_attributes[$attr]; - - if ($option == "single" || (count($value) == 1 && $option != 'all')) { - $value = array_shift($value); - } - - return $value; - } - - /** - * Alias function of getValue for perl-ldap interface - * - * @see getValue() - * @return string|array|PEAR_Error - */ - public function get_value() - { - $args = func_get_args(); - return call_user_func_array(array( &$this, 'getValue' ), $args); - } - - /** - * Returns an array of attributes names - * - * @access public - * @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 - * - * @access public - * @return boolean - */ - public function exists($attr) - { - $attr = $this->getAttrName($attr); - return array_key_exists($attr, $this->_attributes); - } - - /** - * Adds a new attribute or a new value to an existing attribute - * - * The paramter has to be an array of the form: - * array('attributename' => 'single value', - * 'attributename' => array('value1', 'value2)) - * When the attribute already exists the values will be added, else 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. - * - * Note, that you can add values of attributes that you haven't 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 - * - * @access public - * @return true|Net_LDAP2_Error - */ - public function add($attr = array()) - { - if (false == is_array($attr)) { - return PEAR::raiseError("Parameter must be an array"); - } - foreach ($attr as $k => $v) { - $k = $this->getAttrName($k); - if (false == 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[strtolower($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)); - } - $return = true; - return $return; - } - - /** - * Deletes an whole attribute or 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 given attributes will be - * deleted - * array("attributename" => "value") - The value will be deleted - * array("attributename" => array("value1", "value2") - The given values - * will be deleted - * If $attr is null or omitted , then 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. - * - * Please note that 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 (NULL or missing to delete whole entry) - * - * @access public - * @return true - */ - public function delete($attr = null) - { - if (is_null($attr)) { - $this->_delete = true; - return true; - } - 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 - if (is_numeric(key($attr))) { - foreach ($attr as $name) { - if (is_array($name)) { - // someone 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 attr was not marked before - $name = $this->getAttrName($name); - if ($this->exists($name)) { - $this->_changes["delete"][$name] = null; - unset($this->_attributes[$name]); - } - } - } - } else { - // Here we have a hash with "attributename" => "value to delete" - foreach ($attr as $name => $values) { - if (is_int($name)) { - // someone mixed modes and gave us just an attribute name - $this->delete($values); - } else { - // mark for update() if this attr was not marked before; - // this time it must consider the selected values also - $name = $this->getAttrName($name); - if ($this->exists($name)) { - if (false == 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]); - } - } - } - } - } - } - $return = true; - return $return; - } - - /** - * Replaces attributes or its values - * - * The parameter has to an array of the following form: - * array("attributename" => "single value", - * "attribute2name" => array("value1", "value2"), - * "deleteme1" => null, - * "deleteme2" => "") - * 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 bool $force Force replacing mode in case we can't read the attr value but are allowed to replace it - * - * @access public - * @return true|Net_LDAP2_Error - */ - public function replace($attr = array(), $force = false) - { - if (false == is_array($attr)) { - return PEAR::raiseError("Parameter must be an array"); - } - foreach ($attr as $k => $v) { - $k = $this->getAttrName($k); - if (false == is_array($v)) { - // delete attributes with empty values; treat ints as string - if (is_int($v)) { - $v = "$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)); - } - } - $return = true; - return $return; - } - - /** - * Update the entry on the directory server - * - * This will evaluate all changes made so far and send them - * to the directory server. - * Please note, that 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. - * - * @param Net_LDAP2 $ldap If passed, a call to setLDAP() is issued prior update, thus switching the LDAP-server. This is for perl-ldap interface compliance - * - * @access public - * @return true|Net_LDAP2_Error - * @todo Entry rename with a DN containing special characters needs testing! - */ - public function update($ldap = null) - { - if ($ldap) { - $msg = $this->setLDAP($ldap); - if (Net_LDAP2::isError($msg)) { - return PEAR::raiseError('You passed an invalid $ldap variable to update()'); - } - } - - // ensure we have a valid LDAP object - $ldap =& $this->getLDAP(); - if (!$ldap instanceof Net_LDAP2) { - return PEAR::raiseError("The entries LDAP object is not valid"); - } - - // Get and check link - $link = $ldap->getLink(); - if (!is_resource($link)) { - return PEAR::raiseError("Could not update entry: internal LDAP link is invalid"); - } - - /* - * Delete the entry - */ - if (true === $this->_delete) { - return $ldap->delete($this); - } - - /* - * New entry - */ - if (true === $this->_new) { - $msg = $ldap->add($this); - if (Net_LDAP2::isError($msg)) { - return $msg; - } - $this->_new = false; - $this->_changes['add'] = array(); - $this->_changes['delete'] = array(); - $this->_changes['replace'] = array(); - $this->_original = $this->_attributes; - - $return = true; - return $return; - } - - /* - * Rename/move entry - */ - if (false == is_null($this->_newdn)) { - if ($ldap->getLDAPVersion() !== 3) { - return PEAR::raiseError("Renaming/Moving an entry is only supported in LDAPv3"); - } - // make dn relative to parent (needed for ldap rename) - $parent = Net_LDAP2_Util::ldap_explode_dn($this->_newdn, array('casefolding' => 'none', 'reverse' => false, 'onlyvalues' => false)); - if (Net_LDAP2::isError($parent)) { - return $parent; - } - $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 = Net_LDAP2_Util::canonical_dn($child); - } - $parent = Net_LDAP2_Util::canonical_dn($parent); - - // rename/move - if (false == @ldap_rename($link, $this->_dn, $child, $parent, true)) { - return PEAR::raiseError("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 - */ - // ADD - foreach ($this->_changes["add"] as $attr => $value) { - // if attribute exists, add new values - if ($this->exists($attr)) { - if (false === @ldap_mod_add($link, $this->dn(), array($attr => $value))) { - return PEAR::raiseError("Could not add new values to attribute $attr: " . - @ldap_error($link), @ldap_errno($link)); - } - } else { - // new attribute - if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) { - return PEAR::raiseError("Could not add new attribute $attr: " . - @ldap_error($link), @ldap_errno($link)); - } - } - // all went well here, I guess - unset($this->_changes["add"][$attr]); - } - - // DELETE - foreach ($this->_changes["delete"] as $attr => $value) { - // In LDAPv3 you need to specify the old values for deleting - if (is_null($value) && $ldap->getLDAPVersion() === 3) { - $value = $this->_original[$attr]; - } - if (false === @ldap_mod_del($link, $this->dn(), array($attr => $value))) { - return PEAR::raiseError("Could not delete attribute $attr: " . - @ldap_error($link), @ldap_errno($link)); - } - unset($this->_changes["delete"][$attr]); - } - - // REPLACE - foreach ($this->_changes["replace"] as $attr => $value) { - if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) { - return PEAR::raiseError("Could not replace attribute $attr values: " . - @ldap_error($link), @ldap_errno($link)); - } - unset($this->_changes["replace"][$attr]); - } - - // all went well, so _original (server) becomes _attributes (local copy) - $this->_original = $this->_attributes; - - $return = true; - return $return; - } - - /** - * Returns the right attribute name - * - * @param string $attr Name of attribute - * - * @access protected - * @return string The right name of the attribute - */ - protected function getAttrName($attr) - { - $name = strtolower($attr); - if (array_key_exists($name, $this->_map)) { - $attr = $this->_map[$name]; - } - return $attr; - } - - /** - * Returns a reference to the LDAP-Object of this entry - * - * @access public - * @return Net_LDAP2|Net_LDAP2_Error Reference to the Net_LDAP2 Object (the connection) or Net_LDAP2_Error - */ - public function &getLDAP() - { - if (!$this->_ldap instanceof Net_LDAP2) { - $err = new PEAR_Error('LDAP is not a valid Net_LDAP2 object'); - return $err; - } else { - return $this->_ldap; - } - } - - /** - * Sets a reference to the LDAP-Object of this entry - * - * After setting a Net_LDAP2 object, calling update() will use that object for - * updating directory contents. Use this to dynamicly switch directorys. - * - * @param Net_LDAP2 &$ldap Net_LDAP2 object that this entry should be connected to - * - * @access public - * @return true|Net_LDAP2_Error - */ - public function setLDAP(&$ldap) - { - if (!$ldap instanceof Net_LDAP2) { - return PEAR::raiseError("LDAP is not a valid Net_LDAP2 object"); - } else { - $this->_ldap =& $ldap; - return true; - } - } - - /** - * Marks the entry as new/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 Value to set, defaults to "true" - * - * @return void - */ - public function markAsNew($mark = true) - { - $this->_new = ($mark)? true : false; - } - - /** - * Applies a regular expression onto a single- or multivalued attribute (like preg_match()) - * - * This method behaves like PHPs preg_match() but with some exceptions. - * If you want to retrieve match information, then you MUST pass the - * $matches parameter via reference! otherwise you will get no matches. - * Since it is possible to have multi valued attributes the $matches - * array will have a additionally numerical dimension (one for each value): - * - * $matches = array( - * 0 => array (usual preg_match() returnarray), - * 1 => array (usual preg_match() returnarray) - * ) - * - * Please note, that $matches will be initialized to an empty array inside. - * - * Usage example: - * - * $result = $entry->preg_match('/089(\d+)/', 'telephoneNumber', &$matches); - * if ( $result === true ){ - * echo "First match: ".$matches[0][1]; // Match of value 1, content of first bracket - * } else { - * if ( Net_LDAP2::isError($result) ) { - * echo "Error: ".$result->getMessage(); - * } else { - * echo "No match found."; - * } - * } - * - * - * Please note that it is important to test for an Net_LDAP2_Error, because objects are - * evaluating to true by default, thus if an error occured, and you only check using "==" then - * you get misleading results. Use the "identical" (===) operator to test for matches to - * avoid this as shown above. - * - * @param string $regex The regular expression - * @param string $attr_name The attribute to search in - * @param array $matches (optional, PASS BY REFERENCE!) Array to store matches in - * - * @return boolean|Net_LDAP2_Error TRUE, if we had a match in one of the values, otherwise false. Net_LDAP2_Error in case something went wrong - */ - public function pregMatch($regex, $attr_name, $matches = array()) - { - $matches = array(); - - // fetch attribute values - $attr = $this->getValue($attr_name, 'all'); - if (Net_LDAP2::isError($attr)) { - return $attr; - } else { - unset($attr['count']); - } - - // perform preg_match() on all values - $match = false; - foreach ($attr as $thisvalue) { - $matches_int = array(); - if (preg_match($regex, $thisvalue, $matches_int)) { - $match = true; - array_push($matches, $matches_int); // store matches in reference - } - } - return $match; - } - - /** - * Alias of {@link pregMatch()} for compatibility to Net_LDAP 1 - * - * @see pregMatch() - * @return boolean|Net_LDAP2_Error - */ - public function preg_match() - { - $args = func_get_args(); - return call_user_func_array(array( &$this, 'pregMatch' ), $args); - } - - /** - * Tells if the entry is consiedered as new (not present in the server) - * - * Please note, that this doesn't tell you if the entry is present on the server. - * Use {@link Net_LDAP2::dnExists()} to see if an entry is already there. - * - * @return boolean - */ - public function isNew() - { - return $this->_new; - } - - - /** - * Is this entry going to be deleted once update() is called? - * - * @return boolean - */ - public function willBeDeleted() - { - return $this->_delete; - } - - /** - * Is this entry going to be moved once update() is called? - * - * @return boolean - */ - 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 - */ - public function currentDN() - { - return $this->_dn; - } - - /** - * Returns the attribute changes to be carried out once update() is called - * - * @return array - */ - public function getChanges() - { - return $this->_changes; - } -} -?> diff --git a/framework/Ldap/lib/Horde/Ldap/Ldap/RootDSE.php b/framework/Ldap/lib/Horde/Ldap/RootDSE.php similarity index 100% rename from framework/Ldap/lib/Horde/Ldap/Ldap/RootDSE.php rename to framework/Ldap/lib/Horde/Ldap/RootDSE.php diff --git a/framework/Ldap/lib/Horde/Ldap/Ldap/Schema.php b/framework/Ldap/lib/Horde/Ldap/Schema.php similarity index 100% rename from framework/Ldap/lib/Horde/Ldap/Ldap/Schema.php rename to framework/Ldap/lib/Horde/Ldap/Schema.php diff --git a/framework/Ldap/lib/Horde/Ldap/Ldap/SchemaCache.interface.php b/framework/Ldap/lib/Horde/Ldap/SchemaCache.interface.php similarity index 100% rename from framework/Ldap/lib/Horde/Ldap/Ldap/SchemaCache.interface.php rename to framework/Ldap/lib/Horde/Ldap/SchemaCache.interface.php diff --git a/framework/Ldap/lib/Horde/Ldap/Ldap/Search.php b/framework/Ldap/lib/Horde/Ldap/Search.php similarity index 100% rename from framework/Ldap/lib/Horde/Ldap/Ldap/Search.php rename to framework/Ldap/lib/Horde/Ldap/Search.php diff --git a/framework/Ldap/lib/Horde/Ldap/Ldap/SimpleFileSchemaCache.php b/framework/Ldap/lib/Horde/Ldap/SimpleFileSchemaCache.php similarity index 100% rename from framework/Ldap/lib/Horde/Ldap/Ldap/SimpleFileSchemaCache.php rename to framework/Ldap/lib/Horde/Ldap/SimpleFileSchemaCache.php diff --git a/framework/Ldap/lib/Horde/Ldap/Ldap/Util.php b/framework/Ldap/lib/Horde/Ldap/Util.php similarity index 100% rename from framework/Ldap/lib/Horde/Ldap/Ldap/Util.php rename to framework/Ldap/lib/Horde/Ldap/Util.php -- 2.11.0