code:
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* File containing the Net_LDAP2_RootDSE interface class.
*
* PHP version 5
*
* @category  Net
* @package   Net_LDAP2
* @author    Jan Wagner <wagner@netsols.de>
* @copyright 2009 Jan Wagner
* @license   http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
* @version   SVN: $Id: RootDSE.php 286718 2009-08-03 07:30:49Z beni $
* @link      http://pear.php.net/package/Net_LDAP2/
*/

/**
* Includes
*/
require_once 'PEAR.php';

/**
* Getting the rootDSE entry of a LDAP server
*
* @category Net
* @package  Net_LDAP2
* @author   Jan Wagner <wagner@netsols.de>
* @license  http://www.gnu.org/copyleft/lesser.html LGPL
* @link     http://pear.php.net/package/Net_LDAP22/
*/
class Net_LDAP2_RootDSE extends PEAR
{
    /**
    * @access protected
    * @var object Net_LDAP2_Entry
    **/
    protected $_entry;

    /**
    * Class constructor
    *
    * @param Net_LDAP2_Entry &$entry Net_LDAP2_Entry object of the RootDSE
    */
    protected function __construct(&$entry)
    {
        $this->_entry = $entry;
    }

    /**
    * Fetches a RootDSE object from an LDAP connection
    *
    * @param Net_LDAP2 $ldap  Directory from which the RootDSE should be fetched
    * @param array     $attrs Array of attributes to search for
    *
    * @access static
    * @return Net_LDAP2_RootDSE|Net_LDAP2_Error
    */
    public static function fetch($ldap, $attrs = null)
    {
        if (!$ldap instanceof Net_LDAP2) {
            return PEAR::raiseError("Unable to fetch Schema: Parameter \$ldap must be a Net_LDAP2 object!");
        }

        if (is_array($attrs) && count($attrs) > 0 ) {
            $attributes = $attrs;
        } else {
            $attributes = array('vendorName',
                                'vendorVersion',
                                'namingContexts',
                                'altServer',
                                'supportedExtension',
                                'supportedControl',
                                'supportedSASLMechanisms',
                                'supportedLDAPVersion',
                                'subschemaSubentry' );
        }
        $result = $ldap->search('', '(objectClass=*)', array('attributes' => $attributes, 'scope' => 'base'));
        if (self::isError($result)) {
            return $result;
        }
        $entry = $result->shiftEntry();
        if (false === $entry) {
            return PEAR::raiseError('Could not fetch RootDSE entry');
        }
        $ret = new Net_LDAP2_RootDSE($entry);
        return $ret;
    }

    /**
    * Gets the requested attribute value
    *
    * Same usuage as {@link Net_LDAP2_Entry::getValue()}
    *
    * @param string $attr    Attribute name
    * @param array  $options Array of options
    *
    * @access public
    * @return mixed Net_LDAP2_Error object or attribute values
    * @see Net_LDAP2_Entry::get_value()
    */
    public function getValue($attr = '', $options = '')
    {
        return $this->_entry->get_value($attr, $options);
    }

    /**
    * Alias function of getValue() for perl-ldap interface
    *
    * @see getValue()
    * @return mixed
    */
    public function get_value()
    {
        $args = func_get_args();
        return call_user_func_array(array( &$this, 'getValue' ), $args);
    }

    /**
    * Determines if the extension is supported
    *
    * @param array $oids Array of oids to check
    *
    * @access public
    * @return boolean
    */
    public function supportedExtension($oids)
    {
        return $this->checkAttr($oids, 'supportedExtension');
    }

    /**
    * Alias function of supportedExtension() for perl-ldap interface
    *
    * @see supportedExtension()
    * @return boolean
    */
    public function supported_extension()
    {
        $args = func_get_args();
        return call_user_func_array(array( &$this, 'supportedExtension'), $args);
    }

    /**
    * Determines if the version is supported
    *
    * @param array $versions Versions to check
    *
    * @access public
    * @return boolean
    */
    public function supportedVersion($versions)
    {
        return $this->checkAttr($versions, 'supportedLDAPVersion');
    }

    /**
    * Alias function of supportedVersion() for perl-ldap interface
    *
    * @see supportedVersion()
    * @return boolean
    */
    public function supported_version()
    {
        $args = func_get_args();
        return call_user_func_array(array(&$this, 'supportedVersion'), $args);
    }

    /**
    * Determines if the control is supported
    *
    * @param array $oids Control oids to check
    *
    * @access public
    * @return boolean
    */
    public function supportedControl($oids)
    {
        return $this->checkAttr($oids, 'supportedControl');
    }

    /**
    * Alias function of supportedControl() for perl-ldap interface
    *
    * @see supportedControl()
    * @return boolean
    */
    public function supported_control()
    {
        $args = func_get_args();
        return call_user_func_array(array(&$this, 'supportedControl' ), $args);
    }

    /**
    * Determines if the sasl mechanism is supported
    *
    * @param array $mechlist SASL mechanisms to check
    *
    * @access public
    * @return boolean
    */
    public function supportedSASLMechanism($mechlist)
    {
        return $this->checkAttr($mechlist, 'supportedSASLMechanisms');
    }

    /**
    * Alias function of supportedSASLMechanism() for perl-ldap interface
    *
    * @see supportedSASLMechanism()
    * @return boolean
    */
    public function supported_sasl_mechanism()
    {
        $args = func_get_args();
        return call_user_func_array(array(&$this, 'supportedSASLMechanism'), $args);
    }

    /**
    * Checks for existance of value in attribute
    *
    * @param array  $values values to check
    * @param string $attr   attribute name
    *
    * @access protected
    * @return boolean
    */
    protected function checkAttr($values, $attr)
    {
        if (!is_array($values)) $values = array($values);

        foreach ($values as $value) {
            if (!@in_array($value, $this->get_value($attr, 'all'))) {
                return false;
            }
        }
        return true;
    }
}

?>


code:
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* File containing the Net_LDAP2_Schema interface class.
*
* PHP version 5
*
* @category  Net
* @package   Net_LDAP2
* @author    Jan Wagner <wagner@netsols.de>
* @author    Benedikt Hallinger <beni@php.net>
* @copyright 2009 Jan Wagner, Benedikt Hallinger
* @license   http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
* @version   SVN: $Id: Schema.php 296515 2010-03-22 14:46:41Z beni $
* @link      http://pear.php.net/package/Net_LDAP2/
* @todo see the comment at the end of the file
*/

/**
* Includes
*/
require_once 'PEAR.php';

/**
* Syntax definitions
*
* Please don't forget to add binary attributes to isBinary() below
* to support proper value fetching from Net_LDAP2_Entry
*/
define('NET_LDAP2_SYNTAX_BOOLEAN',            '1.3.6.1.4.1.1466.115.121.1.7');
define('NET_LDAP2_SYNTAX_DIRECTORY_STRING',   '1.3.6.1.4.1.1466.115.121.1.15');
define('NET_LDAP2_SYNTAX_DISTINGUISHED_NAME', '1.3.6.1.4.1.1466.115.121.1.12');
define('NET_LDAP2_SYNTAX_INTEGER',            '1.3.6.1.4.1.1466.115.121.1.27');
define('NET_LDAP2_SYNTAX_JPEG',               '1.3.6.1.4.1.1466.115.121.1.28');
define('NET_LDAP2_SYNTAX_NUMERIC_STRING',     '1.3.6.1.4.1.1466.115.121.1.36');
define('NET_LDAP2_SYNTAX_OID',                '1.3.6.1.4.1.1466.115.121.1.38');
define('NET_LDAP2_SYNTAX_OCTET_STRING',       '1.3.6.1.4.1.1466.115.121.1.40');

