Code

Some backport from trunk
[gosa.git] / gosa-core / include / functions.inc
index c0af25616f106baecc84c51b72c457fe43faa76b..d14308bcb45988897ccb484c7606655a47c14ebe 100644 (file)
 /*! \file
  * Common functions and named definitions. */
 
+/* Define globals for revision comparing */
+$svn_path = '$HeadURL$';
+$svn_revision = '$Revision$';
+
 /* Configuration file location */
 if(!isset($_SERVER['CONFIG_DIR'])){
   define ("CONFIG_DIR", "/etc/gosa");
@@ -68,12 +72,8 @@ define('DES_CBC_MD5',3);
 define('DES3_CBC_MD5',5);
 define('DES3_CBC_SHA1',16);
 
-/* Define globals for revision comparing */
-$svn_path = '$HeadURL$';
-$svn_revision = '$Revision$';
-
 /* Include required files */
-require_once("class_location.inc");
+include_once("class_location.inc");
 require_once ("functions_debug.inc");
 require_once ("accept-to-gettext.inc");
 
@@ -701,17 +701,27 @@ function ldap_expired_account($config, $userdn, $uid)
 
     // Fetch required attributes
     foreach(array('shadowExpire','shadowLastChange','shadowMax','shadowMin',
-                'shadowInactive','shadowWarning') as $attr){
+                'shadowInactive','shadowWarning','sambaKickoffTime') as $attr){
         $$attr = (isset($attrs[$attr][0]))? $attrs[$attr][0] : null;
     }
 
 
+    // Check if the account has reached its kick off limitations.
+    // ---------------------------------------------------------
+    // Once the accout reaches the kick off limit it has expired.
+    if($sambaKickoffTime !== null){
+        if(time() >= $sambaKickoffTime){
+            return(POSIX_ACCOUNT_EXPIRED);
+        }
+    }
+
+
     // Check if the account has expired.
     // ---------------------------------
     // An account is locked/expired once its expiration date has reached (shadowExpire).
     // If the optional attribute (shadowInactive) is set, we've to postpone
     //  the account expiration by the amount of days specified in (shadowInactive).
-    if($shadowExpire != null && $shadowExpire >= $current){
+    if($shadowExpire != null && $shadowExpire <= $current){
 
         // The account seems to be expired, but we've to check 'shadowInactive' additionally.
         // ShadowInactive specifies an amount of days we've to reprieve the user.
@@ -1298,7 +1308,7 @@ function eval_sizelimit()
     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);
     }
 
@@ -1440,7 +1450,9 @@ function get_ou($class,$name)
     global $config;
 
     if(!$config->configRegistry->propertyExists($class,$name)){
-        trigger_error("No department mapping found for type ".$name);
+        if($config->boolValueIsTrue("core","developmentMode")){
+            trigger_error("No department mapping found for type ".$name);
+        }
         return "";
     }
 
@@ -1743,6 +1755,7 @@ function dn2base($dn)
  * */
 function check_command($cmdline)
 {
+  return(TRUE);  
   $cmd= preg_replace("/ .*$/", "", $cmdline);
 
   /* Check if command exists in filesystem */
@@ -1980,179 +1993,6 @@ function netmask_to_bits($netmask)
 }
 
 
