Hү)=¡uiK>grmcHLу\T cMPPdYK-״$gW.a63"HjDءcwPi1X`)/.=Gh{t/Ҷ爬ZuSĜ)(_0a]*a>نdHSfܶ<&B\MY>-E.O4 a"W:FTvSJ(Dux PN$I~-g~ǖ0(9aV+[4R`kF$J ݃#|3zLl96+aQ0 n ݨJ٣hjʚS!{ܝڻWX=˛gmw.R>aSeyaQjl27LnyliG=oy&#mgMDnJP\9`X].۳31Ъ! oN a|HrA$ s+@PBI%-/ Zx͐J-/ W1dջ$0pWކ\->5N[Y n4lKlӃ0Ia`]*E.0h=!XwF/t)]6\k{dX97%q왞uB8 *M6"\En$<^w.}z޿0cN~\̈$:/)=Wf}v'l۽|'/.:o?/>ԆVک>tVR&(+ޅq eS/_ǻ+f2o#>3Yf+ nW|ک>tVR&(Zn*[hNt@eqVq2o#>3H^S/Tnک>tVR&(Ү/dRAj&_ Ұ<2o#>3c/zAAک>tVR&(ҲPjم&_ Ұ<2o#>3d`dحe봿ک>tVR&(Ҟ/\ꌠ%NLу6^q1w2o#>3t--l`ک>tVR&(+;W +)&_ Ұ<2o#>3WaZDГm;cpک>tVR&(/hHΛu[ۼ3ߟunɀI]$H #;K9|j'$7?04$=WTtYWˮUp acI0Au٢ Z?*n\>;PE !RWKom,EǙU**vinf8U׋m%0|2;; @xYż\,E KxcmHpH▾Q CИoԪu1`(BY1U˳Qf KZ qh`?`N+7M*kiM![s2f ]֎u]l œ*V<KӀ{!*9h Qh6x/mʻ\j? fq隢 =rJ@EwlE sU>:F9iݬ]R xhG^E΁D j16>TpX6~1Fy`oaop>#.eʑʖzr{getValue('subschemaSubentry', 'single'); if (!Net_LDAP2::isError($base)) { $dn = $base; } } } // Support for buggy LDAP servers (e.g. Siemens DirX 6.x) that incorrectly // call this entry subSchemaSubentry instead of subschemaSubentry. // Note the correct case/spelling as per RFC 2251. if (is_null($dn)) { // get the subschema entry via root dse $dse = $ldap->rootDSE(array('subSchemaSubentry')); if (false == Net_LDAP2::isError($dse)) { $base = $dse->getValue('subSchemaSubentry', 'single'); if (!Net_LDAP2::isError($base)) { $dn = $base; } } } // Final fallback case where there is no subschemaSubentry attribute // in the root DSE (this is a bug for an LDAP v3 server so report this // to your LDAP vendor if you get this far). if (is_null($dn)) { $dn = 'cn=Subschema'; } // fetch the subschema entry $result = $ldap->search($dn, '(objectClass=*)', array('attributes' => array_values($schema_o->types), 'scope' => 'base')); if (Net_LDAP2::isError($result)) { return PEAR::raiseError('Could not fetch Subschema entry: '.$result->getMessage()); } $entry = $result->shiftEntry(); if (!$entry instanceof Net_LDAP2_Entry) { if ($entry instanceof Net_LDAP2_Error) { return PEAR::raiseError('Could not fetch Subschema entry: '.$entry->getMessage()); } else { return PEAR::raiseError('Could not fetch Subschema entry (search returned '.$result->count().' entries. Check parameter \'basedn\')'); } } $schema_o->parse($entry); return $schema_o; } /** * Return a hash of entries for the given type * * Returns a hash of entry for the givene type. Types may be: * objectclasses, attributes, ditcontentrules, ditstructurerules, matchingrules, * matchingruleuses, nameforms, syntaxes * * @param string $type Type to fetch * * @access public * @return array|Net_LDAP2_Error Array or Net_LDAP2_Error */ public function &getAll($type) { $map = array('objectclasses' => &$this->_objectClasses, 'attributes' => &$this->_attributeTypes, 'ditcontentrules' => &$this->_dITContentRules, 'ditstructurerules' => &$this->_dITStructureRules, 'matchingrules' => &$this->_matchingRules, 'matchingruleuses' => &$this->_matchingRuleUse, 'nameforms' => &$this->_nameForms, 'syntaxes' => &$this->_ldapSyntaxes ); $key = strtolower($type); $ret = ((key_exists($key, $map)) ? $map[$key] : PEAR::raiseError("Unknown type $type")); return $ret; } /** * Return a specific entry * * @param string $type Type of name * @param string $name Name or OID to fetch * * @access public * @return mixed Entry or Net_LDAP2_Error */ public function &get($type, $name) { if ($this->_initialized) { $type = strtolower($type); if (false == key_exists($type, $this->types)) { return PEAR::raiseError("No such type $type"); } $name = strtolower($name); $type_var = &$this->{'_' . $this->types[$type]}; if (key_exists($name, $type_var)) { return $type_var[$name]; } elseif (key_exists($name, $this->_oids) && $this->_oids[$name]['type'] == $type) { return $this->_oids[$name]; } else { return PEAR::raiseError("Could not find $type $name"); } } else { $return = null; return $return; } } /** * Fetches attributes that MAY be present in the given objectclass * * @param string $oc Name or OID of objectclass * * @access public * @return array|Net_LDAP2_Error Array with attributes or Net_LDAP2_Error */ public function may($oc) { return $this->_getAttr($oc, 'may'); } /** * Fetches attributes that MUST be present in the given objectclass * * @param string $oc Name or OID of objectclass * * @access public * @return array|Net_LDAP2_Error Array with attributes or Net_LDAP2_Error */ public function must($oc) { return $this->_getAttr($oc, 'must'); } /** * Fetches the given attribute from the given objectclass * * @param string $oc Name or OID of objectclass * @param string $attr Name of attribute to fetch * * @access protected * @return array|Net_LDAP2_Error The attribute or Net_LDAP2_Error */ protected function _getAttr($oc, $attr) { $oc = strtolower($oc); if (key_exists($oc, $this->_objectClasses) && key_exists($attr, $this->_objectClasses[$oc])) { return $this->_objectClasses[$oc][$attr]; } elseif (key_exists($oc, $this->_oids) && $this->_oids[$oc]['type'] == 'objectclass' && key_exists($attr, $this->_oids[$oc])) { return $this->_oids[$oc][$attr]; } else { return PEAR::raiseError("Could not find $attr attributes for $oc "); } } /** * Returns the name(s) of the immediate superclass(es) * * @param string $oc Name or OID of objectclass * * @access public * @return array|Net_LDAP2_Error Array of names or Net_LDAP2_Error */ public function superclass($oc) { $o = $this->get('objectclass', $oc); if (Net_LDAP2::isError($o)) { return $o; } return (key_exists('sup', $o) ? $o['sup'] : array()); } /** * Parses the schema of the given Subschema entry * * @param Net_LDAP2_Entry &$entry Subschema entry * * @access public * @return void */ public function parse(&$entry) { foreach ($this->types as $type => $attr) { // initialize map type to entry $type_var = '_' . $attr; $this->{$type_var} = array(); // get values for this type if ($entry->exists($attr)) { $values = $entry->getValue($attr); if (is_array($values)) { foreach ($values as $value) { unset($schema_entry); // this was a real mess without it // get the schema entry $schema_entry = $this->_parse_entry($value); // set the type $schema_entry['type'] = $type; // save a ref in $_oids $this->_oids[$schema_entry['oid']] = &$schema_entry; // save refs for all names in type map $names = $schema_entry['aliases']; array_push($names, $schema_entry['name']); foreach ($names as $name) { $this->{$type_var}[strtolower($name)] = &$schema_entry; } } } } } $this->_initialized = true; } /** * Parses an attribute value into a schema entry * * @param string $value Attribute value * * @access protected * @return array|false Schema entry array or false */ protected function &_parse_entry($value) { // tokens that have no value associated $noValue = array('single-value', 'obsolete', 'collective', 'no-user-modification', 'abstract', 'structural', 'auxiliary'); // tokens that can have multiple values $multiValue = array('must', 'may', 'sup'); $schema_entry = array('aliases' => array()); // initilization $tokens = $this->_tokenize($value); // get an array of tokens // remove surrounding brackets if ($tokens[0] == '(') array_shift($tokens); if ($tokens[count($tokens) - 1] == ')') array_pop($tokens); // -1 doesnt work on arrays :-( $schema_entry['oid'] = array_shift($tokens); // first token is the oid // cycle over the tokens until none are left while (count($tokens) > 0) { $token = strtolower(array_shift($tokens)); if (in_array($token, $noValue)) { $schema_entry[$token] = 1; // single value token } else { // this one follows a string or a list if it is multivalued if (($schema_entry[$token] = array_shift($tokens)) == '(') { // this creates the list of values and cycles through the tokens // until the end of the list is reached ')' $schema_entry[$token] = array(); while ($tmp = array_shift($tokens)) { if ($tmp == ')') break; if ($tmp != '$') array_push($schema_entry[$token], $tmp); } } // create a array if the value should be multivalued but was not if (in_array($token, $multiValue) && !is_array($schema_entry[$token])) { $schema_entry[$token] = array($schema_entry[$token]); } } } // get max length from syntax if (key_exists('syntax', $schema_entry)) { if (preg_match('/{(\d+)}/', $schema_entry['syntax'], $matches)) { $schema_entry['max_length'] = $matches[1]; } } // force a name if (empty($schema_entry['name'])) { $schema_entry['name'] = $schema_entry['oid']; } // make one name the default and put the other ones into aliases if (is_array($schema_entry['name'])) { $aliases = $schema_entry['name']; $schema_entry['name'] = array_shift($aliases); $schema_entry['aliases'] = $aliases; } return $schema_entry; } /** * Tokenizes the given value into an array of tokens * * @param string $value String to parse * * @access protected * @return array Array of tokens */ protected function _tokenize($value) { $tokens = array(); // array of tokens $matches = array(); // matches[0] full pattern match, [1,2,3] subpatterns // this one is taken from perl-ldap, modified for php $pattern = "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x"; /** * This one matches one big pattern wherin only one of the three subpatterns matched * We are interested in the subpatterns that matched. If it matched its value will be * non-empty and so it is a token. Tokens may be round brackets, a string, or a string * enclosed by ' */ preg_match_all($pattern, $value, $matches); for ($i = 0; $i < count($matches[0]); $i++) { // number of tokens (full pattern match) for ($j = 1; $j < 4; $j++) { // each subpattern if (null != trim($matches[$j][$i])) { // pattern match in this subpattern $tokens[$i] = trim($matches[$j][$i]); // this is the token } } } return $tokens; } /** * Returns wether a attribute syntax is binary or not * * This method gets used by Net_LDAP2_Entry to decide which * PHP function needs to be used to fetch the value in the * proper format (e.g. binary or string) * * @param string $attribute The name of the attribute (eg.: 'sn') * * @access public * @return boolean */ public function isBinary($attribute) { $return = false; // default to false // This list contains all syntax that should be treaten as // containing binary values // The Syntax Definitons go into constants at the top of this page $syntax_binary = array( NET_LDAP2_SYNTAX_OCTET_STRING, NET_LDAP2_SYNTAX_JPEG ); // Check Syntax $attr_s = $this->get('attribute', $attribute); if (Net_LDAP2::isError($attr_s)) { // Attribute not found in schema $return = false; // consider attr not binary } elseif (isset($attr_s['syntax']) && in_array($attr_s['syntax'], $syntax_binary)) { // Syntax is defined as binary in schema $return = true; } else { // Syntax not defined as binary, or not found // if attribute is a subtype, check superior attribute syntaxes if (isset($attr_s['sup'])) { foreach ($attr_s['sup'] as $superattr) { $return = $this->isBinary($superattr); if ($return) { break; // stop checking parents since we are binary } } } } return $return; } /** * See if an schema element exists * * @param string $type Type of name, see get() * @param string $name Name or OID * * @return boolean */ public function exists($type, $name) { $entry = $this->get($type, $name); if ($entry instanceof Net_LDAP2_ERROR) { return false; } else { return true; } } /** * See if an attribute is defined in the schema * * @param string $attribute Name or OID of the attribute * @return boolean */ public function attributeExists($attribute) { return $this->exists('attribute', $attribute); } /** * See if an objectClass is defined in the schema * * @param string $ocl Name or OID of the objectClass * @return boolean */ public function objectClassExists($ocl) { return $this->exists('objectclass', $ocl); } /** * See to which ObjectClasses an attribute is assigned * * The objectclasses are sorted into the keys 'may' and 'must'. * * @param string $attribute Name or OID of the attribute * * @return array|Net_LDAP2_Error Associative array with OCL names or Error */ public function getAssignedOCLs($attribute) { $may = array(); $must = array(); // Test if the attribute type is defined in the schema, // if so, retrieve real name for lookups $attr_entry = $this->get('attribute', $attribute); if ($attr_entry instanceof Net_LDAP2_ERROR) { return PEAR::raiseError("Attribute $attribute not defined in schema: ".$attr_entry->getMessage()); } else { $attribute = $attr_entry['name']; } // We need to get all defined OCLs for this. $ocls = $this->getAll('objectclasses'); foreach ($ocls as $ocl => $ocl_data) { // Fetch the may and must attrs and see if our searched attr is contained. // If so, record it in the corresponding array. $ocl_may_attrs = $this->may($ocl); $ocl_must_attrs = $this->must($ocl); if (is_array($ocl_may_attrs) && in_array($attribute, $ocl_may_attrs)) { array_push($may, $ocl_data['name']); } if (is_array($ocl_must_attrs) && in_array($attribute, $ocl_must_attrs)) { array_push($must, $ocl_data['name']); } } return array('may' => $may, 'must' => $must); } /** * See if an attribute is available in a set of objectClasses * * @param string $attribute Attribute name or OID * @param array $ocls Names of OCLs to check for * * @return boolean TRUE, if the attribute is defined for at least one of the OCLs */ public function checkAttribute($attribute, $ocls) { foreach ($ocls as $ocl) { $ocl_entry = $this->get('objectclass', $ocl); $ocl_may_attrs = $this->may($ocl); $ocl_must_attrs = $this->must($ocl); if (is_array($ocl_may_attrs) && in_array($attribute, $ocl_may_attrs)) { return true; } if (is_array($ocl_must_attrs) && in_array($attribute, $ocl_must_attrs)) { return true; } } return false; // no ocl for the ocls found. } } ?>