/**
* Load an LDAP Schema and provide information
*
* This class takes a Subschema entry, parses this information
* and makes it available in an array. Most of the code has been
* inspired by perl-ldap( http://perl-ldap.sourceforge.net).
* You will find portions of their implementation in here.
*
* @category Net
* @package  Net_LDAP2
* @author   Jan Wagner <wagner@netsols.de>
* @author   Benedikt Hallinger <beni@php.net>
* @license  http://www.gnu.org/copyleft/lesser.html LGPL
* @link     http://pear.php.net/package/Net_LDAP22/
*/
class Net_LDAP2_Schema extends PEAR
{
    /**
    * Map of entry types to ldap attributes of subschema entry
    *
    * @access public
    * @var array
    */
    public $types = array(
            'attribute'        => 'attributeTypes',
            'ditcontentrule'   => 'dITContentRules',
            'ditstructurerule' => 'dITStructureRules',
            'matchingrule'     => 'matchingRules',
            'matchingruleuse'  => 'matchingRuleUse',
            'nameform'         => 'nameForms',
            'objectclass'      => 'objectClasses',
            'syntax'           => 'ldapSyntaxes'
        );

    /**
    * Array of entries belonging to this type
    *
    * @access protected
    * @var array
    */
    protected $_attributeTypes    = array();
    protected $_matchingRules     = array();
    protected $_matchingRuleUse   = array();
    protected $_ldapSyntaxes      = array();
    protected $_objectClasses     = array();
    protected $_dITContentRules   = array();
    protected $_dITStructureRules = array();
    protected $_nameForms         = array();

    /**
    * hash of all fetched oids
    *
    * @access protected
    * @var array
    */
    protected $_oids = array();

    /**
    * Tells if the schema is initialized
    *
    * @access protected
    * @var boolean
    * @see parse(), get()
    */
    protected $_initialized = false;

    /**
    * Constructor of the class
    *
    * @access protected
    */
    protected function __construct()
    {
        $this->PEAR('Net_LDAP2_Error'); // default error class
    }

    /**
    * Fetch the Schema from an LDAP connection
    *
    * @param Net_LDAP2 $ldap LDAP connection
    * @param string    $dn   (optional) Subschema entry dn
    *
    * @access public
    * @return Net_LDAP2_Schema|NET_LDAP2_Error
    */
    public function fetch($ldap, $dn = null)
    {
        if (!$ldap instanceof Net_LDAP2) {
            return PEAR::raiseError("Unable to fetch Schema: Parameter \$ldap must be a Net_LDAP2 object!");
        }

        $schema_o = new Net_LDAP2_Schema();

        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;
                }
            }
        }

        // 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.
    }
}
?>

license: mit


code:
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* File containing the Net_LDAP2_SchemaCache interface class.
*
* PHP version 5
*
* @category  Net
* @package   Net_LDAP2
* @author    Benedikt Hallinger <beni@php.net>
* @copyright 2009 Benedikt Hallinger
* @license   http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
* @version   SVN: $Id: SchemaCache.interface.php 286718 2009-08-03 07:30:49Z beni $
* @link      http://pear.php.net/package/Net_LDAP2/
*/

/**
* Interface describing a custom schema cache object
*
* To implement a custom schema cache, one must implement this interface and
* pass the instanciated object to Net_LDAP2s registerSchemaCache() method.
*/
interface Net_LDAP2_SchemaCache
{
    /**
    * Return the schema object from the cache
    *
    * Net_LDAP2 will consider anything returned invalid, except
    * a valid Net_LDAP2_Schema object.
    * In case you return a Net_LDAP2_Error, this error will be routed
    * to the return of the $ldap->schema() call.
    * If you return something else, Net_LDAP2 will
    * fetch a fresh Schema object from the LDAP server.
    *
    * You may want to implement a cache aging mechanism here too.
    *
    * @return Net_LDAP2_Schema|Net_LDAP2_Error|false
    */
    public function loadSchema();

    /**
    * Store a schema object in the cache
    *
    * This method will be called, if Net_LDAP2 has fetched a fresh
    * schema object from LDAP and wants to init or refresh the cache.
    *
    * In case of errors you may return a Net_LDAP2_Error which will
    * be routet to the client.
    * Note that doing this prevents, that the schema object fetched from LDAP
    * will be given back to the client, so only return errors if storing
    * of the cache is something crucial (e.g. for doing something else with it).
    * Normaly you dont want to give back errors in which case Net_LDAP2 needs to
    * fetch the schema once per script run and instead use the error
    * returned from loadSchema().
    *
    * @return true|Net_LDAP2_Error
    */
    public function storeSchema($schema);
}


code:
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* File containing the Net_LDAP2_Search interface class.
*
* PHP version 5
*
* @category  Net
* @package   Net_LDAP2
* @author    Tarjej Huse <tarjei@bergfald.no>
* @author    Benedikt Hallinger <beni@php.net>
* @copyright 2009 Tarjej Huse, Benedikt Hallinger
* @license   http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
* @version   SVN: $Id: Search.php 328961 2013-01-03 09:04:30Z beni $
* @link      http://pear.php.net/package/Net_LDAP2/
*/

/**
* Includes
*/
require_once 'PEAR.php';

/**
* Result set of an LDAP search
*
* @category Net
* @package  Net_LDAP2
* @author   Tarjej Huse <tarjei@bergfald.no>
* @author   Benedikt Hallinger <beni@php.net>
* @license  http://www.gnu.org/copyleft/lesser.html LGPL
* @link     http://pear.php.net/package/Net_LDAP22/
*/
class Net_LDAP2_Search extends PEAR implements Iterator
{
    /**
    * Search result identifier
    *
    * @access protected
    * @var resource
    */
    protected $_search;

    /**
    * LDAP resource link
    *
    * @access protected
    * @var resource
    */
    protected $_link;

    /**
    * Net_LDAP2 object
    *
    * A reference of the Net_LDAP2 object for passing to Net_LDAP2_Entry
    *
    * @access protected
    * @var object Net_LDAP2
    */
    protected $_ldap;

    /**
    * Result entry identifier
    *
    * @access protected
    * @var resource
    */
    protected $_entry = null;

    /**
    * The errorcode the search got
    *
    * Some errorcodes might be of interest, but might not be best handled as errors.
    * examples: 4 - LDAP_SIZELIMIT_EXCEEDED - indicates a huge search.
    *               Incomplete results are returned. If you just want to check if there's anything in the search.
    *               than this is a point to handle.
    *           32 - no such object - search here returns a count of 0.
    *
    * @access protected
    * @var int
    */
    protected $_errorCode = 0; // if not set - sucess!

