index 68b17919b6de0788ce7730a3ea7443de4b8a5ca2..893d1431d386971e9c26885bb3daf41109d5f324 100644 (file)
if (tests::is_id($_POST['new_limit']) &&
isset($_POST['action']) && $_POST['action']=="newlimit"){
if (tests::is_id($_POST['new_limit']) &&
isset($_POST['action']) && $_POST['action']=="newlimit"){
- session::global_set('size_limit', validate($_POST['new_limit']));
+ session::global_set('size_limit', get_post('new_limit'));
session::set('size_ignore', FALSE);
}
session::set('size_ignore', FALSE);
}
* */
function check_command($cmdline)
{
* */
function check_command($cmdline)
{
+ return(TRUE);
$cmd= preg_replace("/ .*$/", "", $cmdline);
/* Check if command exists in filesystem */
$cmd= preg_replace("/ .*$/", "", $cmdline);
/* Check if command exists in filesystem */
}
}
-/*! \brief Recursion helper for gen_id() */
-function recurse($rule, $variables)
-{
- $result= array();
-
- if (!count($variables)){
- return array($rule);
- }
-
- reset($variables);
- $key= key($variables);
- $val= current($variables);
- unset ($variables[$key]);
-
- foreach($val as $possibility){
- $nrule= str_replace("{$key}", $possibility, $rule);
- $result= array_merge($result, recurse($nrule, $variables));
- }
-
- return ($result);
-}
-
-
-/*! \brief Expands user ID based on possible rules
- *
- * Unroll given rule string by filling in attributes.
- *
- * \param string 'rule' The rule string from gosa.conf.
- * \param array 'attributes' A dictionary of attribute/value mappings
- * \return string Expanded string, still containing the id keyword.
- */
-function expand_id($rule, $attributes)
-{
- /* Check for id rule */
- if(preg_match('/^id(:|#|!)\d+$/',$rule)){
- return (array("{$rule}"));
- }
-
- /* Check for clean attribute */
- if (preg_match('/^%[a-zA-Z0-9]+$/', $rule)){
- $rule= preg_replace('/^%/', '', $rule);
- $val= rewrite(str_replace(' ', '', strtolower($attributes[$rule])));
- return (array($val));
- }
-
- /* Check for attribute with parameters */
- if (preg_match('/^%[a-zA-Z0-9]+\[[0-9-]+\]$/', $rule)){
- $param= preg_replace('/^[^[]+\[([^]]+)]$/', '\\1', $rule);
- $part= preg_replace('/^%/', '', preg_replace('/\[.*$/', '', $rule));
- $val= rewrite(str_replace(' ', '', strtolower($attributes[$part])));
- $start= preg_replace ('/-.*$/', '', $param);
- $stop = preg_replace ('/^[^-]+-/', '', $param);
-
- /* Assemble results */
- $result= array();
- for ($i= $start; $i<= $stop; $i++){
- $result[]= substr($val, 0, $i);
- }
- return ($result);
- }
-
- echo "Error in idGenerator string: don't know how to handle rule $rule.\n";
- return (array($rule));
-}
-
-
-/*! \brief Generate a list of uid proposals based on a rule
- *
- * Unroll given rule string by filling in attributes and replacing
- * all keywords.
- *
- * \param string 'rule' The rule string from gosa.conf.
- * \param array 'attributes' A dictionary of attribute/value mappings
- * \return array List of valid not used uids
- */
-function gen_uids($rule, $attributes)
-{
- global $config;
-
- /* Search for keys and fill the variables array with all
- possible values for that key. */
- $part= "";
- $trigger= false;
- $stripped= "";
- $variables= array();
-
- for ($pos= 0, $l= strlen($rule); $pos < $l; $pos++){
-
- if ($rule[$pos] == "{" ){
- $trigger= true;
- $part= "";
- continue;
- }
-
- if ($rule[$pos] == "}" ){
- $variables[$pos]= expand_id($part, $attributes);
- $stripped.= "{".$pos."}";
- $trigger= false;
- continue;
- }
-
- if ($trigger){
- $part.= $rule[$pos];
- } else {
- $stripped.= $rule[$pos];
- }
- }
-
- /* Recurse through all possible combinations */
- $proposed= recurse($stripped, $variables);
-
- /* Get list of used ID's */
- $ldap= $config->get_ldap_link();
- $ldap->cd($config->current['BASE']);
-
- /* Remove used uids and watch out for id tags */
- $ret= array();
- foreach($proposed as $uid){
-
- /* Check for id tag and modify uid if needed */
- if(preg_match('/\{id(:|!)\d+}/',$uid, $m)){
- $size= preg_replace('/^.*{id(:|!)(\d+)}.*$/', '\\2', $uid);
-
- $start= $m[1]==":"?0:-1;
- for ($i= $start, $p= pow(10,$size)-1; $i < $p; $i++){
- if ($i == -1) {
- $number= "";
- } else {
- $number= sprintf("%0".$size."d", $i+1);
- }
- $res= preg_replace('/{id(:|!)\d+}/', $number, $uid);
-
- $ldap->search("(uid=".preg_replace('/[{}]/', '', $res).")",array('dn'));
- if($ldap->count() == 0){
- $uid= $res;
- break;
- }
- }
-
- /* Remove link if nothing has been found */
- $uid= preg_replace('/{id(:|!)\d+}/', '', $uid);
- }
-
- if(preg_match('/\{id#\d+}/',$uid)){
- $size= preg_replace('/^.*{id#(\d+)}.*$/', '\\1', $uid);
-
- while (true){
- mt_srand((double) microtime()*1000000);
- $number= sprintf("%0".$size."d", mt_rand(0, pow(10, $size)-1));
- $res= preg_replace('/{id#(\d+)}/', $number, $uid);
- $ldap->search("(uid=".preg_replace('/[{}]/', '', $res).")",array('dn'));
- if($ldap->count() == 0){
- $uid= $res;
- break;
- }
- }
-
- /* Remove link if nothing has been found */
- $uid= preg_replace('/{id#\d+}/', '', $uid);
- }
-
- /* Don't assign used ones */
- $ldap->search("(uid=".preg_replace('/[{}]/', '', $uid).")",array('dn'));
- if($ldap->count() == 0){
- /* Add uid, but remove {} first. These are invalid anyway. */
- $ret[]= preg_replace('/[{}]/', '', $uid);
- }
- }
-
- return(array_unique($ret));
-}
-
-
/*! \brief Convert various data sizes to bytes
*
* Given a certain value in the format n(g|m|k), where n
/*! \brief Convert various data sizes to bytes
*
* Given a certain value in the format n(g|m|k), where n
msg_dialog::display(_("Internal error"), sprintf(_("File %s cannot be deleted!"), bold($directory."/".$file)), ERROR_DIALOG);
// This should never be reached
}
msg_dialog::display(_("Internal error"), sprintf(_("File %s cannot be deleted!"), bold($directory."/".$file)), ERROR_DIALOG);
// This should never be reached
}
- } elseif(is_dir($directory."/".$file) &&
- is_writable($directory."/".$file)) {
- // Just recursively delete it
- rmdirRecursive($directory."/".$file);
- }
+ }
}
// We should now create a fresh revision file
clean_smarty_compile_dir($directory);
}
// We should now create a fresh revision file
clean_smarty_compile_dir($directory);
@@ -2896,16 +2720,29 @@ function get_languages($languages_in_own_language = FALSE,$strip_region_tag = FA
* */
function get_post($name)
{
* */
function get_post($name)
{
- if(!isset($_POST[$name])){
- trigger_error("Requested POST value (".$name.") does not exists, you should add a check to prevent this message.");
- return(FALSE);
- }
+ if(!isset($_POST[$name])){
+ trigger_error("Requested POST value (".$name.") does not exists, you should add a check to prevent this message.");
+ return(FALSE);
+ }
- if(get_magic_quotes_gpc()){
- $val = stripcslashes($_POST[$name]);
- }else{
- $val = $_POST[$name];
- }
+ // Handle Posted Arrays
+ $tmp = array();
+ if(is_array($_POST[$name]) && !is_string($_POST[$name])){
+ foreach($_POST[$name] as $key => $val){
+ if(get_magic_quotes_gpc()){
+ $val = stripcslashes($val);
+ }
+ $tmp[$key] = $val;
+ }
+ return($tmp);
+ }else{
+
+ if(get_magic_quotes_gpc()){
+ $val = stripcslashes($_POST[$name]);
+ }else{
+ $val = $_POST[$name];
+ }
+ }
return($val);
}
return($val);
}
}
}
-/*! \brief Change the password of a given DN
- *
- * Change the password of a given DN with the specified hash.
- *
- * \param string 'dn' the DN whose password shall be changed
- * \param string 'password' the password
- * \param int mode
- * \param string 'hash' which hash to use to encrypt it, default is empty
- * for cleartext storage.
- * \return boolean TRUE on success FALSE on error
+/*! \brief Change the password for a given object ($dn).
+ * This method uses the specified hashing method to generate a new password
+ * for the object and it also takes care of sambaHashes, if enabled.
+ * Finally the postmodify hook of the class 'user' will be called, if it is set.
+ *
+ * @param String The DN whose password shall be changed.
+ * @param String The new password.
+ * @param Boolean Skip adding samba hashes to the target (sambaNTPassword,sambaLMPassword)
+ * @param String The hashin method to use, default is the global configured default.
+ * @param String The users old password, this allows script based rollback mechanisms,
+ * the prehook will then be called witch switched newPassword/oldPassword.
+ * @return Boolean TRUE on success else FALSE.
*/
*/
-function change_password ($dn, $password, $mode=0, $hash= "")
+function change_password ($dn, $password, $mode=FALSE, $hash= "", $old_password = "", &$message = "")
{
{
- global $config;
- $newpass= "";
-
- /* Convert to lower. Methods are lowercase */
- $hash= strtolower($hash);
-
- // Get all available encryption Methods
-
- // NON STATIC CALL :)
- $methods = new passwordMethod(session::get('config'),$dn);
- $available = $methods->get_available_methods();
-
- // read current password entry for $dn, to detect the encryption Method
- $ldap = $config->get_ldap_link();
- $ldap->cat ($dn, array("shadowLastChange", "userPassword", "uid"));
- $attrs = $ldap->fetch ();
-
- /* Is ensure that clear passwords will stay clear */
- if($hash == "" && isset($attrs['userPassword'][0]) && !preg_match ("/^{([^}]+)}(.+)/", $attrs['userPassword'][0])){
- $hash = "clear";
- }
-
- // Detect the encryption Method
- if ( (isset($attrs['userPassword'][0]) && preg_match ("/^{([^}]+)}(.+)/", $attrs['userPassword'][0], $matches)) || $hash != ""){
+ global $config;
+ $newpass= "";
- /* Check for supported algorithm */
+ // Not sure, why this is here, but maybe some encryption methods require it.
mt_srand((double) microtime()*1000000);
mt_srand((double) microtime()*1000000);
- /* Extract used hash */
- if ($hash == ""){
- $test = passwordMethod::get_method($attrs['userPassword'][0],$dn);
- } else {
- $test = new $available[$hash]($config,$dn);
- $test->set_hash($hash);
- }
-
- } else {
- // User MD5 by default
- $hash= "md5";
- $test = new $available['md5']($config, $dn);
- }
-
- if($test instanceOf passwordMethod){
+ // Get a list of all available password encryption methods.
+ $methods = new passwordMethod(session::get('config'),$dn);
+ $available = $methods->get_available_methods();
- $deactivated = $test->is_locked($config,$dn);
+ // Fetch the current object data, to be able to detect the current hashing method
+ // and to be able to rollback changes once has an error occured.
+ $ldap = $config->get_ldap_link();
+ $ldap->cat ($dn, array("shadowLastChange", "userPassword","sambaNTPassword","sambaLMPassword", "uid"));
+ $attrs = $ldap->fetch ();
+ $initialAttrs = $attrs;
+
+ // If no hashing method is enforced, then detect what method we've to use.
+ $hash = strtolower($hash);
+ if(empty($hash)){
+
+ // Do we need clear-text password for this object?
+ if(isset($attrs['userPassword'][0]) && !preg_match ("/^{([^}]+)}(.+)/", $attrs['userPassword'][0])){
+ $hash = "clear";
+ $test = new $available[$hash]($config,$dn);
+ $test->set_hash($hash);
+ }
- /* Feed password backends with information */
- $test->dn= $dn;
- $test->attrs= $attrs;
- $newpass= $test->generate_hash($password);
+ // If we've still no valid hashing method detected, then try to extract if from the userPassword attribute.
+ elseif(isset($attrs['userPassword'][0]) && preg_match ("/^{([^}]+)}(.+)/", $attrs['userPassword'][0], $matches)){
+ $test = passwordMethod::get_method($attrs['userPassword'][0],$dn);
+ $hash = $test->get_hash_name();
+ }
- // Update shadow timestamp?
- if (isset($attrs["shadowLastChange"][0])){
- $shadow= (int)(date("U") / 86400);
- } else {
- $shadow= 0;
+ // No current password was found and no hash is enforced, so we've to use the config default here.
+ $hash = $config->get_cfg_value('core','passwordDefaultHash');
+ $test = new $available[$hash]($config,$dn);
+ $test->set_hash($hash);
+ }else{
+ $test = new $available[$hash]($config,$dn);
+ $test->set_hash($hash);
}
}
- // Write back modified entry
- $ldap->cd($dn);
- $attrs= array();
+ // We've now a valid password-method-handle and can create the new password hash or don't we?
+ if(!$test instanceOf passwordMethod){
+ $message = _("Cannot detect password hash!");
+ }else{
- // Not for groups
- if ($mode == 0){
+ // Feed password backends with object information.
+ $test->dn = $dn;
+ $test->attrs = $attrs;
+ $newpass= $test->generate_hash($password);
+ // Do we have to append samba attributes too?
+ // - sambaNTPassword / sambaLMPassword
$tmp = $config->get_cfg_value('core','sambaHashHook');
$tmp = $config->get_cfg_value('core','sambaHashHook');
- if(!empty($tmp)){
-
- // Create SMB Password
+ $attrs= array();
+ if (!$mode && !empty($tmp)){
$attrs= generate_smb_nt_hash($password);
$attrs= generate_smb_nt_hash($password);
-
+ $shadow = (isset($attrs["shadowLastChange"][0]))?(int)(date("U") / 86400):0;
if ($shadow != 0){
$attrs['shadowLastChange']= $shadow;
}
}
if ($shadow != 0){
$attrs['shadowLastChange']= $shadow;
}
}
- }
-
- $attrs['userPassword']= array();
- $attrs['userPassword']= $newpass;
- $ldap->modify($attrs);
+ // Write back the new password hash
+ $ldap->cd($dn);
+ $attrs['userPassword']= $newpass;
- /* Read ! if user was deactivated */
- if($deactivated){
- $test->lock_account($config,$dn);
- }
+ // Prepare a special attribute list, which will be used for event hook calls
+ $attrsEvent = array();
+ foreach($initialAttrs as $name => $value){
+ if(!is_numeric($name))
+ $attrsEvent[$name] = escapeshellarg($value[0]);
+ }
+ $attrsEvent['dn'] = escapeshellarg($initialAttrs['dn']);
+ foreach($attrs as $name => $value){
+ $attrsEvent[$name] = escapeshellarg($value);
+ }
+ $attrsEvent['current_password'] = escapeshellarg($old_password);
+ $attrsEvent['new_password'] = escapeshellarg($password);
+
+ // Call the premodify hook now
+ $passwordPlugin = new password($config,$dn);
+ plugin::callHook($passwordPlugin, 'PREMODIFY', $attrsEvent, $output,$retCode,$error, $directlyPrintError = FALSE);
+ if($retCode === 0 && count($output)){
+ $message = sprintf(_("Pre-event hook reported a problem: %s. Password change canceled!"),implode($output));
+ return(FALSE);
+ }
- new log("modify","users/passwordMethod",$dn,array_keys($attrs),$ldap->get_error());
+ // Perform ldap operations
+ $ldap->modify($attrs);
- if (!$ldap->success()) {
- msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, LDAP_MOD, ERROR_DIALOG));
- } else {
+ // Check if the object was locked before, if it was, lock it again!
+ $deactivated = $test->is_locked($config,$dn);
+ if($deactivated){
+ $test->lock_account($config,$dn);
+ }
- /* Run backend method for change/create */
- if(!$test->set_password($password)){
- return(FALSE);
- }
+ // Check if everything went fine and then call the post event hooks.
+ // If an error occures, then try to rollback the complete actions done.
+ $preRollback = FALSE;
+ $ldapRollback = FALSE;
+ $success = TRUE;
+ if (!$ldap->success()) {
+ new log("modify","users/passwordMethod",$dn,array(),"Password change - ldap modifications! - FAILED");
+ $success =FALSE;
+ $message = msgPool::ldaperror($ldap->get_error(), $dn, LDAP_MOD);
+ $preRollback =TRUE;
+ } else {
- /* Find postmodify entries for this class */
- $command= $config->get_cfg_value("password","postmodify");
+ // Now call the passwordMethod change mechanism.
+ if(!$test->set_password($password)){
+ $ldapRollback = TRUE;
+ $preRollback =TRUE;
+ $success = FALSE;
+ new log("modify","users/passwordMethod",$dn,array(),"Password change - set_password! - FAILED");
+ $message = _("Password change failed!");
+ }else{
+
+ // Execute the password hook
+ plugin::callHook($passwordPlugin, 'POSTMODIFY', $attrsEvent, $output,$retCode,$error, $directlyPrintError = FALSE);
+ if($retCode === 0){
+ if(count($output)){
+ new log("modify","users/passwordMethod",$dn,array(),"Password change - Post modify hook reported! - FAILED!");
+ $message = sprintf(_("Post-event hook reported a problem: %s. Password change canceled!"),implode($output));
+ $ldapRollback = TRUE;
+ $preRollback = TRUE;
+ $success = FALSE;
+ }else{
+ #new log("modify","users/passwordMethod",$dn,array(),"Password change - successfull!");
+ }
+ }else{
+ $ldapRollback = TRUE;
+ $preRollback = TRUE;
+ $success = FALSE;
+ new log("modify","users/passwordMethod",$dn,array(),"Password change - postmodify hook execution! - FAILED");
+ new log("modify","users/passwordMethod",$dn,array(),$error);
+
+ // Call password method again and send in old password to
+ // keep the database consistency
+ $test->set_password($old_password);
+ }
+ }
+ }
- if ($command != ""){
- /* Walk through attribute list */
- $command= preg_replace("/%userPassword/", $password, $command);
- $command= preg_replace("/%dn/", $dn, $command);
+ // Setting the password in the ldap database or further operation failed, we should now execute
+ // the plugins pre-event hook, using switched passwords, new/old password.
+ // This ensures that passwords which were set outside of GOsa, will be reset to its
+ // starting value.
+ if($preRollback){
+ new log("modify","users/passwordMethod",$dn,array(),"Rolling back premodify hook!");
+ $oldpass= $test->generate_hash($old_password);
+ $attrsEvent['current_password'] = escapeshellarg($password);
+ $attrsEvent['new_password'] = escapeshellarg($old_password);
+ foreach(array("userPassword","sambaNTPassword","sambaLMPassword") as $attr){
+ if(isset($initialAttrs[$attr][0])) $attrsEvent[$attr] = $initialAttrs[$attr][0];
+ }
+
+ plugin::callHook($passwordPlugin, 'PREMODIFY', $attrsEvent, $output,$retCode,$error, $directlyPrintError = FALSE);
+ if($retCode === 0 && count($output)){
+ $message = sprintf(_("Pre-event hook reported a problem: %s. Password change canceled!"),implode($output));
+ new log("modify","users/passwordMethod",$dn,array(),"Rolling back premodify hook! - FAILED!");
+ }
+ }
+
+ // We've written the password to the ldap database, but executing the postmodify hook failed.
+ // Now, we've to rollback all password related ldap operations.
+ if($ldapRollback){
+ new log("modify","users/passwordMethod",$dn,array(),"Rolling back ldap modifications!");
+ $attrs = array();
+ foreach(array("userPassword","sambaNTPassword","sambaLMPassword") as $attr){
+ if(isset($initialAttrs[$attr][0])) $attrs[$attr] = $initialAttrs[$attr][0];
+ }
+ $ldap->cd($dn);
+ $ldap->modify($attrs);
+ if(!$ldap->success()){
+ $message = msgPool::ldaperror($ldap->get_error(), $dn, LDAP_MOD);
+ new log("modify","users/passwordMethod",$dn,array(),"Rolling back ldap modifications! - FAILED");
+ }
+ }
- if (check_command($command)){
- @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execute");
- exec($command);
- } else {
- $message= sprintf(_("Command %s specified as post modify action for plugin %s does not exist!"), bold($command), bold("password"));
- msg_dialog::display(_("Configuration error"), $message, ERROR_DIALOG);
+ // Log action.
+ if($success){
+ stats::log('global', 'global', array('users'), $action = 'change_password', $amount = 1, 0, $test->get_hash());
+ new log("modify","users/passwordMethod",$dn,array(),"Password change - successfull!");
+ }else{
+ new log("modify","users/passwordMethod",$dn,array(),"Password change - FAILED!");
}
}
- }
+
+ return($success);
}
}
- return(TRUE);
- }
}
}
return ("");
}
} else {
return ("");
}
} else {
- $tmp= $config->get_cfg_value("core",'sambaHashHook')." ".escapeshellarg($password);
+ $tmp = $config->get_cfg_value("core",'sambaHashHook');
+ $tmp = preg_replace("/%userPassword/", escapeshellarg($password), $tmp);
+ $tmp = preg_replace("/%password/", escapeshellarg($password), $tmp);
@DEBUG (DEBUG_LDAP, __LINE__, __FUNCTION__, __FILE__, $tmp, "Execute");
exec($tmp, $ar);
@DEBUG (DEBUG_LDAP, __LINE__, __FUNCTION__, __FILE__, $tmp, "Execute");
exec($tmp, $ar);
}
}
+
+/*! \brief Detect the special character handling for the currently used ldap database.
+ * For example some convert , to \2C or " to \22.
+ *
+ * @param Config The GOsa configuration object.
+ * @return Array An array containing a character mapping the use.
+ */
+function detectLdapSpecialCharHandling()
+{
+ // The list of chars to test for
+ global $config;
+ if(!$config) return(NULL);
+
+ // In the DN we've to use escaped characters, but the object name (o)
+ // has the be un-escaped.
+ $name = 'GOsaLdapEncoding_,_"_(_)_+';
+ $dnName = 'GOsaLdapEncoding_\,_\"_(_)_\+';
+
+ // Prapare name to be useable in filters
+ $fixed= normalizeLdap(str_replace('\\\\', '\\\\\\', $name));
+ $filterName = str_replace('\\,', '\\\\,', $fixed);
+
+ // Create the target dn
+ $oDN = "o={$dnName},".$config->current['BASE'];
+
+ // Get ldap connection and check if we've already created the character
+ // detection object.
+ $ldapCID = ldap_connect($config->current['SERVER']);
+ ldap_set_option($ldapCID, LDAP_OPT_PROTOCOL_VERSION, 3);
+ ldap_bind($ldapCID, $config->current['ADMINDN'],$config->current['ADMINPASSWORD']);
+ $res = ldap_list($ldapCID, $config->current['BASE'],
+ "(&(o=".$filterName.")(objectClass=organization))",
+ array('dn'));
+
+ // If we haven't created the character-detection object, then create it now.
+ $cnt = ldap_count_entries($ldapCID, $res);
+ if(!$cnt){
+ $obj = array();
+ $obj['objectClass'] = array('top','organization');
+ $obj['o'] = $name;
+ $obj['description'] = 'GOsa character encoding test-object.';
+ if(!@ldap_add($ldapCID, $oDN, $obj)){
+ trigger_error("GOsa couldn't detect the special character handling used by your ldap!");
+ return(NULL);
+ }
+ }
+
+ // Read the character-handling detection entry from the ldap.
+ $res = ldap_list($ldapCID, $config->current['BASE'],
+ "(&(o=".$filterName.")(objectClass=organization))",
+ array('dn','o'));
+ $cnt = ldap_count_entries($ldapCID, $res);
+ if($cnt != 1 || !$res){
+ trigger_error("GOsa couldn't detect the special character handling used by your ldap!");
+ return(NULL);
+ }else{
+
+ // Get the character handling entry from the ldap and check how the
+ // values were written. Compare them with what
+ // we've initially intended to write and create a mapping out
+ // of the results.
+ $re = ldap_first_entry($ldapCID, $res);
+ $attrs = ldap_get_attributes($ldapCID, $re);
+
+ // Extract the interessting characters out of the dn and the
+ // initially used $name for the entry.
+ $mapDNstr = preg_replace("/^o=GOsaLdapEncoding_([^,]*),.*$/","\\1", trim(ldap_get_dn($ldapCID, $re)));
+ $mapDN = preg_split("/_/", $mapDNstr,0, PREG_SPLIT_NO_EMPTY);
+
+ $mapNameStr = preg_replace("/^GOsaLdapEncoding_/","",$dnName);
+ $mapName = preg_split("/_/", $mapNameStr,0, PREG_SPLIT_NO_EMPTY);
+
+ // Create a mapping out of the results.
+ $map = array();
+ foreach($mapName as $key => $entry){
+ $map[$entry] = $mapDN[$key];
+ }
+ return($map);
+ }
+ return(NULL);
+}
+
+
+/*! \brief Replaces placeholder in a given string.
+ * For example:
+ * '%uid@gonicus.de' Replaces '%uid' with 'uid'.
+ * '{%uid[0]@gonicus.de}' Replaces '%uid[0]' with the first char of 'uid'.
+ * '%uid[2-4]@gonicus.de' Replaces '%uid[2-4]' with three chars from 'uid' starting from the second.
+ *
+ * The surrounding {} in example 2 are optional.
+ *
+ * @param String The string to perform the action on.
+ * @param Array An array of replacements.
+ * @return The resulting string.
+ */
+function fillReplacements($str, $attrs, $shellArg = FALSE, $default = "")
+{
+ // Search for '{%...[n-m]}
+ // Get all matching parts of the given string and sort them by
+ // length, to avoid replacing strings like '%uidNumber' with 'uid'
+ // instead of 'uidNumber'; The longest tring at first.
+ preg_match_all('/(\{?%([a-z0-9]+)(\[(([0-9]+)(\-([0-9]+))?)\])?\}?)/i', $str ,$matches, PREG_SET_ORDER);
+ $hits = array();
+ foreach($matches as $match){
+ $hits[strlen($match[2]).$match[0]] = $match;
+ }
+ krsort($hits);
+
+ // Replace the placeholder in the given string now.
+ foreach($hits as $match){
+
+ // Avoid errors about undefined index.
+ $name = $match[2];
+ if(!isset($attrs[$name])) $attrs[$name] = $default;
+
+ // Calculate the replacement
+ $start = (isset($match[5])) ? $match[5] : 0;
+ $end = strlen($attrs[$name]);
+ if(isset($match[5]) && !isset($match[7])){
+ $end = 1;
+ }elseif(isset($match[5]) && isset($match[7])){
+ $end = ($match[7]-$start+1);
+ }
+ $value = substr($attrs[$name], $start, $end);
+
+ // Use values which are valid for shell execution?
+ if($shellArg) $value = escapeshellarg($value);
+
+ // Replace the placeholder within the string.
+ $str = preg_replace("/".preg_quote($match[0],'/')."/", $value, $str);
+ }
+ return($str);
+}
+
+
+/*! \brief Generate a list of uid proposals based on a rule
+ *
+ * Unroll given rule string by filling in attributes and replacing
+ * all keywords.
+ *
+ * \param string 'rule' The rule string from gosa.conf.
+ * \param array 'attributes' A dictionary of attribute/value mappings
+ * \return array List of valid not used uids
+ */
+function gen_uids($rule, $attributes)
+{
+ global $config;
+ $ldap = $config->get_ldap_link();
+ $ldap->cd($config->current['BASE']);
+
+
+ // Strip out non ascii chars
+ foreach($attributes as $name => $value){
+ $value = iconv('UTF-8', 'US-ASCII//TRANSLIT', $value);
+ $value = preg_replace('/[^(\x20-\x7F)]*/','',$value);
+ $attributes[$name] = strtolower($value);
+ }
+
+ // Search for '{%...[n-m]}
+ // Get all matching parts of the given string and sort them by
+ // length, to avoid replacing strings like '%uidNumber' with 'uid'
+ // instead of 'uidNumber'; The longest tring at first.
+ preg_match_all('/(\{?%([a-z0-9]+)(\[(([0-9]+)(\-([0-9]+))?)\])?\}?)/i', $rule ,$matches, PREG_SET_ORDER);
+ $replacements = array();
+ foreach($matches as $match){
+
+ // No start position given, then add the complete value
+ if(!isset($match[5])){
+ $replacements[$match[0]][] = $attributes[$match[2]];
+
+ // Start given but no end, so just add a simple character
+ }elseif(!isset($match[7])){
+ if(isset($attributes[$match[2]][$match[5]])){
+ $replacements[$match[0]][] = $attributes[$match[2]][$match[5]];
+ }
+
+ // Add all values in range
+ }else{
+ $str = "";
+ for($i=$match[5]; $i<= $match[7]; $i++){
+ if(isset($attributes[$match[2]][$i])){
+ $str .= $attributes[$match[2]][$i];
+ $replacements[$match[0]][] = $str;
+ }
+ }
+ }
+ }
+
+ // Create proposal array
+ $rules = array($rule);
+ foreach($replacements as $tag => $values){
+ $rules = gen_uid_proposals($rules, $tag,$values);
+ }
+
+
+ // Search for id tags {id:3} / {id#3}
+ preg_match_all('/\{id(#|:)([0-9])+\}/i', $rule ,$matches, PREG_SET_ORDER);
+ $idReplacements = array();
+ foreach($matches as $match){
+ if(count($match) != 3) continue;
+
+ // Generate random number
+ if($match[1] == '#'){
+ foreach($rules as $id => $ruleStr){
+ $genID = rand(pow(10,$match[2] -1),pow(10, ($match[2])) - 1);
+ $rules[$id] = preg_replace("/".preg_quote($match[0],'/')."/", $genID,$ruleStr);
+ }
+ }
+
+ // Search for next free id
+ if($match[1] == ':'){
+
+ // Walk through rules and replace all occurences of {id:..}
+ foreach($rules as $id => $ruleStr){
+ $genID = 0;
+ $start = TRUE;
+ while($start || $ldap->count()){
+ $start = FALSE;
+ $number= sprintf("%0".$match[2]."d", $genID);
+ $testRule = preg_replace("/".preg_quote($match[0],'/')."/",$number,$ruleStr);
+ $ldap->search('uid='.normalizeLdap($testRule));
+ $genID ++;
+ }
+ $rules[$id] = preg_replace("/".preg_quote($match[0],'/')."/",$number,$ruleStr);
+ }
+ }
+ }
+
+ // Create result set by checking which uid is already used and which is free.
+ $ret = array();
+ foreach($rules as $rule){
+ $ldap->search('uid='.normalizeLdap($rule));
+ if(!$ldap->count()){
+ $ret[] = $rule;
+ }
+ }
+
+ return($ret);
+}
+
+
+function gen_uid_proposals(&$rules, $tag, $values)
+{
+ $newRules = array();
+ foreach($rules as $rule){
+ foreach($values as $value){
+ $newRules[] = preg_replace("/".preg_quote($tag,'/')."/", $value, $rule);
+ }
+ }
+ return($newRules);
+}
+
// vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
?>
// vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
?>