-/*! \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
@@ -2338,11 +2178,7 @@ function clean_smarty_compile_dir($directory)
               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);
@@ -2980,136 +2816,218 @@ function get_correct_class_name($cls)
 }
 
 
-/*! \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);
 
-    /* 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);
-  }
+    // Get a list of all available password encryption methods.
+    $methods = new passwordMethod(session::get('config'),$dn);
+    $available = $methods->get_available_methods();
 
-  if($test instanceOf passwordMethod){
-
-    $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');
-        if(!empty($tmp)){
-
-            // Create SMB Password
+        $attrs= array();
+        if (!$mode && !empty($tmp)){
             $attrs= generate_smb_nt_hash($password);
-
-            if ($shadow != 0){
-                $attrs['shadowLastChange']= $shadow;
+            if(!count($attrs) || !is_array($attrs)){
+                msg_dialog::display(tr("Error"),tr("Cannot generate SAMBA hash!"),ERROR_DIALOG);
+                return(FALSE);    
+            }else{
+                $shadow = (isset($attrs["shadowLastChange"][0]))?(int)(date("U") / 86400):0;
+                if ($shadow != 0){
+                    $attrs['shadowLastChange']= $shadow;
+                }
             }
         }
-    }
 
-    $attrs['userPassword']= array();
-    $attrs['userPassword']= $newpass;
+        // Write back the new password hash 
+        $ldap->cd($dn);
+        $attrs['userPassword']= $newpass;
 
-    $ldap->modify($attrs);
-
-    /* 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);
-  }
 }
 
 
@@ -3133,7 +3051,7 @@ function generate_smb_nt_hash($password)
     $hash = $rpc->mksmbhash($password);
     if(!$rpc->success()){
         msg_dialog::display(_("Error"),msgPool::rpcError($rpc->get_error()),ERROR_DIALOG);
-        return("");
+        return(array());
     }
 
   }elseif ($config->get_cfg_value("core","gosaSupportURI") != ""){
@@ -3151,7 +3069,9 @@ function generate_smb_nt_hash($password)
       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);
@@ -3161,7 +3081,7 @@ function generate_smb_nt_hash($password)
 
     if ($hash == "") {
       msg_dialog::display(_("Configuration error"), sprintf(_("Generating SAMBA hash by running %s failed: check %s!"), bold($config->get_cfg_value("core",'sambaHashHook'), bold("sambaHashHook"))), ERROR_DIALOG);
-      return ("");
+      return(array());
     }
   }
 
@@ -3618,6 +3538,9 @@ function image($path, $action= "", $title= "", $align= "middle")
   $label= null;
 
   // Bail out, if there's no style file
+  if(!class_exists('session')){
+    return "";    
+  }
   if(!session::global_is_set("img-styles")){
 
     // Get theme
@@ -3694,5 +3617,310 @@ function bold($str)
 }
 
 
+
+/*! \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);
+
+    // Add lower case placeholders to avoid errors
+    foreach($attrs as $key => $attr) $attrs[strtolower($key)] = $attr;
+
+    // Replace the placeholder in the given string now.
+    foreach($hits as $match){
+
+        // Avoid errors about undefined index.
+        $name = strtolower($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 single character
+        }elseif(!isset($match[7])){
+            if(isset($attributes[$match[2]][$match[5]])){
+                $tmp = " ".$attributes[$match[2]];
+                $replacements[$match[0]][] = trim($tmp[$match[5]]);
+            }
+
+        // Add all values in range
+        }else{
+            $str = "";
+            for($i=$match[5]; $i<= $match[7]; $i++){
+                if(isset($attributes[$match[2]][$i])){
+                    $tmp = " ".$attributes[$match[2]];
+                    $str .= $tmp[$i];
+                    $replacements[$match[0]][] = trim($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);
+}
+
+
+function gen_uuid() 
+{
+    return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+        // 32 bits for "time_low"
+        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
+
+        // 16 bits for "time_mid"
+        mt_rand( 0, 0xffff ),
+
+        // 16 bits for "time_hi_and_version",
+        // four most significant bits holds version number 4
+        mt_rand( 0, 0x0fff ) | 0x4000,
+
+        // 16 bits, 8 bits for "clk_seq_hi_res",
+        // 8 bits for "clk_seq_low",
+        // two most significant bits holds zero and one for variant DCE1.1
+        mt_rand( 0, 0x3fff ) | 0x8000,
+
+        // 48 bits for "node"
+        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
+    );
+}
+
+function gosa_file_name($filename)
+{
+    $tempfile = tempnam(sys_get_temp_dir(), 'GOsa'); 
+    if(move_uploaded_file($filename, $tempfile)){ 
+       return( $tempfile);
+    }
+}
+
+function gosa_file($filename)
+{
+    $tempfile = tempnam(sys_get_temp_dir(), 'GOsa'); 
+    if(move_uploaded_file($filename, $tempfile)){ 
+       return file( $tempfile );
+    }
+}
+
+function gosa_fopen($filename, $mode)
+{
+    $tempfile = tempnam(sys_get_temp_dir(), 'GOsa'); 
+    if(move_uploaded_file($filename, $tempfile)){ 
+       return fopen( $tempfile, $mode );
+    }
+}
+
 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
 ?>