    /**
    * Cache for all entries already fetched from iterator interface
    *
    * @access protected
    * @var array
    */
    protected $_iteratorCache = array();

    /**
    * What attributes we searched for
    *
    * The $attributes array contains the names of the searched attributes and gets
    * passed from $Net_LDAP2->search() so the Net_LDAP2_Search object can tell
    * what attributes was searched for ({@link searchedAttrs())
    *
    * This variable gets set from the constructor and returned
    * from {@link searchedAttrs()}
    *
    * @access protected
    * @var array
    */
    protected $_searchedAttrs = array();

    /**
    * Cache variable for storing entries fetched internally
    *
    * This currently is not used by all functions and need consolidation.
    *
    * @access protected
    * @var array
    */
    protected $_entry_cache = false;

    /**
    * Cache variable for count()
    *
    * @see count()
    * @access protected
    * @var int
    */
    protected $_count_cache = null;

    /**
    * Constructor
    *
    * @param resource           &$search    Search result identifier
    * @param Net_LDAP2|resource &$ldap      Net_LDAP2 object or just a LDAP-Link resource
    * @param array              $attributes (optional) Array with searched attribute names. (see {@link $_searchedAttrs})
    *
    * @access public
    */
    public function __construct(&$search, &$ldap, $attributes = array())
    {
        $this->PEAR('Net_LDAP2_Error');

        $this->setSearch($search);

        if ($ldap instanceof Net_LDAP2) {
            $this->_ldap =& $ldap;
            $this->setLink($this->_ldap->getLink());
        } else {
            $this->setLink($ldap);
        }

        $this->_errorCode = @ldap_errno($this->_link);

        if (is_array($attributes) && !empty($attributes)) {
            $this->_searchedAttrs = $attributes;
        }
    }

    /**
    * Returns an array of entry objects.
    *
    * @return array Array of entry objects.
    */
    public function entries()
    {
        $entries = array();

        if (false === $this->_entry_cache) {
            // cache is empty: fetch from LDAP
            while ($entry = $this->shiftEntry()) {
                $entries[] = $entry;
            }
            $this->_entry_cache = $entries; // store result in cache
        }

        return $this->_entry_cache;
    }

    /**
    * Get the next entry in the searchresult from LDAP server.
    *
    * This will return a valid Net_LDAP2_Entry object or false, so
    * you can use this method to easily iterate over the entries inside
    * a while loop.
    *
    * @return Net_LDAP2_Entry|false  Reference to Net_LDAP2_Entry object or false
    */
    public function &shiftEntry()
    {
        if (is_null($this->_entry)) {
            if(!$this->_entry = @ldap_first_entry($this->_link, $this->_search)) {
                $false = false;
                return $false;
            }
            $entry = Net_LDAP2_Entry::createConnected($this->_ldap, $this->_entry);
            if ($entry instanceof PEAR_Error) $entry = false;
        } else {
            if (!$this->_entry = @ldap_next_entry($this->_link, $this->_entry)) {
                $false = false;
                return $false;
            }
            $entry = Net_LDAP2_Entry::createConnected($this->_ldap, $this->_entry);
            if ($entry instanceof PEAR_Error) $entry = false;
        }
        return $entry;
    }

    /**
    * Alias function of shiftEntry() for perl-ldap interface
    *
    * @see shiftEntry()
    * @return Net_LDAP2_Entry|false
    */
    public function shift_entry()
    {
        $args = func_get_args();
        return call_user_func_array(array( &$this, 'shiftEntry' ), $args);
    }

    /**
    * Retrieve the next entry in the searchresult, but starting from last entry
    *
    * This is the opposite to {@link shiftEntry()} and is also very useful
    * to be used inside a while loop.
    *
    * @return Net_LDAP2_Entry|false
    */
    public function popEntry()
    {
        if (false === $this->_entry_cache) {
            // fetch entries into cache if not done so far
            $this->_entry_cache = $this->entries();
        }

        $return = array_pop($this->_entry_cache);
        return (null === $return)? false : $return;
    }

    /**
    * Alias function of popEntry() for perl-ldap interface
    *
    * @see popEntry()
    * @return Net_LDAP2_Entry|false
    */
    public function pop_entry()
    {
        $args = func_get_args();
        return call_user_func_array(array( &$this, 'popEntry' ), $args);
    }

    /**
    * Return entries sorted as array
    *
    * This returns a array with sorted entries and the values.
    * Sorting is done with PHPs {@link array_multisort()}.
    * This method relies on {@link as_struct()} to fetch the raw data of the entries.
    *
    * Please note that attribute names are case sensitive!
    *
    * Usage example:
    *
    *   // to sort entries first by location, then by surename, but descending:
    *   $entries = $search->sorted_as_struct(array('locality','sn'), SORT_DESC);
    *
    *
    * @param array $attrs Array of attribute names to sort; order from left to right.
    * @param int   $order Ordering direction, either constant SORT_ASC or SORT_DESC
    *
    * @return array|Net_LDAP2_Error   Array with sorted entries or error
    * @todo what about server side sorting as specified in http://www.ietf.org/rfc/rfc2891.txt?
    */
    public function sorted_as_struct($attrs = array('cn'), $order = SORT_ASC)
    {
        /*
        * Old Code, suitable and fast for single valued sorting
        * This code should be used if we know that single valued sorting is desired,
        * but we need some method to get that knowledge...
        */
        /*
        $attrs = array_reverse($attrs);
        foreach ($attrs as $attribute) {
            if (!ldap_sort($this->_link, $this->_search, $attribute)){
                $this->raiseError("Sorting failed for Attribute " . $attribute);
            }
        }

        $results = ldap_get_entries($this->_link, $this->_search);

        unset($results['count']); //for tidier output
        if ($order) {
            return array_reverse($results);
        } else {
            return $results;
        }*/

        /*
        * New code: complete "client side" sorting
        */
        // first some parameterchecks
        if (!is_array($attrs)) {
            return PEAR::raiseError("Sorting failed: Parameterlist must be an array!");
        }
        if ($order != SORT_ASC && $order != SORT_DESC) {
            return PEAR::raiseError("Sorting failed: sorting direction not understood! (neither constant SORT_ASC nor SORT_DESC)");
        }

        // fetch the entries data
        $entries = $this->as_struct();

        // now sort each entries attribute values
        // this is neccessary because later we can only sort by one value,
        // so we need the highest or lowest attribute now, depending on the
        // selected ordering for that specific attribute
        foreach ($entries as $dn => $entry) {
            foreach ($entry as $attr_name => $attr_values) {
                sort($entries[$dn][$attr_name]);
                if ($order == SORT_DESC) {
                    array_reverse($entries[$dn][$attr_name]);
                }
            }
        }

        // reformat entrys array for later use with array_multisort()
        $to_sort = array(); // <- will be a numeric array similar to ldap_get_entries
        foreach ($entries as $dn => $entry_attr) {
            $row       = array();
            $row['dn'] = $dn;
            foreach ($entry_attr as $attr_name => $attr_values) {
                $row[$attr_name] = $attr_values;
            }
            $to_sort[] = $row;
        }

        // Build columns for array_multisort()
        // each requested attribute is one row
        $columns = array();
        foreach ($attrs as $attr_name) {
            foreach ($to_sort as $key => $row) {
                $columns[$attr_name][$key] =& $to_sort[$key][$attr_name][0];
            }
        }

        // sort the colums with array_multisort, if there is something
        // to sort and if we have requested sort columns
        if (!empty($to_sort) && !empty($columns)) {
            $sort_params = '';
            foreach ($attrs as $attr_name) {
                $sort_params .= '$columns[\''.$attr_name.'\'], '.$order.', ';
            }
            eval("array_multisort($sort_params \$to_sort);"); // perform sorting
        }

        return $to_sort;
    }

