/** * Writes a DN to the filehandle * * @param string $dn DN to write * * @access private * @return void */ function _writeDN($dn) { // prepare DN if ($this->_options['encode'] == 'base64') { $dn = $this->_convertDN($dn) . PHP_EOL; } elseif ($this->_options['encode'] == 'canonical') { $dn = Net_LDAP_Util::canonical_dn($dn, array('casefold' => 'none')) . PHP_EOL; } else { $dn = $dn . PHP_EOL; } $this->_writeLine($dn, 'Net_LDAP_LDIF error: unable to write DN of entry ' . $this->_entrynum); }
/** * Returns the given DN in a canonical form * * Returns false if DN is not a valid Distinguished Name. * Note: The empty string "" is a valid DN. DN can either be a string or an array * as returned by ldap_explode_dn, which is useful when constructing a DN. * The DN array may have be indexed (each array value is a OCL=VALUE pair) * or associative (array key is OCL and value is VALUE). * * It performs the following operations on the given DN: * - Removes the leading 'OID.' characters if the type is an OID instead of a name. * - Escapes all RFC 2253 special characters (",", "+", """, "\", "<", ">", ";", "#", "=", " "), slashes ("/"), and any other character where the ASCII code is < 32 as \hexpair. * - Converts all leading and trailing spaces in values to be \20. * - If an RDN contains multiple parts, the parts are re-ordered so that the attribute type names are in alphabetical order. * * OPTIONS is a list of name/value pairs, valid options are: * casefold Controls case folding of attribute type names. * Attribute values are not affected by this option. The default is to uppercase. * Valid values are: * lower Lowercase attribute type names. * upper Uppercase attribute type names. This is the default. * none Do not change attribute type names. * [NOT IMPLEMENTED] mbcescape If TRUE, characters that are encoded as a multi-octet UTF-8 sequence will be escaped as \(hexpair){2,*}. * reverse If TRUE, the RDN sequence is reversed. * separator Separator to use between RDNs. Defaults to comma (','). * * @static * @param array|string $dn The DN * @param array $option Options to use * @return false|string The canonical DN * @todo implement option mbcescape */ function canonical_dn($dn, $options = array('casefold' => 'upper')) { if ($dn === '') { return $dn; } // empty DN is valid! // options check if (!isset($options['reverse'])) { $options['reverse'] = false; } else { $options['reverse'] = true; } if (!isset($options['casefold'])) { $options['casefold'] = 'upper'; } if (!isset($options['separator'])) { $options['separator'] = ','; } if (!is_array($dn)) { $dn = explode($options['separator'], $dn); } else { // Is array, check, if the array is indexed or associative $assoc = false; foreach ($dn as $dn_key => $dn_part) { if (!is_int($dn_key)) { $assoc = true; } } // convert to inexed, if associative array detected if ($assoc) { $newdn = array(); foreach ($dn as $dn_key => $dn_part) { if (is_array($dn_part)) { $newdn[] = $dn_part; // copy array as-is, so we can resolve it later } else { $newdn[] = $dn_key . '=' . $dn_part; } } $dn =& $newdn; } } // Escaping and casefolding foreach ($dn as $pos => $dnval) { if (is_array($dnval)) { // subarray detected, this means very surely, that we had // a multivalued dn part, which must be resolved $dnval_new = ''; foreach ($dnval as $subkey => $subval) { // build RDN part if (!is_int($subkey)) { $subval = $subkey . '=' . $subval; } $subval_processed = Net_LDAP_Util::canonical_dn($subval); if (false === $subval_processed) { return false; } $dnval_new .= $subval_processed . '+'; } $dn[$pos] = substr($dnval_new, 0, -1); // store RDN part, strip last plus } else { // try to split multivalued RDNS into array $rdns = Net_LDAP_Util::split_rdn_multival($dnval); if (count($rdns) > 1) { // Multivalued RDN was detected! // The RDN value is expected to be correctly split by split_rdn_multival(). // It's time to sort the RDN and build the DN! $rdn_string = ''; sort($rdns, SORT_STRING); // Sort RDN keys alphabetically foreach ($rdns as $rdn) { $subval_processed = Net_LDAP_Util::canonical_dn($rdn); if (false === $subval_processed) { return false; } $rdn_string .= $subval_processed . '+'; } $dn[$pos] = substr($rdn_string, 0, -1); // store RDN part, strip last plus } else { // no multivalued RDN! $dn_comp = explode('=', $rdns[0]); $ocl = ltrim($dn_comp[0]); // trim left whitespaces 'cause of "cn=foo, l=bar" syntax (whitespace after comma) $val = $dn_comp[1]; // strip OCL., otherwise apply casefolding and escaping if (substr(strtolower($ocl), 0, 4) == 'oid.') { $ocl = substr($ocl, 4); } else { if ($options['casefold'] == 'upper') { $ocl = strtoupper($ocl); } if ($options['casefold'] == 'lower') { $ocl = strtolower($ocl); } $ocl = Net_LDAP_Util::escape_dn_value(array($ocl)); $ocl = $ocl[0]; } // escaping of dn-value $val = Net_LDAP_Util::escape_dn_value(array($val)); $val = $val[0]; $dn[$pos] = $ocl . '=' . $val; } } } if ($options['reverse']) { $dn = array_reverse($dn); } return implode($options['separator'], $dn); }
/** * Tell if a DN does exist in the directory * * @param string $dn The DN of the object to test * * @return boolean|Net_LDAP_Error */ function dnExists($dn) { if (!is_string($dn)) { return PEAR::raiseError('$dn is expected to be a string but is ' . gettype($dn) . ' ' . get_class($dn)); } // make dn relative to parent $base = Net_LDAP_Util::ldap_explode_dn($dn, array('casefold' => 'none', 'reverse' => false, 'onlyvalues' => false)); if (Net_LDAP::isError($base)) { return $base; } $entry_rdn = array_shift($base); if (is_array($entry_rdn)) { // maybe the dn consist of a multivalued RDN, we must build the dn in this case // because the $entry_rdn is an array! $filter_dn = Net_LDAP_Util::canonical_dn($entry_rdn); } $base = Net_LDAP_Util::canonical_dn($base); $result = @ldap_list($this->_link, $base, $entry_rdn, array(), 1, 1); if (@ldap_count_entries($this->_link, $result)) { return true; } if (ldap_errno($this->_link) == 32) { return false; } if (ldap_errno($this->_link) != 0) { return PEAR::raiseError(ldap_error($this->_link), ldap_errno($this->_link)); } return false; }
/** * Update the entry on the directory server * * @access public * @param Net_LDAP $ldap (optional) If you provide a Net_LDAP object, be sure to PASS IT VIA REFERENCE! * @return true|Net_LDAP_Error * @todo Entry rename with a DN containing special characters needs testing! */ function update($ldap = false) { if (!$ldap) { // If object is not provided, then use this entrys ldap object $ldap =& $this->_ldap; } else { if (!is_a($ldap, 'Net_LDAP')) { $ldap = false; // throw error } else { // store the provided ldap object internally, if we haven't got one already if (!$this->_ldap) { $this->_ldap =& $ldap; } } } // ensure we have a valid LDAP object if (!is_a($ldap, 'Net_LDAP')) { return PEAR::raiseError("Need a Net_LDAP object as parameter"); } $link = $ldap->getLink(); /* * Delete the entry */ if (true === $this->_delete) { return $ldap->delete($this); } /* * New entry */ if (true === $this->_new) { $msg = $ldap->add($this); if (Net_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 = Net_LDAP_Util::ldap_explode_dn($this->_newdn, array('casefolding' => 'none', 'reverse' => false, 'onlyvalues' => false)); if (Net_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 = Net_LDAP_Util::canonical_dn($child); } $parent = Net_LDAP_Util::canonical_dn($parent); // rename 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; }
/** * Tell if a dn already exists * * @param string $dn The DN of the object to test * @return boolean */ function dnExists($dn) { // make dn relative to parent $base = Net_LDAP_Util::ldap_explode_dn($this->_newdn, array('casefolding' => 'none', 'reverse' => false, 'onlyvalues' => false)); if (Net_LDAP::isError($base)) { return $base; } $filter = array_shift($base); // 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($filter)) { $filter = Net_LDAP_Util::canonical_dn($filter); } $base = Net_LDAP_Util::canonical_dn($base); $result = @ldap_list($this->_link, $base, $filter, array(), 1, 1); if (ldap_errno($this->_link) == 32) { $return = false; return $return; } if (ldap_errno($this->_link) != 0) { PEAR::raiseError(ldap_error($this->_link), ldap_errno($this->_link)); } if (@ldap_count_entries($this->_link, $result)) { $return = true; return $return; } $return = false; return $return; }