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$
* @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
    */
    public 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$
* @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
    */
    public function __construct()
    {
        parent::__construct('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 static 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.
    }
}
?>

repo_name: spookie/gnu

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$
* @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$
* @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())
    {
        parent::__construct('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$
* @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 __construct($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$
* @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_callback(
                            "/^\w+=/",
                            function ($matches) {
                                return strtoupper($matches[0]);
                            },
                            $subrdn_v
                        );
                    } else if ($options['casefold'] == 'lower') {
                        $subrdn_v = preg_replace_callback(
                            "/^\w+=/",
                            function ($matches) {
                                return strtolower($matches[0]);
                            },
                            $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_callback(
                        "/^\w+=/",
                        function ($matches) {
                            return strtoupper($matches[0]);
                        },
                        $value
                    );
                } else if ($options['casefold'] == 'lower') {
                    $value = preg_replace_callback(
                        "/^\w+=/",
                        function ($matches) {
                            return strtolower($matches[0]);
                        },
                        $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_callback(
            "/\\\[0-9A-Fa-f]{2}/",
            function ($matches) {
                return chr(hexdec($matches[0]));
            },
            $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: 2019-08-14 14:51+0100\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"
"Language: \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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Afrikaans (http://www.transifex.com/gnu-social/gnu-social/language/af/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: af\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Arabic (http://www.transifex.com/gnu-social/gnu-social/language/ar/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Arabic (Egypt) (http://www.transifex.com/gnu-social/gnu-social/language/ar_EG/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ar_EG\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Asturian (http://www.transifex.com/gnu-social/gnu-social/language/ast/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ast\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Belarusian (Tarask) (http://www.transifex.com/gnu-social/gnu-social/language/be@tarask/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: be@tarask\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Bulgarian (http://www.transifex.com/gnu-social/gnu-social/language/bg/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: bg\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Bengali (India) (http://www.transifex.com/gnu-social/gnu-social/language/bn_IN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: bn_IN\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Breton (http://www.transifex.com/gnu-social/gnu-social/language/br/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: br\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2011 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 09:02+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Catalan (http://www.transifex.com/gnu-social/gnu-social/language/ca/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ca\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\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 "Cal especificar un servidor central."

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

#. 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:133
#, 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.
#: LdapCommon.php:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "No s'ha pogut connectar amb el servidor LDAP: %s."


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Czech (http://www.transifex.com/gnu-social/gnu-social/language/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Danish (http://www.transifex.com/gnu-social/gnu-social/language/da/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: da\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\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:133
#, 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:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2011 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 09:02+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: German (http://www.transifex.com/gnu-social/gnu-social/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\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 "Ein Host muss angegeben werden."

#. TRANS: Exception thrown when initialising the LDAP Common plugin fails
#. because of an incorrect configuration.
#: LdapCommon.php:68
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.
#: LdapCommon.php:72
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.
#: LdapCommon.php:133
#, 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.
#: LdapCommon.php:137
#, php-format
msgid "Could not connect to LDAP server: %s."
msgstr "Konnte keine Verbindung zum LDAP-Server herstellen: %s."