    /**
    * Return entries sorted as objects
    *
    * This returns a array with sorted Net_LDAP2_Entry objects.
    * The sorting is actually done with {@link sorted_as_struct()}.
    *
    * Please note that attribute names are case sensitive!
    * Also note, that it is (depending on server capabilitys) possible to let
    * the server sort your results. This happens through search controls
    * and is described in detail at {@link http://www.ietf.org/rfc/rfc2891.txt}
    *
    * Usage example:
    *
    *   // to sort entries first by location, then by surename, but descending:
    *   $entries = $search->sorted(array('locality','sn'), SORT_DESC);
    *
    *
    * @param array $attrs Array of sort attributes to sort; order from left to right.
    * @param int   $order Ordering direction, either constant SORT_ASC or SORT_DESC
    *
    * @return array|Net_LDAP2_Error   Array with sorted Net_LDAP2_Entries or error
    * @todo Entry object construction could be faster. Maybe we could use one of the factorys instead of fetching the entry again
    */
    public function sorted($attrs = array('cn'), $order = SORT_ASC)
    {
        $return = array();
        $sorted = $this->sorted_as_struct($attrs, $order);
        if (PEAR::isError($sorted)) {
            return $sorted;
        }
        foreach ($sorted as $key => $row) {
            $entry = $this->_ldap->getEntry($row['dn'], $this->searchedAttrs());
            if (!PEAR::isError($entry)) {
                array_push($return, $entry);
            } else {
                return $entry;
            }
        }
        return $return;
    }

    /**
    * Return entries as array
    *
    * This method returns the entries and the selected attributes values as
    * array.
    * The first array level contains all found entries where the keys are the
    * DNs of the entries. The second level arrays contian the entries attributes
    * such that the keys is the lowercased name of the attribute and the values
    * are stored in another indexed array. Note that the attribute values are stored
    * in an array even if there is no or just one value.
    *
    * The array has the following structure:
    *
    * $return = array(
    *           'cn=foo,dc=example,dc=com' => array(
    *                                                'sn'       => array('foo'),
    *                                                'multival' => array('val1', 'val2', 'valN')
    *                                             )
    *           'cn=bar,dc=example,dc=com' => array(
    *                                                'sn'       => array('bar'),
    *                                                'multival' => array('val1', 'valN')
    *                                             )
    *           )
    *
    *
    * @return array      associative result array as described above
    */
    public function as_struct()
    {
        $return  = array();
        $entries = $this->entries();
        foreach ($entries as $entry) {
            $attrs            = array();
            $entry_attributes = $entry->attributes();
            foreach ($entry_attributes as $attr_name) {
                $attr_values = $entry->getValue($attr_name, 'all');
                if (!is_array($attr_values)) {
                    $attr_values = array($attr_values);
                }
                $attrs[$attr_name] = $attr_values;
            }
            $return[$entry->dn()] = $attrs;
        }
        return $return;
    }

    /**
    * Set the search objects resource link
    *
    * @param resource &$search Search result identifier
    *
    * @access public
    * @return void
    */
    public function setSearch(&$search)
    {
        $this->_search = $search;
    }

    /**
    * Set the ldap ressource link
    *
    * @param resource &$link Link identifier
    *
    * @access public
    * @return void
    */
    public function setLink(&$link)
    {
        $this->_link = $link;
    }

    /**
    * Returns the number of entries in the searchresult
    *
    * @return int Number of entries in search.
    */
    public function count()
    {
        // this catches the situation where OL returned errno 32 = no such object!
        if (!$this->_search) {
            return 0;
        }
        // ldap_count_entries is slow (see pear bug #18752) with large results,
        // so we cache the result internally.
        if ($this->_count_cache === null) {
            $this->_count_cache = @ldap_count_entries($this->_link, $this->_search);
        }

        return $this->_count_cache;
    }

    /**
    * Get the errorcode the object got in its search.
    *
    * @return int The ldap error number.
    */
    public function getErrorCode()
    {
        return $this->_errorCode;
    }

    /**
    * Destructor
    *
    * @access protected
    */
    public function _Net_LDAP2_Search()
    {
        @ldap_free_result($this->_search);
    }

    /**
    * Closes search result
    *
    * @return void
    */
    public function done()
    {
        $this->_Net_LDAP2_Search();
    }

    /**
    * Return the attribute names this search selected
    *
    * @return array
    * @see $_searchedAttrs
    * @access protected
    */
    protected function searchedAttrs()
    {
        return $this->_searchedAttrs;
    }

    /**
    * Tells if this search exceeds a sizelimit
    *
    * @return boolean
    */
    public function sizeLimitExceeded()
    {
        return ($this->getErrorCode() == 4);
    }

    /*
    * SPL Iterator interface methods.
    * This interface allows to use Net_LDAP2_Search
    * objects directly inside a foreach loop!
    */
    /**
    * SPL Iterator interface: Return the current element.
    *
    * The SPL Iterator interface allows you to fetch entries inside
    * a foreach() loop: foreach ($search as $dn => $entry) { ...
    *
    * Of course, you may call {@link current()}, {@link key()}, {@link next()},
    * {@link rewind()} and {@link valid()} yourself.
    *
    * If the search throwed an error, it returns false.
    * False is also returned, if the end is reached
    * In case no call to next() was made, we will issue one,
    * thus returning the first entry.
    *
    * @return Net_LDAP2_Entry|false
    */
    public function current()
    {
        if (count($this->_iteratorCache) == 0) {
            $this->next();
            reset($this->_iteratorCache);
        }
        $entry = current($this->_iteratorCache);
        return ($entry instanceof Net_LDAP2_Entry)? $entry : false;
    }

    /**
    * SPL Iterator interface: Return the identifying key (DN) of the current entry.
    *
    * @see current()
    * @return string|false DN of the current entry; false in case no entry is returned by current()
    */
    public function key()
    {
        $entry = $this->current();
        return ($entry instanceof Net_LDAP2_Entry)? $entry->dn() :false;
    }

    /**
    * SPL Iterator interface: Move forward to next entry.
    *
    * After a call to {@link next()}, {@link current()} will return
    * the next entry in the result set.
    *
    * @see current()
    * @return void
    */
    public function next()
    {
        // fetch next entry.
        // if we have no entrys anymore, we add false (which is
        // returned by shiftEntry()) so current() will complain.
        if (count($this->_iteratorCache) - 1 <= $this->count()) {
            $this->_iteratorCache[] = $this->shiftEntry();
        }

        // move on array pointer to current element.
        // even if we have added all entries, this will
        // ensure proper operation in case we rewind()
        next($this->_iteratorCache);
    }

    /**
    * SPL Iterator interface:  Check if there is a current element after calls to {@link rewind()} or {@link next()}.
    *
    * Used to check if we've iterated to the end of the collection.
    *
    * @see current()
    * @return boolean FALSE if there's nothing more to iterate over
    */
    public function valid()
    {
        return ($this->current() instanceof Net_LDAP2_Entry);
    }

    /**
    * SPL Iterator interface: Rewind the Iterator to the first element.
    *
    * After rewinding, {@link current()} will return the first entry in the result set.
    *
    * @see current()
    * @return void
    */
    public function rewind()
    {
        reset($this->_iteratorCache);
    }
}

?>


code:
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* File containing the example simple file based Schema Caching class.
*
* PHP version 5
*
* @category  Net
* @package   Net_LDAP2
* @author    Benedikt Hallinger <beni@php.net>
* @copyright 2009 Benedikt Hallinger
* @license   http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
* @version   SVN: $Id: SimpleFileSchemaCache.php 286718 2009-08-03 07:30:49Z beni $
* @link      http://pear.php.net/package/Net_LDAP2/
*/

/**
* A simple file based schema cacher with cache aging.
*
* Once the cache is too old, the loadSchema() method will return false, so
* Net_LDAP2 will fetch a fresh object from the LDAP server that will
* overwrite the current (outdated) old cache.
*/
class Net_LDAP2_SimpleFileSchemaCache implements Net_LDAP2_SchemaCache
{
    /**
    * Internal config of this cache
    *
    * @see Net_LDAP2_SimpleFileSchemaCache()
    * @var array
    */
    protected $config = array(
        'path'    => '/tmp/Net_LDAP_Schema.cache',
        'max_age' => 1200
    );

    /**
    * Initialize the simple cache
    *
    * Config is as following:
    *  path     Complete path to the cache file.
    *  max_age  Maximum age of cache in seconds, 0 means "endlessly".
    *
    * @param array $cfg Config array
    */
    public function Net_LDAP2_SimpleFileSchemaCache($cfg)
    {
    	foreach ($cfg as $key => $value) {
			if (array_key_exists($key, $this->config)) {
				if (gettype($this->config[$key]) != gettype($value)) {
					$this->getCore()->dropFatalError(__CLASS__.": Could not set config! Key $key does not match type ".gettype($this->config[$key])."!");
				}
				$this->config[$key] = $value;
			} else {
				$this->getCore()->dropFatalError(__CLASS__.": Could not set config! Key $key is not defined!");
			}
		}
    }

    /**
    * Return the schema object from the cache
    *
    * If file is existent and cache has not expired yet,
    * then the cache is deserialized and returned.
    *
    * @return Net_LDAP2_Schema|Net_LDAP2_Error|false
    */
    public function loadSchema()
    {
         $return = false; // Net_LDAP2 will load schema from LDAP
         if (file_exists($this->config['path'])) {
             $cache_maxage = filemtime($this->config['path']) + $this->config['max_age'];
             if (time() <= $cache_maxage || $this->config['max_age'] == 0) {
                 $return = unserialize(file_get_contents($this->config['path']));
             }
         }
         return $return;
    }

    /**
    * Store a schema object in the cache
    *
    * This method will be called, if Net_LDAP2 has fetched a fresh
    * schema object from LDAP and wants to init or refresh the cache.
    *
    * To invalidate the cache and cause Net_LDAP2 to refresh the cache,
    * you can call this method with null or false as value.
    * The next call to $ldap->schema() will then refresh the caches object.
    *
    * @param mixed $schema The object that should be cached
    * @return true|Net_LDAP2_Error|false
    */
    public function storeSchema($schema) {
        file_put_contents($this->config['path'], serialize($schema));
        return true;
    }
}


code:
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* File containing the Net_LDAP2_Util interface class.
*
* PHP version 5
*
* @category  Net
* @package   Net_LDAP2
* @author    Benedikt Hallinger <beni@php.net>
* @copyright 2009 Benedikt Hallinger
* @license   http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
* @version   SVN: $Id: Util.php 332278 2013-12-05 11:01:15Z beni $
* @link      http://pear.php.net/package/Net_LDAP2/
*/

/**
* Includes
*/
require_once 'PEAR.php';

/**
* Utility Class for Net_LDAP2
*
* This class servers some functionality to the other classes of Net_LDAP2 but most of
* the methods can be used separately as well.
*
* @category Net
* @package  Net_LDAP2
* @author   Benedikt Hallinger <beni@php.net>
* @license  http://www.gnu.org/copyleft/lesser.html LGPL
* @link     http://pear.php.net/package/Net_LDAP22/
*/
class Net_LDAP2_Util extends PEAR
{
    /**
     * Constructor
     *
     * @access public
     */
    public function __construct()
    {
         // We do nothing here, since all methods can be called statically.
         // In Net_LDAP <= 0.7, we needed a instance of Util, because
         // it was possible to do utf8 encoding and decoding, but this
         // has been moved to the LDAP class. The constructor remains only
         // here to document the downward compatibility of creating an instance.
    }

    /**
    * Explodes the given DN into its elements
    *
    * {@link http://www.ietf.org/rfc/rfc2253.txt RFC 2253} says, a Distinguished Name is a sequence
    * of Relative Distinguished Names (RDNs), which themselves
    * are sets of Attributes. For each RDN a array is constructed where the RDN part is stored.
    *
    * For example, the DN 'OU=Sales+CN=J. Smith,DC=example,DC=net' is exploded to:
    * <kbd>array( [0] => array([0] => 'OU=Sales', [1] => 'CN=J. Smith'), [2] => 'DC=example', [3] => 'DC=net' )</kbd>
    *
    * [NOT IMPLEMENTED] DNs might also contain values, which are the bytes of the BER encoding of
    * the X.500 AttributeValue rather than some LDAP string syntax. These values are hex-encoded
    * and prefixed with a #. To distinguish such BER values, ldap_explode_dn uses references to
    * the actual values, e.g. '1.3.6.1.4.1.1466.0=#04024869,DC=example,DC=com' is exploded to:
    * [ { '1.3.6.1.4.1.1466.0' => "\004\002Hi" }, { 'DC' => 'example' }, { 'DC' => 'com' } ];
    * See {@link http://www.vijaymukhi.com/vmis/berldap.htm} for more information on BER.
    *
    *  It also performs the following operations on the given DN:
    *   - Unescape "\" followed by ",", "+", """, "\", "<", ">", ";", "#", "=", " ", or a hexpair
    *     and strings beginning with "#".
    *   - Removes the leading 'OID.' characters if the type is an OID instead of a name.
    *   - 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 types names.
    *               Attribute values are not affected by this option.
    *               The default is to uppercase. Valid values are:
    *               lower        Lowercase attribute types names.
    *               upper        Uppercase attribute type names. This is the default.
    *               none         Do not change attribute type names.
    *   reverse     If TRUE, the RDN sequence is reversed.
    *   onlyvalues  If TRUE, then only attributes values are returned ('foo' instead of 'cn=foo')
    *

    * @param string $dn      The DN that should be exploded
    * @param array  $options Options to use
    *
    * @static
    * @return array   Parts of the exploded DN
    * @todo implement BER
    */
    public static function ldap_explode_dn($dn, $options = array('casefold' => 'upper'))
    {
        if (!isset($options['onlyvalues'])) $options['onlyvalues']  = false;
        if (!isset($options['reverse']))    $options['reverse']     = false;
        if (!isset($options['casefold']))   $options['casefold']    = 'upper';

        // Escaping of DN and stripping of "OID."
        $dn = self::canonical_dn($dn, array('casefold' => $options['casefold']));

        // splitting the DN
        $dn_array = preg_split('/(?<=[^\\\\]),/', $dn);

        // clear wrong splitting (possibly we have split too much)
        // /!\ Not clear, if this is neccessary here
        //$dn_array = self::correct_dn_splitting($dn_array, ',');

        // construct subarrays for multivalued RDNs and unescape DN value
        // also convert to output format and apply casefolding
        foreach ($dn_array as $key => $value) {
            $value_u = self::unescape_dn_value($value);
            $rdns    = self::split_rdn_multival($value_u[0]);
            if (count($rdns) > 1) {
                // MV RDN!
                foreach ($rdns as $subrdn_k => $subrdn_v) {
                    // Casefolding
                    if ($options['casefold'] == 'upper') $subrdn_v = preg_replace("/^(\w+=)/e", "''.strtoupper('\\1').''", $subrdn_v);
                    if ($options['casefold'] == 'lower') $subrdn_v = preg_replace("/^(\w+=)/e", "''.strtolower('\\1').''", $subrdn_v);

                    if ($options['onlyvalues']) {
                        preg_match('/(.+?)(?<!\\\\)=(.+)/', $subrdn_v, $matches);
                        $rdn_ocl         = $matches[1];
                        $rdn_val         = $matches[2];
                        $unescaped       = self::unescape_dn_value($rdn_val);
                        $rdns[$subrdn_k] = $unescaped[0];
                    } else {
                        $unescaped = self::unescape_dn_value($subrdn_v);
                        $rdns[$subrdn_k] = $unescaped[0];
                    }
                }

                $dn_array[$key] = $rdns;
            } else {
                // normal RDN

                // Casefolding
                if ($options['casefold'] == 'upper') $value = preg_replace("/^(\w+=)/e", "''.strtoupper('\\1').''", $value);
                if ($options['casefold'] == 'lower') $value = preg_replace("/^(\w+=)/e", "''.strtolower('\\1').''", $value);

                if ($options['onlyvalues']) {
                    preg_match('/(.+?)(?<!\\\\)=(.+)/', $value, $matches);
                    $dn_ocl         = $matches[1];
                    $dn_val         = $matches[2];
                    $unescaped      = self::unescape_dn_value($dn_val);
                    $dn_array[$key] = $unescaped[0];
                } else {
                    $unescaped = self::unescape_dn_value($value);
                    $dn_array[$key] = $unescaped[0];
                }
            }
        }

        if ($options['reverse']) {
            return array_reverse($dn_array);
        } else {
            return $dn_array;
        }
    }

    /**
    * Escapes a DN value according to RFC 2253
    *
    * Escapes the given VALUES according to RFC 2253 so that they can be safely used in LDAP DNs.
    * The characters ",", "+", """, "\", "<", ">", ";", "#", "=" with a special meaning in RFC 2252
    * are preceeded by ba backslash. Control characters with an ASCII code < 32 are represented as \hexpair.
    * Finally all leading and trailing spaces are converted to sequences of \20.
    *
    * @param array $values An array containing the DN values that should be escaped
    *
    * @static
    * @return array The array $values, but escaped
    */
    public static function escape_dn_value($values = array())
    {
        // Parameter validation
        if (!is_array($values)) {
            $values = array($values);
        }

        foreach ($values as $key => $val) {
            // Escaping of filter meta characters
            $val = str_replace('\\', '\\\\', $val);
            $val = str_replace(',',    '\,', $val);
            $val = str_replace('+',    '\+', $val);
            $val = str_replace('"',    '\"', $val);
            $val = str_replace('<',    '\<', $val);
            $val = str_replace('>',    '\>', $val);
            $val = str_replace(';',    '\;', $val);
            $val = str_replace('#',    '\#', $val);
            $val = str_replace('=',    '\=', $val);

            // ASCII < 32 escaping
            $val = self::asc2hex32($val);

            // Convert all leading and trailing spaces to sequences of \20.
            if (preg_match('/^(\s*)(.+?)(\s*)$/', $val, $matches)) {
                $val = $matches[2];
                for ($i = 0; $i < strlen($matches[1]); $i++) {
                    $val = '\20'.$val;
                }
                for ($i = 0; $i < strlen($matches[3]); $i++) {
                    $val = $val.'\20';
                }
            }

            if (null === $val) $val = '\0';  // apply escaped "null" if string is empty

            $values[$key] = $val;
        }

        return $values;
    }

    /**
    * Undoes the conversion done by escape_dn_value().
    *
    * Any escape sequence starting with a baskslash - hexpair or special character -
    * will be transformed back to the corresponding character.
    *
    * @param array $values Array of DN Values
    *
    * @return array Same as $values, but unescaped
    * @static
    */
    public static function unescape_dn_value($values = array())
    {
        // Parameter validation
        if (!is_array($values)) {
            $values = array($values);
        }

        foreach ($values as $key => $val) {
            // strip slashes from special chars
            $val = str_replace('\\\\', '\\', $val);
            $val = str_replace('\,',    ',', $val);
            $val = str_replace('\+',    '+', $val);
            $val = str_replace('\"',    '"', $val);
            $val = str_replace('\<',    '<', $val);
            $val = str_replace('\>',    '>', $val);
            $val = str_replace('\;',    ';', $val);
            $val = str_replace('\#',    '#', $val);
            $val = str_replace('\=',    '=', $val);

            // Translate hex code into ascii
            $values[$key] = self::hex2asc($val);
        }

        return $values;
    }

    /**
    * Returns the given DN in a canonical form
    *
    * Returns false if DN is not a valid Distinguished Name.
    * 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 (',').
    *
    * Note: The empty string "" is a valid DN, so be sure not to do a "$can_dn == false" test,
    *       because an empty string evaluates to false. Use the "===" operator instead.
    *
    * @param array|string $dn      The DN
    * @param array        $options Options to use
    *
    * @static
    * @return false|string The canonical DN or FALSE
    * @todo implement option mbcescape
    */
    public static function canonical_dn($dn, $options = array('casefold' => 'upper', 'separator' => ','))
    {
        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)) {
            // It is not clear to me if the perl implementation splits by the user defined
            // separator or if it just uses this separator to construct the new DN
            $dn = preg_split('/(?<=[^\\\\])'.$options['separator'].'/', $dn);

            // clear wrong splitting (possibly we have split too much)
            $dn = self::correct_dn_splitting($dn, $options['separator']);
        } 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 indexed, if associative array detected
            if ($assoc) {
                $newdn = array();
                foreach ($dn as $dn_key => $dn_part) {
                    if (is_array($dn_part)) {
                        ksort($dn_part, SORT_STRING); // we assume here, that the rdn parts are also associative
                        $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 = self::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 = self::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 = self::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!
                    // split at first unescaped "="
                    $dn_comp = preg_split('/(?<=[^\\\\])=/', $rdns[0], 2);
                    $ocl     = ltrim($dn_comp[0]);  // trim left whitespaces 'cause of "cn=foo, l=bar" syntax (whitespace after comma)
                    $val     = $dn_comp[1];

                    // strip 'OID.', 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 = self::escape_dn_value(array($ocl));
                        $ocl = $ocl[0];
                    }

                    // escaping of dn-value
                    $val = self::escape_dn_value(array($val));
                    $val = str_replace('/', '\/', $val[0]);

                    $dn[$pos] = $ocl.'='.$val;
                }
            }
        }

        if ($options['reverse']) $dn = array_reverse($dn);
        return implode($options['separator'], $dn);
    }

    /**
    * Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters.
    *
    * Any control characters with an ACII code < 32 as well as the characters with special meaning in
    * LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a
    * backslash followed by two hex digits representing the hexadecimal value of the character.
    *
    * @param array $values Array of values to escape
    *
    * @static
    * @return array Array $values, but escaped
    */
    public static function escape_filter_value($values = array())
    {
        // Parameter validation
        if (!is_array($values)) {
            $values = array($values);
        }

        foreach ($values as $key => $val) {
            // Escaping of filter meta characters
            $val = str_replace('\\', '\5c', $val);
            $val = str_replace('*',  '\2a', $val);
            $val = str_replace('(',  '\28', $val);
            $val = str_replace(')',  '\29', $val);

            // ASCII < 32 escaping
            $val = self::asc2hex32($val);

            if (null === $val) $val = '\0';  // apply escaped "null" if string is empty

            $values[$key] = $val;
        }

        return $values;
    }

    /**
    * Undoes the conversion done by {@link escape_filter_value()}.
    *
    * Converts any sequences of a backslash followed by two hex digits into the corresponding character.
    *
    * @param array $values Array of values to escape
    *
    * @static
    * @return array Array $values, but unescaped
    */
    public static function unescape_filter_value($values = array())
    {
        // Parameter validation
        if (!is_array($values)) {
            $values = array($values);
        }

        foreach ($values as $key => $value) {
            // Translate hex code into ascii
            $values[$key] = self::hex2asc($value);
        }

        return $values;
    }

    /**
    * Converts all ASCII chars < 32 to "\HEX"
    *
    * @param string $string String to convert
    *
    * @static
    * @return string
    */
    public static function asc2hex32($string)
    {
        for ($i = 0; $i < strlen($string); $i++) {
            $char = substr($string, $i, 1);
            if (ord($char) < 32) {
                $hex = dechex(ord($char));
                if (strlen($hex) == 1) $hex = '0'.$hex;
                $string = str_replace($char, '\\'.$hex, $string);
            }
        }
        return $string;
    }

    /**
    * Converts all Hex expressions ("\HEX") to their original ASCII characters
    *
    * @param string $string String to convert
    *
    * @static
    * @author beni@php.net, heavily based on work from DavidSmith@byu.net
    * @return string
    */
    public static function hex2asc($string)
    {
        $string = preg_replace("/\\\([0-9A-Fa-f]{2})/e", "''.chr(hexdec('\\1')).''", $string);
        return $string;
    }

    /**
    * Split an multivalued RDN value into an Array
    *
    * A RDN can contain multiple values, spearated by a plus sign.
    * This function returns each separate ocl=value pair of the RDN part.
    *
    * If no multivalued RDN is detected, an array containing only
    * the original rdn part is returned.
    *
    * For example, the multivalued RDN 'OU=Sales+CN=J. Smith' is exploded to:
    * <kbd>array([0] => 'OU=Sales', [1] => 'CN=J. Smith')</kbd>
    *
    * The method trys to be smart if it encounters unescaped "+" characters, but may fail,
    * so ensure escaped "+"es in attr names and attr values.
    *
    * [BUG] If you have a multivalued RDN with unescaped plus characters
    *       and there is a unescaped plus sign at the end of an value followed by an
    *       attribute name containing an unescaped plus, then you will get wrong splitting:
    *         $rdn = 'OU=Sales+C+N=J. Smith';
    *       returns:
    *         array('OU=Sales+C', 'N=J. Smith');
    *       The "C+" is treaten as value of the first pair instead as attr name of the second pair.
    *       To prevent this, escape correctly.
    *
    * @param string $rdn Part of an (multivalued) escaped RDN (eg. ou=foo OR ou=foo+cn=bar)
    *
    * @static
    * @return array Array with the components of the multivalued RDN or Error
    */
    public static function split_rdn_multival($rdn)
    {
        $rdns = preg_split('/(?<!\\\\)\+/', $rdn);
        $rdns = self::correct_dn_splitting($rdns, '+');
        return array_values($rdns);
    }

    /**
    * Splits an attribute=value syntax into an array
    *
    * If escaped delimeters are used, they are returned escaped as well.
    * The split will occur at the first unescaped delimeter character.
    * In case an invalid delimeter is given, no split will be performed and an
    * one element array gets returned.
    * Optional also filter-assertion delimeters can be considered (>, <, >=, <=, ~=).
    *
    * @param string  $attr      Attribute and Value Syntax ("foo=bar")
    * @param boolean $extended  If set to true, also filter-assertion delimeter will be matched
    * @param boolean $withDelim If set to true, the return array contains the delimeter at index 1, putting the value to index 2
    *
    * @return array Indexed array: 0=attribute name, 1=attribute value OR ($withDelim=true): 0=attr, 1=delimeter, 2=value
    */
    public static function split_attribute_string($attr, $extended=false, $withDelim=false)
    {
	if ($withDelim) $withDelim = PREG_SPLIT_DELIM_CAPTURE;

        if (!$extended) {
            return preg_split('/(?<!\\\\)(=)/', $attr, 2, $withDelim);
        } else {
            return preg_split('/(?<!\\\\)(>=|<=|>|<|~=|=)/', $attr, 2, $withDelim);
        }
    }

    /**
    * Corrects splitting of dn parts
    *
    * @param array $dn        Raw DN array
    * @param array $separator Separator that was used when splitting
    *
    * @return array Corrected array
    * @access protected
    */
    protected static function correct_dn_splitting($dn = array(), $separator = ',')
    {
        foreach ($dn as $key => $dn_value) {
            $dn_value = $dn[$key]; // refresh value (foreach caches!)
            // if the dn_value is not in attr=value format, then we had an
            // unescaped separator character inside the attr name or the value.
            // We assume, that it was the attribute value.
            // [TODO] To solve this, we might ask the schema. Keep in mind, that UTIL class
            //        must remain independent from the other classes or connections.
            if (!preg_match('/.+(?<!\\\\)=.+/', $dn_value)) {
                unset($dn[$key]);
                if (array_key_exists($key-1, $dn)) {
                    $dn[$key-1] = $dn[$key-1].$separator.$dn_value; // append to previous attr value
                } else {
                    $dn[$key+1] = $dn_value.$separator.$dn[$key+1]; // first element: prepend to next attr name
                }
            }
        }
        return array_values($dn);
    }
}

?>


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
#: LdapCommon.php:64
msgid "A host must be specified."
msgstr ""

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
#: LdapCommon.php:68
msgid "\"basedn\" must be specified."
msgstr ""

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
#: LdapCommon.php:72
msgid "The username attribute must be set."
msgstr ""

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#: LdapCommon.php:130
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr ""

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#: LdapCommon.php:134
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation of StatusNet - LdapCommon to Catalan (català)
# Exported from translatewiki.net
#
# Author: Toniher
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:58+0000\n"
"Language-Team: Catalan <https://translatewiki.net/wiki/Portal:ca>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: ca\n"
"X-Message-Group: #out-statusnet-plugin-ldapcommon\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Cal especificar un servidor central."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "Cal especificar «basedn»."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr ""

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "No s'ha pogut connectar amb el servidor LDAP: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "No s'ha pogut connectar amb el servidor LDAP: %s."


code:
# Translation of StatusNet - LdapCommon to German (Deutsch)
# Exported from translatewiki.net
#
# Author: DaSch
# Author: Inkowik
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:58+0000\n"
"Language-Team: German <https://translatewiki.net/wiki/Portal:de>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: de\n"
"X-Message-Group: #out-statusnet-plugin-ldapcommon\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Ein Host muss angegeben werden."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "\"Basedn\" muss angegeben werden."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "Das Benutzernamens-Attribut muss festgelegt werden."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "Konnte keine Verbindung zum LDAP-Server herstellen: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "Konnte keine Verbindung zum LDAP-Server herstellen: %s."


code:
# Translation of StatusNet - LdapCommon to Spanish (español)
# Exported from translatewiki.net
#
# Author: Armando-Martin
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:58+0000\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Debe especificarse un servidor (host)."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "Debe especificarse \"basedn\"."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "Debe establecer el atributo de nombre de usuario (username)."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "No se pudo conectar con el servidor LDAP: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "No se pudo conectar al servidor LDAP:  %s."


code:
# Translation of StatusNet - LdapCommon to Basque (euskara)
# Exported from translatewiki.net
#
# Author: Artsuaga
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:58+0000\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Host bat definitu behar duzu."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "\"basedn\" definitu behar duzu."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "Erabiltzaile atributua definitu behar duzu."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "Ezin izan da LDAP zerbitzarira konektatu: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "Ezin izan da LDAP zerbitzarira konektatu: %s"


code:
# Translation of StatusNet - LdapCommon to French (français)
# Exported from translatewiki.net
#
# Author: Crochet.david
# Author: Hashar
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:58+0000\n"
"Language-Team: French <https://translatewiki.net/wiki/Portal:fr>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: fr\n"
"X-Message-Group: #out-statusnet-plugin-ldapcommon\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Un hôte LDAP doit être indiqué."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "« basedn » doit être spécifié."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "L'attribut « username » doit être défini."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "Impossible de se connecter au serveur LDAP : %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "Impossible de se connecter au serveur LDAP : %s."


code:
# Translation of StatusNet - LdapCommon to Galician (galego)
# Exported from translatewiki.net
#
# Author: Toliño
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:58+0000\n"
"Language-Team: Galician <https://translatewiki.net/wiki/Portal:gl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: gl\n"
"X-Message-Group: #out-statusnet-plugin-ldapcommon\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Cómpre especificar un provedor de hospedaxe."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "Cómpre especificar \"basedn\"."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "Cómpre definir o atributo \"username\"."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "Non se puido conectar co servidor LDAP: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "Non se puido conectar co servidor LDAP: %s."


code:
# Translation of StatusNet - LdapCommon to Interlingua (interlingua)
# Exported from translatewiki.net
#
# Author: McDutchie
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:58+0000\n"
"Language-Team: Interlingua <https://translatewiki.net/wiki/Portal:ia>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: ia\n"
"X-Message-Group: #out-statusnet-plugin-ldapcommon\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Un host debe esser specificate."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "\"basedn\" debe esser specificate."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "Le attributo \"username\" debe esser definite."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "Non poteva connecter al servitor LDAP: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "Non poteva connecter al servitor LDAP: %s."


code:
# Translation of StatusNet - LdapCommon to Italian (italiano)
# Exported from translatewiki.net
#
# Author: GreenFox
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:58+0000\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Un host deve essere specificato."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "\"basedn\" deve essere specificato."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "L'attributo \"username\" deve essere impostato."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "Impossibile connettersi al server LDAP: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "Impossibile connettersi al server LDAP: %s"


code:
# Translation of StatusNet - LdapCommon to Macedonian (македонски)
# Exported from translatewiki.net
#
# Author: Bjankuloski06
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:59+0000\n"
"Language-Team: Macedonian <https://translatewiki.net/wiki/Portal:mk>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: mk\n"
"X-Message-Group: #out-statusnet-plugin-ldapcommon\n"
"Plural-Forms: nplurals=2; plural=(n == 1 || n%10 == 1) ? 0 : 1;\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Мора да наведете домаќин."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "Мора да наведете „basedn“"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "Мора да се назначи атрибутот за корисничко име."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "Не можам да се поврзам со опслужувачот на LDAP: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "Не можам да се поврзам со опслужувачот на LDAP: %s"


code:
# Translation of StatusNet - LdapCommon to Dutch (Nederlands)
# Exported from translatewiki.net
#
# Author: Siebrand
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:59+0000\n"
"Language-Team: Dutch <https://translatewiki.net/wiki/Portal:nl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: nl\n"
"X-Message-Group: #out-statusnet-plugin-ldapcommon\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Er moet een host opgegeven worden."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "\"basedn\" moet opgegeven worden."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "Het attribuut \"username\" moet ingesteld zijn."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "Het wat niet mogelijk een verbinding te krijgen met de LDAP-server: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""
"Het wat niet mogelijk een verbinding te krijgen met de LDAP-server: %s."


code:
# Translation of StatusNet - LdapCommon to Tagalog (Tagalog)
# Exported from translatewiki.net
#
# Author: AnakngAraw
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - LdapCommon\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-30 11:07+0000\n"
"PO-Revision-Date: 2012-06-30 11:08:59+0000\n"
"Language-Team: Tagalog <https://translatewiki.net/wiki/Portal:tl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2011-06-05 21:50:26+0000\n"
"X-Generator: MediaWiki 1.20alpha (233fc08); Translate 2012-06-21\n"
"X-Translation-Project: translatewiki.net <https://translatewiki.net>\n"
"X-Language-Code: tl\n"
"X-Message-Group: #out-statusnet-plugin-ldapcommon\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "A host must be specified."
msgstr "Dapat tukuyin ang isang tagapagpasinaya."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "\"basedn\" must be specified."
msgstr "Dapat tukuyin ang isang \"basedn\"."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails because of an incorrect configuration.
msgid "The username attribute must be set."
msgstr "Dapat itakda ang isang katangian ng pangalan ng tagagamit."

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s"
msgstr "Hindi makaugnay sa tagapaghain ng LDAP: %s"

#. TRANS: Exception thrown in the LDAP Common plugin when LDAP server is not available.
#. TRANS: %s is the error message.
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "Hindi makaugnay sa tagapaghain ng LDAP: %s."