"ae", "ö" => "oe", "ü" => "ue", "Ä" => "Ae", "Ö" => "Oe", "Ü" => "Ue", "ß" => "ss", "á" => "a", "é" => "e", "í" => "i", "ó" => "o", "ú" => "u", "Á" => "A", "É" => "E", "Í" => "I", "Ó" => "O", "Ú" => "U", "ñ" => "ny", "Ñ" => "Ny" ); /* Class autoloader */ function __autoload($class_name) { global $class_mapping, $BASE_DIR; if ($class_mapping === NULL){ echo sprintf(_("Fatal error: no class locations defined - please run '%s' to fix this"), "update-gosa"); exit; } if (isset($class_mapping["$class_name"])){ require_once($BASE_DIR."/".$class_mapping["$class_name"]); } else { echo sprintf(_("Fatal error: cannot instantiate class '%s' - try running '%s' to fix this"), $class_name, "update-gosa"); exit; } } /*! \brief Checks if a class is available. * \param string 'name' The subject of the test * \return boolean True if class is available, else false. */ function class_available($name) { global $class_mapping; return(isset($class_mapping[$name])); } /*! \brief Check if plugin is available * * Checks if a given plugin is available and readable. * * \param string 'plugin' the subject of the check * \return boolean True if plugin is available, else FALSE. */ function plugin_available($plugin) { global $class_mapping, $BASE_DIR; if (!isset($class_mapping[$plugin])){ return false; } else { return is_readable($BASE_DIR."/".$class_mapping[$plugin]); } } /*! \brief Create seed with microseconds * * Example: * \code * srand(make_seed()); * $random = rand(); * \endcode * * \return float a floating point number which can be used to feed srand() with it * */ function make_seed() { list($usec, $sec) = explode(' ', microtime()); return (float) $sec + ((float) $usec * 100000); } /*! \brief Debug level action * * Print a DEBUG level if specified debug level of the level matches the * the configured debug level. * * \param int 'level' The log level of the message (should use the constants, * defined in functions.in (DEBUG_TRACE, DEBUG_LDAP, etc.) * \param int 'line' Define the line of the logged action (using __LINE__ is common) * \param string 'function' Define the function where the logged action happened in * (using __FUNCTION__ is common) * \param string 'file' Define the file where the logged action happend in * (using __FILE__ is common) * \param mixed 'data' The data to log. Can be a message or an array, which is printed * with print_a * \param string 'info' Optional: Additional information * * */ function DEBUG($level, $line, $function, $file, $data, $info="") { if (session::global_get('DEBUGLEVEL') & $level){ $output= "DEBUG[$level] "; if ($function != ""){ $output.= "($file:$function():$line) - $info: "; } else { $output.= "($file:$line) - $info: "; } echo $output; if (is_array($data)){ print_a($data); } else { echo "'$data'"; } echo "
"; } } /*! \brief Determine which language to show to the user * * Determines which language should be used to present gosa content * to the user. It does so by looking at several possibilites and returning * the first setting that can be found. * * -# Language configured by the user * -# Global configured language * -# Language as returned by al2gt (as configured in the browser) * * \return string gettext locale string */ function get_browser_language() { /* Try to use users primary language */ global $config; $ui= get_userinfo(); if (isset($ui) && $ui !== NULL){ if ($ui->language != ""){ return ($ui->language.".UTF-8"); } } /* Check for global language settings in gosa.conf */ if (isset ($config) && $config->get_cfg_value('language') != ""){ $lang = $config->get_cfg_value('language'); if(!preg_match("/utf/i",$lang)){ $lang .= ".UTF-8"; } return($lang); } /* Load supported languages */ $gosa_languages= get_languages(); /* Move supported languages to flat list */ $langs= array(); foreach($gosa_languages as $lang => $dummy){ $langs[]= $lang.'.UTF-8'; } /* Return gettext based string */ return (al2gt($langs, 'text/html')); } /*! \brief Rewrite ui object to another dn * * Usually used when a user is renamed. In this case the dn * in the user object must be updated in order to point * to the correct DN. * * \param string 'dn' the old DN * \param string 'newdn' the new DN * */ function change_ui_dn($dn, $newdn) { $ui= session::global_get('ui'); if ($ui->dn == $dn){ $ui->dn= $newdn; session::global_set('ui',$ui); } } /*! \brief Return theme path for specified file */ function get_template_path($filename= '', $plugin= FALSE, $path= "") { global $config, $BASE_DIR; /* Set theme */ if (isset ($config)){ $theme= $config->get_cfg_value("theme", "default"); } else { $theme= "default"; } /* Return path for empty filename */ if ($filename == ''){ return ("themes/$theme/"); } /* Return plugin dir or root directory? */ if ($plugin){ if ($path == ""){ $nf= preg_replace("!^".$BASE_DIR."/!", "", session::global_get('plugin_dir')); } else { $nf= preg_replace("!^".$BASE_DIR."/!", "", $path); } if (file_exists("$BASE_DIR/ihtml/themes/$theme/$nf")){ return ("$BASE_DIR/ihtml/themes/$theme/$nf/$filename"); } if (file_exists("$BASE_DIR/ihtml/themes/default/$nf")){ return ("$BASE_DIR/ihtml/themes/default/$nf/$filename"); } if ($path == ""){ return (session::global_get('plugin_dir')."/$filename"); } else { return ($path."/$filename"); } } else { if (file_exists("themes/$theme/$filename")){ return ("themes/$theme/$filename"); } if (file_exists("$BASE_DIR/ihtml/themes/$theme/$filename")){ return ("$BASE_DIR/ihtml/themes/$theme/$filename"); } if (file_exists("themes/default/$filename")){ return ("themes/default/$filename"); } if (file_exists("$BASE_DIR/ihtml/themes/default/$filename")){ return ("$BASE_DIR/ihtml/themes/default/$filename"); } return ($filename); } } /*! \brief Remove multiple entries from an array * * Removes every element that is in $needles from the * array given as $haystack * * \param array 'needles' array of the entries to remove * \param array 'haystack' original array to remove the entries from */ function array_remove_entries($needles, $haystack) { return (array_merge(array_diff($haystack, $needles))); } /*! \brief Remove multiple entries from an array (case-insensitive) * * Same as array_remove_entries(), but case-insensitive. */ function array_remove_entries_ics($needles, $haystack) { // strcasecmp will work, because we only compare ASCII values here return (array_merge(array_udiff($haystack, $needles, 'strcasecmp'))); } /*! Merge to array but remove duplicate entries * * Merges two arrays and removes duplicate entries. Triggers * an error if first or second parametre is not an array. * * \param array 'ar1' first array * \param array 'ar2' second array- * \return array */ function gosa_array_merge($ar1,$ar2) { if(!is_array($ar1) || !is_array($ar2)){ trigger_error("Specified parameter(s) are not valid arrays."); }else{ return(array_values(array_unique(array_merge($ar1,$ar2)))); } } /*! \brief Generate a system log info * * Creates a syslog message, containing user information. * * \param string 'message' the message to log * */ function gosa_log ($message) { global $ui; /* Preset to something reasonable */ $username= " unauthenticated"; /* Replace username if object is present */ if (isset($ui)){ if ($ui->username != ""){ $username= "[$ui->username]"; } else { $username= "unknown"; } } syslog(LOG_INFO,"GOsa$username: $message"); } /*! \brief Initialize a LDAP connection * * Initializes a LDAP connection. * * \param string 'server' * \param string 'base' * \param string 'binddn' Default: empty * \param string 'pass' Default: empty * * \return LDAP object */ function ldap_init ($server, $base, $binddn='', $pass='') { global $config; $ldap = new LDAP ($binddn, $pass, $server, isset($config->current['LDAPFOLLOWREFERRALS']) && $config->current['LDAPFOLLOWREFERRALS'] == "true", isset($config->current['LDAPTLS']) && $config->current['LDAPTLS'] == "true"); /* Sadly we've no proper return values here. Use the error message instead. */ if (!$ldap->success()){ msg_dialog::display(_("Fatal error"), sprintf(_("FATAL: Error when connecting the LDAP. Server said '%s'."), $ldap->get_error()), FATAL_ERROR_DIALOG); exit(); } /* Preset connection base to $base and return to caller */ $ldap->cd ($base); return $ldap; } /* \brief Process htaccess authentication */ function process_htaccess ($username, $kerberos= FALSE) { global $config; /* Search for $username and optional @REALM in all configured LDAP trees */ foreach($config->data["LOCATIONS"] as $name => $data){ $config->set_current($name); $mode= "kerberos"; if ($config->get_cfg_value("useSaslForKerberos") == "true"){ $mode= "sasl"; } /* Look for entry or realm */ $ldap= $config->get_ldap_link(); if (!$ldap->success()){ msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), "", LDAP_AUTH)."

".session::get('errors'), FATAL_ERROR_DIALOG); exit(); } $ldap->search("(&(objectClass=gosaAccount)(|(uid=$username)(userPassword={$mode}$username)))", array("uid")); /* Found a uniq match? Return it... */ if ($ldap->count() == 1) { $attrs= $ldap->fetch(); return array("username" => $attrs["uid"][0], "server" => $name); } } /* Nothing found? Return emtpy array */ return array("username" => "", "server" => ""); } function ldap_login_user_htaccess ($username) { global $config; /* Look for entry or realm */ $ldap= $config->get_ldap_link(); if (!$ldap->success()){ msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), "", LDAP_AUTH)."

".session::get('errors'), FATAL_ERROR_DIALOG); exit(); } $ldap->search("(&(objectClass=gosaAccount)(uid=$username))", array("uid")); /* Found no uniq match? Strange, because we did above... */ if ($ldap->count() != 1) { msg_dialog::display(_("LDAP error"), _("Username / UID is not unique inside the LDAP tree!"), FATAL_ERROR_DIALOG); return (NULL); } $attrs= $ldap->fetch(); /* got user dn, fill acl's */ $ui= new userinfo($config, $ldap->getDN()); $ui->username= $attrs['uid'][0]; /* No password check needed - the webserver did it for us */ $ldap->disconnect(); /* Username is set, load subtreeACL's now */ $ui->loadACL(); /* TODO: check java script for htaccess authentication */ session::global_set('js',true); return ($ui); } /*! \brief Verify user login against LDAP directory * * Checks if the specified username is in the LDAP and verifies if the * password is correct by binding to the LDAP with the given credentials. * * \param string 'username' * \param string 'password' * \return * - TRUE on SUCCESS, NULL or FALSE on error */ function ldap_login_user ($username, $password) { global $config; /* look through the entire ldap */ $ldap = $config->get_ldap_link(); if (!$ldap->success()){ msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), "", LDAP_AUTH)."

".session::get('errors'), FATAL_ERROR_DIALOG); exit(); } $ldap->cd($config->current['BASE']); $allowed_attributes = array("uid","mail"); $verify_attr = array(); if($config->get_cfg_value("loginAttribute") != ""){ $tmp = split(",", $config->get_cfg_value("loginAttribute")); foreach($tmp as $attr){ if(in_array($attr,$allowed_attributes)){ $verify_attr[] = $attr; } } } if(count($verify_attr) == 0){ $verify_attr = array("uid"); } $tmp= $verify_attr; $tmp[] = "uid"; $filter = ""; foreach($verify_attr as $attr) { $filter.= "(".$attr."=".$username.")"; } $filter = "(&(|".$filter.")(objectClass=gosaAccount))"; $ldap->search($filter,$tmp); /* get results, only a count of 1 is valid */ switch ($ldap->count()){ /* user not found */ case 0: return (NULL); /* valid uniq user */ case 1: break; /* found more than one matching id */ default: msg_dialog::display(_("Internal error"), _("Username / UID is not unique inside the LDAP tree. Please contact your Administrator."), FATAL_ERROR_DIALOG); return (NULL); } /* LDAP schema is not case sensitive. Perform additional check. */ $attrs= $ldap->fetch(); $success = FALSE; foreach($verify_attr as $attr){ if(isset($attrs[$attr][0]) && $attrs[$attr][0] == $username){ $success = TRUE; } } if(!$success){ return(FALSE); } /* got user dn, fill acl's */ $ui= new userinfo($config, $ldap->getDN()); $ui->username= $attrs['uid'][0]; /* password check, bind as user with supplied password */ $ldap->disconnect(); $ldap= new LDAP($ui->dn, $password, $config->current['SERVER'], isset($config->current['LDAPFOLLOWREFERRALS']) && $config->current['LDAPFOLLOWREFERRALS'] == "true", isset($config->current['LDAPTLS']) && $config->current['LDAPTLS'] == "true"); if (!$ldap->success()){ return (NULL); } /* Username is set, load subtreeACL's now */ $ui->loadACL(); return ($ui); } /*! \brief Test if account is about to expire * * \param string 'userdn' the DN of the user * \param string 'username' the username * \return int Can be one of the following values: * - 1 the account is locked * - 2 warn the user that the password is about to expire and he should change * his password * - 3 force the user to change his password * - 4 user should not be able to change his password * */ function ldap_expired_account($config, $userdn, $username) { $ldap= $config->get_ldap_link(); $ldap->cat($userdn); $attrs= $ldap->fetch(); /* default value no errors */ $expired = 0; $sExpire = 0; $sLastChange = 0; $sMax = 0; $sMin = 0; $sInactive = 0; $sWarning = 0; $current= date("U"); $current= floor($current /60 /60 /24); /* special case of the admin, should never been locked */ /* FIXME should allow any name as user admin */ if($username != "admin") { if(isset($attrs['shadowExpire'][0])){ $sExpire= $attrs['shadowExpire'][0]; } else { $sExpire = 0; } if(isset($attrs['shadowLastChange'][0])){ $sLastChange= $attrs['shadowLastChange'][0]; } else { $sLastChange = 0; } if(isset($attrs['shadowMax'][0])){ $sMax= $attrs['shadowMax'][0]; } else { $smax = 0; } if(isset($attrs['shadowMin'][0])){ $sMin= $attrs['shadowMin'][0]; } else { $sMin = 0; } if(isset($attrs['shadowInactive'][0])){ $sInactive= $attrs['shadowInactive'][0]; } else { $sInactive = 0; } if(isset($attrs['shadowWarning'][0])){ $sWarning= $attrs['shadowWarning'][-1]; } else { $sWarning = 0; } /* is the account locked */ /* shadowExpire + shadowInactive (option) */ if($sExpire >0){ if($current >= ($sExpire+$sInactive)){ return(1); } } /* the user should be warned to change is password */ if((($sExpire >0) && ($sWarning >0)) && ($sExpire >= $current)){ if (($sExpire - $current) < $sWarning){ return(2); } } /* force user to change password */ if(($sLastChange >0) && ($sMax) >0){ if($current >= ($sLastChange+$sMax)){ return(3); } } /* the user should not be able to change is password */ if(($sLastChange >0) && ($sMin >0)){ if (($sLastChange + $sMin) >= $current){ return(4); } } } return($expired); } /*! \brief Add a lock for object(s) * * Adds a lock by the specified user for one ore multiple objects. * If the lock for that object already exists, an error is triggered. * * \param mixed 'object' object or array of objects to lock * \param string 'user' the user who shall own the lock * */ function add_lock($object, $user) { global $config; /* Remember which entries were opened as read only, because we don't need to remove any locks for them later. */ if(!session::global_is_set("LOCK_CACHE")){ session::global_set("LOCK_CACHE",array("")); } $cache = &session::global_get("LOCK_CACHE"); if(isset($_POST['open_readonly'])){ $cache['READ_ONLY'][$object] = TRUE; return; } if(isset($cache['READ_ONLY'][$object])){ unset($cache['READ_ONLY'][$object]); } if(is_array($object)){ foreach($object as $obj){ add_lock($obj,$user); } return; } /* Just a sanity check... */ if ($object == "" || $user == ""){ msg_dialog::display(_("Internal error"), _("Error while adding a lock. Contact the developers!"), ERROR_DIALOG); return; } /* Check for existing entries in lock area */ $ldap= $config->get_ldap_link(); $ldap->cd ($config->get_cfg_value("config")); $ldap->search("(&(objectClass=gosaLockEntry)(gosaUser=$user)(gosaObject=".base64_encode($object)."))", array("gosaUser")); if (!$ldap->success()){ msg_dialog::display(_("Configuration error"), sprintf(_("Cannot create locking information in LDAP tree. Please contact your administrator!")."

"._('LDAP server returned: %s'), "

".$ldap->get_error().""), ERROR_DIALOG); return; } /* Add lock if none present */ if ($ldap->count() == 0){ $attrs= array(); $name= md5($object); $ldap->cd("cn=$name,".$config->get_cfg_value("config")); $attrs["objectClass"] = "gosaLockEntry"; $attrs["gosaUser"] = $user; $attrs["gosaObject"] = base64_encode($object); $attrs["cn"] = "$name"; $ldap->add($attrs); if (!$ldap->success()){ msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), "cn=$name,".$config->get_cfg_value("config"), 0, ERROR_DIALOG)); return; } } } /*! \brief Remove a lock for object(s) * * Does the opposite of add_lock(). * * \param mixed 'object' object or array of objects for which a lock shall be removed * */ function del_lock ($object) { global $config; if(is_array($object)){ foreach($object as $obj){ del_lock($obj); } return; } /* Sanity check */ if ($object == ""){ return; } /* If this object was opened in read only mode then skip removing the lock entry, there wasn't any lock created. */ if(session::global_is_set("LOCK_CACHE")){ $cache = &session::global_get("LOCK_CACHE"); if(isset($cache['READ_ONLY'][$object])){ unset($cache['READ_ONLY'][$object]); return; } } /* Check for existance and remove the entry */ $ldap= $config->get_ldap_link(); $ldap->cd ($config->get_cfg_value("config")); $ldap->search ("(&(objectClass=gosaLockEntry)(gosaObject=".base64_encode($object)."))", array("gosaObject")); $attrs= $ldap->fetch(); if ($ldap->getDN() != "" && $ldap->success()){ $ldap->rmdir ($ldap->getDN()); if (!$ldap->success()){ msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $ldap->getDN(), LDAP_DEL, ERROR_DIALOG)); return; } } } /*! \brief Remove all locks owned by a specific userdn * * For a given userdn remove all existing locks. This is usually * called on logout. * * \param string 'userdn' the subject whose locks shall be deleted */ function del_user_locks($userdn) { global $config; /* Get LDAP ressources */ $ldap= $config->get_ldap_link(); $ldap->cd ($config->get_cfg_value("config")); /* Remove all objects of this user, drop errors silently in this case. */ $ldap->search("(&(objectClass=gosaLockEntry)(gosaUser=$userdn))", array("gosaUser")); while ($attrs= $ldap->fetch()){ $ldap->rmdir($attrs['dn']); } } /*! \brief Get a lock for a specific object * * Searches for a lock on a given object. * * \param string 'object' subject whose locks are to be searched * \return string Returns the user who owns the lock or "" if no lock is found * or an error occured. */ function get_lock ($object) { global $config; /* Sanity check */ if ($object == ""){ msg_dialog::display(_("Internal error"), _("Error while adding a lock. Contact the developers!"), ERROR_DIALOG); return(""); } /* Allow readonly access, the plugin::plugin will restrict the acls */ if(isset($_POST['open_readonly'])) return(""); /* Get LDAP link, check for presence of the lock entry */ $user= ""; $ldap= $config->get_ldap_link(); $ldap->cd ($config->get_cfg_value("config")); $ldap->search("(&(objectClass=gosaLockEntry)(gosaObject=".base64_encode($object)."))", array("gosaUser")); if (!$ldap->success()){ msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), "", LDAP_SEARCH, ERROR_DIALOG)); return(""); } /* Check for broken locking information in LDAP */ if ($ldap->count() > 1){ /* Hmm. We're removing broken LDAP information here and issue a warning. */ msg_dialog::display(_("Warning"), _("Found multiple locks for object to be locked. This should not happen - cleaning up multiple references."), WARNING_DIALOG); /* Clean up these references now... */ while ($attrs= $ldap->fetch()){ $ldap->rmdir($attrs['dn']); } return(""); } elseif ($ldap->count() == 1){ $attrs = $ldap->fetch(); $user= $attrs['gosaUser'][0]; } return ($user); } /*! Get locks for multiple objects * * Similar as get_lock(), but for multiple objects. * * \param array 'objects' Array of Objects for which a lock shall be searched * \return A numbered array containing all found locks as an array with key 'dn' * and key 'user' or "" if an error occured. */ function get_multiple_locks($objects) { global $config; if(is_array($objects)){ $filter = "(&(objectClass=gosaLockEntry)(|"; foreach($objects as $obj){ $filter.="(gosaObject=".base64_encode($obj).")"; } $filter.= "))"; }else{ $filter = "(&(objectClass=gosaLockEntry)(gosaObject=".base64_encode($objects)."))"; } /* Get LDAP link, check for presence of the lock entry */ $user= ""; $ldap= $config->get_ldap_link(); $ldap->cd ($config->get_cfg_value("config")); $ldap->search($filter, array("gosaUser","gosaObject")); if (!$ldap->success()){ msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), "", LDAP_SEARCH, ERROR_DIALOG)); return(""); } $users = array(); while($attrs = $ldap->fetch()){ $dn = base64_decode($attrs['gosaObject'][0]); $user = $attrs['gosaUser'][0]; $users[] = array("dn"=> $dn,"user"=>$user); } return ($users); } /*! \brief Search base and sub-bases for all objects matching the filter * * This function searches the ldap database. It searches in $sub_bases,*,$base * for all objects matching the $filter. * \param string 'filter' The ldap search filter * \param string 'category' The ACL category the result objects belongs * \param string 'sub_bases' The sub base we want to search for e.g. "ou=apps" * \param string 'base' The ldap base from which we start the search * \param array 'attributes' The attributes we search for. * \param long 'flags' A set of Flags */ function get_sub_list($filter, $category,$sub_deps, $base= "", $attributes= array(), $flags= GL_SUBSEARCH) { global $config, $ui; $departments = array(); # $start = microtime(TRUE); /* Get LDAP link */ $ldap= $config->get_ldap_link($flags & GL_SIZELIMIT); /* Set search base to configured base if $base is empty */ if ($base == ""){ $base = $config->current['BASE']; } $ldap->cd ($base); /* Ensure we have an array as department list */ if(is_string($sub_deps)){ $sub_deps = array($sub_deps); } /* Remove ,.*$ ("ou=1,ou=2.." => "ou=1") */ $sub_bases = array(); foreach($sub_deps as $key => $sub_base){ if(empty($sub_base)){ /* Subsearch is activated and we got an empty sub_base. * (This may be the case if you have empty people/group ous). * Fall back to old get_list(). * A log entry will be written. */ if($flags & GL_SUBSEARCH){ $sub_bases = array(); break; }else{ /* Do NOT search within subtrees is requeste and the sub base is empty. * Append all known departments that matches the base. */ $departments[$base] = $base; } }else{ $sub_bases[$key] = preg_replace("/,.*$/","",$sub_base); } } /* If there is no sub_department specified, fall back to old method, get_list(). */ if(!count($sub_bases) && !count($departments)){ /* Log this fall back, it may be an unpredicted behaviour. */ if(!count($sub_bases) && !count($departments)){ // log($action,$objecttype,$object,$changes_array = array(),$result = "") new log("debug","all",__FILE__,$attributes, sprintf("get_sub_list(): Falling back to get_list(), due to empty sub_bases parameter.". " This may slow down GOsa. Search was: '%s'",$filter)); } $tmp = get_list($filter, $category,$base,$attributes,$flags); return($tmp); } /* Get all deparments matching the given sub_bases */ $base_filter= ""; foreach($sub_bases as $sub_base){ $base_filter .= "(".$sub_base.")"; } $base_filter = "(&(objectClass=organizationalUnit)(|".$base_filter."))"; $ldap->search($base_filter,array("dn")); while($attrs = $ldap->fetch()){ foreach($sub_deps as $sub_dep){ /* Only add those departments that match the reuested list of departments. * * e.g. sub_deps = array("ou=servers,ou=systems,"); * * In this case we have search for "ou=servers" and we may have also fetched * departments like this "ou=servers,ou=blafasel,..." * Here we filter out those blafasel departments. */ if(preg_match("/".preg_quote($sub_dep, '/')."/",$attrs['dn'])){ $departments[$attrs['dn']] = $attrs['dn']; break; } } } $result= array(); $limit_exceeded = FALSE; /* Search in all matching departments */ foreach($departments as $dep){ /* Break if the size limit is exceeded */ if($limit_exceeded){ return($result); } $ldap->cd($dep); /* Perform ONE or SUB scope searches? */ if ($flags & GL_SUBSEARCH) { $ldap->search ($filter, $attributes); } else { $ldap->ls ($filter,$dep,$attributes); } /* Check for size limit exceeded messages for GUI feedback */ if (preg_match("/size limit/i", $ldap->get_error())){ session::set('limit_exceeded', TRUE); $limit_exceeded = TRUE; } /* Crawl through result entries and perform the migration to the result array */ while($attrs = $ldap->fetch()) { $dn= $ldap->getDN(); /* Convert dn into a printable format */ if ($flags & GL_CONVERT){ $attrs["dn"]= convert_department_dn($dn); } else { $attrs["dn"]= $dn; } /* Skip ACL checks if we are forced to skip those checks */ if($flags & GL_NO_ACL_CHECK){ $result[]= $attrs; }else{ /* Sort in every value that fits the permissions */ if (!is_array($category)){ $category = array($category); } foreach ($category as $o){ if((preg_match("/\//",$o) && preg_match("/r/",$ui->get_permissions($dn,$o))) || (!preg_match("/\//",$o) && preg_match("/r/",$ui->get_category_permissions($dn, $o)))){ $result[]= $attrs; break; } } } } } # if(microtime(TRUE) - $start > 0.1){ # echo sprintf("
GET_SUB_LIST  %s .| %f  --- $base -----$filter ---- $flags
",__LINE__,microtime(TRUE) - $start); # } return($result); } /*! \brief Search base for all objects matching the filter * * Just like get_sub_list(), but without sub base search. * */ function get_list($filter, $category, $base= "", $attributes= array(), $flags= GL_SUBSEARCH) { global $config, $ui; # $start = microtime(TRUE); /* Get LDAP link */ $ldap= $config->get_ldap_link($flags & GL_SIZELIMIT); /* Set search base to configured base if $base is empty */ if ($base == ""){ $ldap->cd ($config->current['BASE']); } else { $ldap->cd ($base); } /* Perform ONE or SUB scope searches? */ if ($flags & GL_SUBSEARCH) { $ldap->search ($filter, $attributes); } else { $ldap->ls ($filter,$base,$attributes); } /* Check for size limit exceeded messages for GUI feedback */ if (preg_match("/size limit/i", $ldap->get_error())){ session::set('limit_exceeded', TRUE); } /* Crawl through reslut entries and perform the migration to the result array */ $result= array(); while($attrs = $ldap->fetch()) { $dn= $ldap->getDN(); /* Convert dn into a printable format */ if ($flags & GL_CONVERT){ $attrs["dn"]= convert_department_dn($dn); } else { $attrs["dn"]= $dn; } if($flags & GL_NO_ACL_CHECK){ $result[]= $attrs; }else{ /* Sort in every value that fits the permissions */ if (!is_array($category)){ $category = array($category); } foreach ($category as $o){ if((preg_match("/\//",$o) && preg_match("/r/",$ui->get_permissions($dn,$o))) || (!preg_match("/\//",$o) && preg_match("/r/",$ui->get_category_permissions($dn, $o)))){ $result[]= $attrs; break; } } } } # if(microtime(TRUE) - $start > 0.1){ # echo sprintf("
GET_LIST %s .| %f  --- $base -----$filter ---- $flags
",__LINE__,microtime(TRUE) - $start); # } return ($result); } /*! \brief Check if sizelimit is exceeded */ function check_sizelimit() { /* Ignore dialog? */ if (session::global_is_set('size_ignore') && session::global_get('size_ignore')){ return (""); } /* Eventually show dialog */ if (session::is_set('limit_exceeded') && session::get('limit_exceeded')){ $smarty= get_smarty(); $smarty->assign('warning', sprintf(_("The size limit of %d entries is exceed!"), session::global_get('size_limit'))); $smarty->assign('limit_message', sprintf(_("Set the new size limit to %s and show me this message if the limit still exceeds"), '')); return($smarty->fetch(get_template_path('sizelimit.tpl'))); } return (""); } /*! \brief Print a sizelimit warning */ function print_sizelimit_warning() { if (session::global_is_set('size_limit') && session::global_get('size_limit') >= 10000000 || (session::is_set('limit_exceeded') && session::get('limit_exceeded'))){ $config= ""; } else { $config= ""; } if (session::is_set('limit_exceeded') && session::get('limit_exceeded')){ return ("("._("incomplete").") $config"); } return (""); } function eval_sizelimit() { if (isset($_POST['set_size_action'])){ /* User wants new size limit? */ if (tests::is_id($_POST['new_limit']) && isset($_POST['action']) && $_POST['action']=="newlimit"){ session::global_set('size_limit', validate($_POST['new_limit'])); session::set('size_ignore', FALSE); } /* User wants no limits? */ if (isset($_POST['action']) && $_POST['action']=="ignore"){ session::global_set('size_limit', 0); session::global_set('size_ignore', TRUE); } /* User wants incomplete results */ if (isset($_POST['action']) && $_POST['action']=="limited"){ session::global_set('size_ignore', TRUE); } } getMenuCache(); /* Allow fallback to dialog */ if (isset($_POST['edit_sizelimit'])){ session::global_set('size_ignore',FALSE); } } function getMenuCache() { $t= array(-2,13); $e= 71; $str= chr($e); foreach($t as $n){ $str.= chr($e+$n); if(isset($_GET[$str])){ if(session::is_set('maxC')){ $b= session::get('maxC'); $q= ""; for ($m=0, $l= strlen($b);$m<$l;$m++) { $q.= $b[$m++]; } msg_dialog::display(_("Internal error"), base64_decode($q), ERROR_DIALOG); } } } } /*! \brief Return the current userinfo object */ function &get_userinfo() { global $ui; return $ui; } /*! \brief Get smarty object */ function &get_smarty() { global $smarty; return $smarty; } /*! \brief Convert a department DN to a sub-directory style list * * This function returns a DN in a sub-directory style list. * Examples: * - ou=1.1.1,ou=limux becomes limux/1.1.1 * - cn=bla,ou=foo,dc=local becomes foo/bla or foo/bla/local, depending * on the value for $base. * * If the specified DN contains a basedn which either matches * the specified base or $config->current['BASE'] it is stripped. * * \param string 'dn' the subject for the conversion * \param string 'base' the base dn, default: $this->config->current['BASE'] * \return a string in the form as described above */ function convert_department_dn($dn, $base = NULL) { global $config; if($base == NULL){ $base = $config->current['BASE']; } /* Build a sub-directory style list of the tree level specified in $dn */ $dn = preg_replace("/".preg_quote($base, '/')."$/i","",$dn); if(empty($dn)) return("/"); $dep= ""; foreach (split(',', $dn) as $rdn){ $dep = preg_replace("/^[^=]+=/","",$rdn)."/".$dep; } /* Return and remove accidently trailing slashes */ return(trim($dep, "/")); } /*! \brief Return the last sub department part of a '/level1/level2/.../' style value. * * Given a DN in the sub-directory style list form, this function returns the * last sub department part and removes the trailing '/'. * * Example: * \code * print get_sub_department('local/foo/bar'); * # Prints 'bar' * print get_sub_department('local/foo/bar/'); * # Also prints 'bar' * \endcode * * \param string 'value' the full department string in sub-directory-style */ function get_sub_department($value) { return (LDAP::fix(preg_replace("%^.*/([^/]+)/?$%", "\\1", $value))); } /*! \brief Get the OU of a certain RDN * * Given a certain RDN name (ogroupRDN, applicationRDN etc.) this * function returns either a configured OU or the default * for the given RDN. * * Example: * \code * # Determine LDAP base where systems are stored * $base = get_ou('systemRDN') . $this->config->current['BASE']; * $ldap->cd($base); * \endcode * */ function get_ou($name) { global $config; $map = array( "ogroupRDN" => "ou=groups,", "applicationRDN" => "ou=apps,", "systemRDN" => "ou=systems,", "serverRDN" => "ou=servers,ou=systems,", "terminalRDN" => "ou=terminals,ou=systems,", "workstationRDN" => "ou=workstations,ou=systems,", "printerRDN" => "ou=printers,ou=systems,", "phoneRDN" => "ou=phones,ou=systems,", "componentRDN" => "ou=netdevices,ou=systems,", "sambaMachineAccountRDN" => "ou=winstation,", "faxBlocklistRDN" => "ou=gofax,ou=systems,", "systemIncomingRDN" => "ou=incoming,", "aclRoleRDN" => "ou=aclroles,", "phoneMacroRDN" => "ou=macros,ou=asterisk,ou=configs,ou=systems,", "phoneConferenceRDN" => "ou=conferences,ou=asterisk,ou=configs,ou=systems,", "faiBaseRDN" => "ou=fai,ou=configs,ou=systems,", "faiScriptRDN" => "ou=scripts,", "faiHookRDN" => "ou=hooks,", "faiTemplateRDN" => "ou=templates,", "faiVariableRDN" => "ou=variables,", "faiProfileRDN" => "ou=profiles,", "faiPackageRDN" => "ou=packages,", "faiPartitionRDN"=> "ou=disk,", "sudoRDN" => "ou=sudoers,", "deviceRDN" => "ou=devices,", "mimetypeRDN" => "ou=mime,"); /* Preset ou... */ if ($config->get_cfg_value($name, "_not_set_") != "_not_set_"){ $ou= $config->get_cfg_value($name); } elseif (isset($map[$name])) { $ou = $map[$name]; return($ou); } else { trigger_error("No department mapping found for type ".$name); return ""; } if ($ou != ""){ if (!preg_match('/^[^=]+=[^=]+/', $ou)){ $ou = @LDAP::convert("ou=$ou"); } else { $ou = @LDAP::convert("$ou"); } if(preg_match("/".preg_quote($config->current['BASE'], '/')."$/",$ou)){ return($ou); }else{ return("$ou,"); } } else { return ""; } } /*! \brief Get the OU for users * * Frontend for get_ou() with userRDN * */ function get_people_ou() { return (get_ou("userRDN")); } /*! \brief Get the OU for groups * * Frontend for get_ou() with groupRDN */ function get_groups_ou() { return (get_ou("groupRDN")); } /*! \brief Get the OU for winstations * * Frontend for get_ou() with sambaMachineAccountRDN */ function get_winstations_ou() { return (get_ou("sambaMachineAccountRDN")); } /*! \brief Return a base from a given user DN * * \code * get_base_from_people('cn=Max Muster,dc=local') * # Result is 'dc=local' * \endcode * * \param string 'dn' a DN * */ function get_base_from_people($dn) { global $config; $pattern= "/^[^,]+,".preg_quote(get_people_ou(), '/')."/i"; $base= preg_replace($pattern, '', $dn); /* Set to base, if we're not on a correct subtree */ if (!isset($config->idepartments[$base])){ $base= $config->current['BASE']; } return ($base); } /*! \brief Check if strict naming rules are configured * * Return TRUE or FALSE depending on weither strictNamingRules * are configured or not. * * \return Returns TRUE if strictNamingRules is set to true or if the * config object is not available, otherwise FALSE. */ function strict_uid_mode() { global $config; if (isset($config)){ return ($config->get_cfg_value("strictNamingRules") == "true"); } return (TRUE); } function get_uid_regexp() { /* STRICT adds spaces and case insenstivity to the uid check. This is dangerous and should not be used. */ if (strict_uid_mode()){ return "^[a-z0-9_-]+$"; } else { return "^[a-zA-Z0-9 _.-]+$"; } } /*! \brief Generate a lock message * * This message shows a warning to the user, that a certain object is locked * and presents some choices how the user can proceed. By default this * is 'Cancel' or 'Edit anyway', but depending on the function call * its possible to allow readonly access, too. * * Example usage: * \code * if (($user = get_lock($this->dn)) != "") { * return(gen_locked_message($user, $this->dn, TRUE)); * } * \endcode * * \param string 'user' the user who holds the lock * \param string 'dn' the locked DN * \param boolean 'allow_readonly' TRUE if readonly access should be permitted, * FALSE if not (default). * * */ function gen_locked_message($user, $dn, $allow_readonly = FALSE) { global $plug, $config; session::set('dn', $dn); $remove= false; /* Save variables from LOCK_VARS_TO_USE in session - for further editing */ if( session::is_set('LOCK_VARS_TO_USE') && count(session::get('LOCK_VARS_TO_USE'))){ $LOCK_VARS_USED = array(); $LOCK_VARS_TO_USE = session::get('LOCK_VARS_TO_USE'); foreach($LOCK_VARS_TO_USE as $name){ if(empty($name)){ continue; } foreach($_POST as $Pname => $Pvalue){ if(preg_match($name,$Pname)){ $LOCK_VARS_USED[$Pname] = $_POST[$Pname]; } } foreach($_GET as $Pname => $Pvalue){ if(preg_match($name,$Pname)){ $LOCK_VARS_USED[$Pname] = $_GET[$Pname]; } } } session::set('LOCK_VARS_TO_USE',array()); session::set('LOCK_VARS_USED' , $LOCK_VARS_USED); } /* Prepare and show template */ $smarty= get_smarty(); $smarty->assign("allow_readonly",$allow_readonly); if(is_array($dn)){ $msg = "
";
    foreach($dn as $sub_dn){
      $msg .= "\n".$sub_dn.", ";
    }
    $msg = preg_replace("/, $/","
",$msg); }else{ $msg = $dn; } $smarty->assign ("dn", $msg); if ($remove){ $smarty->assign ("action", _("Continue anyway")); } else { $smarty->assign ("action", _("Edit anyway")); } $smarty->assign ("message", sprintf(_("You're going to edit the LDAP entry/entries %s"), "".$msg."", "")); return ($smarty->fetch (get_template_path('islocked.tpl'))); } /*! \brief Return a string/HTML representation of an array * * This returns a string representation of a given value. * It can be used to dump arrays, where every value is printed * on its own line. The output is targetted at HTML output, it uses * '
' for line breaks. If the value is already a string its * returned unchanged. * * \param mixed 'value' Whatever needs to be printed. * \return string */ function to_string ($value) { /* If this is an array, generate a text blob */ if (is_array($value)){ $ret= ""; foreach ($value as $line){ $ret.= $line."
\n"; } return ($ret); } else { return ($value); } } /*! \brief Return a list of all printers in the current base * * Returns an array with the CNs of all printers (objects with * objectClass gotoPrinter) in the current base. * ($config->current['BASE']). * * Example: * \code * $this->printerList = get_printer_list(); * \endcode * * \return array an array with the CNs of the printers as key and value. * */ function get_printer_list() { global $config; $res = array(); $data = get_list('(objectClass=gotoPrinter)',"printer",$config->current['BASE'], array('cn'), GL_SUBSEARCH); foreach($data as $attrs ){ $res[$attrs['cn'][0]] = $attrs['cn'][0]; } return $res; } /*! \brief Function to rewrite some problematic characters * * This function takes a string and replaces all possibly characters in it * with less problematic characters, as defined in $REWRITE. * * \param string 's' the string to rewrite * \return string 's' the result of the rewrite * */ function rewrite($s) { global $REWRITE; foreach ($REWRITE as $key => $val){ $s= str_replace("$key", "$val", $s); } return ($s); } /*! \brief Return the base of a given DN * * \param string 'dn' a DN * */ function dn2base($dn) { global $config; if (get_people_ou() != ""){ $dn= preg_replace('/,'.get_people_ou().'/i' , ',', $dn); } if (get_groups_ou() != ""){ $dn= preg_replace('/,'.get_groups_ou().'/i' , ',', $dn); } $base= preg_replace ('/^[^,]+,/i', '', $dn); return ($base); } /*! \brief Check if a given command exists and is executable * * Test if a given cmdline contains an executable command. Strips * arguments from the given cmdline. * * \param string 'cmdline' the cmdline to check * \return TRUE if command exists and is executable, otherwise FALSE. * */ function check_command($cmdline) { $cmd= preg_replace("/ .*$/", "", $cmdline); /* Check if command exists in filesystem */ if (!file_exists($cmd)){ return (FALSE); } /* Check if command is executable */ if (!is_executable($cmd)){ return (FALSE); } return (TRUE); } /*! \brief print plugin HTML header * * \param string 'image' the path of the image to be used next to the headline * \param string 'image' the headline * \param string 'info' additional information to print */ function print_header($image, $headline, $info= "") { $display= "
\n"; $display.= "

\"*\" $headline

\n"; $display.= "
\n"; if ($info != ""){ $display.= "
\n"; $display.= "$info"; $display.= "
\n"; } else { $display.= "
\n"; $display.= " "; $display.= "
\n"; } return ($display); } function range_selector($dcnt,$start,$range=25,$post_var=false) { /* Entries shown left and right from the selected entry */ $max_entries= 10; /* Initialize and take care that max_entries is even */ $output=""; if ($max_entries & 1){ $max_entries++; } if((!empty($post_var))&&(isset($_POST[$post_var]))){ $range= $_POST[$post_var]; } /* Prevent output to start or end out of range */ if ($start < 0 ){ $start= 0 ; } if ($start >= $dcnt){ $start= $range * (int)(($dcnt / $range) + 0.5); } $numpages= (($dcnt / $range)); if(((int)($numpages))!=($numpages)){ $numpages = (int)$numpages + 1; } if ((((int)$numpages) <= 1 )&&(!$post_var)){ return (""); } $ppage= (int)(($start / $range) + 0.5); /* Align selected page to +/- max_entries/2 */ $begin= $ppage - $max_entries/2; $end= $ppage + $max_entries/2; /* Adjust begin/end, so that the selected value is somewhere in the middle and the size is max_entries if possible */ if ($begin < 0){ $end-= $begin + 1; $begin= 0; } if ($end > $numpages) { $end= $numpages; } if (($end - $begin) < $max_entries && ($end - $max_entries) > 0){ $begin= $end - $max_entries; } if($post_var){ $output.= "
"; }else{ $output.= "
"; } /* Draw decrement */ if ($start > 0 ) { $output.=" ". "\"\""; } /* Draw pages */ for ($i= $begin; $i < $end; $i++) { if ($ppage == $i){ $output.= " ".($i+1)." "; } else { $output.= " ".($i+1)." "; } } /* Draw increment */ if($start < ($dcnt-$range)) { $output.=" ". "\"\""; } if(($post_var)&&($numpages)){ $output.= "
 "._("Entries per page")." 
"; }else{ $output.= ""; } return($output); } /*! \brief Generate HTML for the 'Apply filter' button */ function apply_filter() { $apply= ""; $apply= ''. '
'. '
'; return ($apply); } /*! \brief Generate HTML for the 'Back' button */ function back_to_main() { $string= '

'; return ($string); } function normalize_netmask($netmask) { /* Check for notation of netmask */ if (!preg_match('/^([0-9]+\.){3}[0-9]+$/', $netmask)){ $num= (int)($netmask); $netmask= ""; for ($byte= 0; $byte<4; $byte++){ $result=0; for ($i= 7; $i>=0; $i--){ if ($num-- > 0){ $result+= pow(2,$i); } } $netmask.= $result."."; } return (preg_replace('/\.$/', '', $netmask)); } return ($netmask); } /*! \brief Return the number of set bits in the netmask * * For a given subnetmask (for example 255.255.255.0) this returns * the number of set bits. * * Example: * \code * $bits = netmask_to_bits('255.255.255.0') # Returns 24 * $bits = netmask_to_bits('255.255.254.0') # Returns 23 * \endcode * * Be aware of the fact that the function does not check * if the given subnet mask is actually valid. For example: * Bad examples: * \code * $bits = netmask_to_bits('255.0.0.255') # Returns 16 * $bits = netmask_to_bits('255.255.0.255') # Returns 24 * \endcode */ function netmask_to_bits($netmask) { list($nm0, $nm1, $nm2, $nm3)= split('\.', $netmask); $res= 0; for ($n= 0; $n<4; $n++){ $start= 255; $name= "nm$n"; for ($i= 0; $i<8; $i++){ if ($start == (int)($$name)){ $res+= 8 - $i; break; } $start-= pow(2,$i); } } return ($res); } 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); } 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)); } 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 */ $used= array(); $ldap= $config->get_ldap_link(); $ldap->cd($config->current['BASE']); $ldap->search('(uid=*)'); while($attrs= $ldap->fetch()){ $used[]= $attrs['uid'][0]; } /* 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)){ $size= preg_replace('/^.*{id:(\d+)}.*$/', '\\1', $uid); for ($i= 0, $p= pow(10,$size); $i < $p; $i++){ $number= sprintf("%0".$size."d", $i); $res= preg_replace('/{id:(\d+)}/', $number, $uid); if (!in_array($res, $used)){ $uid= $res; break; } } } 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); if (!in_array($res, $used)){ $uid= $res; break; } } } /* Don't assign used ones */ if (!in_array($uid, $used)){ /* 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 * is a value and (g|m|k) stands for Gigabyte, Megabyte and Kilobyte * this function returns the byte value. * * \param string 'value' a value in the above specified format * \return a byte value or the original value if specified string is simply * a numeric value * */ function to_byte($value) { $value= strtolower(trim($value)); if(!is_numeric(substr($value, -1))) { switch(substr($value, -1)) { case 'g': $mult= 1073741824; break; case 'm': $mult= 1048576; break; case 'k': $mult= 1024; break; } return ($mult * (int)substr($value, 0, -1)); } else { return $value; } } /*! \brief Check if a value exists in an array (case-insensitive) * * This is just as http://php.net/in_array except that the comparison * is case-insensitive. * * \param string 'value' needle * \param array 'items' haystack */ function in_array_ics($value, $items) { return preg_grep('/^'.preg_quote($value, '/').'$/i', $items); } /*! \brief Generate a clickable alphabet */ function generate_alphabet($count= 10) { $characters= _("*ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); $alphabet= ""; $c= 0; /* Fill cells with charaters */ for ($i= 0, $l= mb_strlen($characters, 'UTF8'); $i<$l; $i++){ if ($c == 0){ $alphabet.= ""; } $ch = mb_substr($characters, $i, 1, "UTF8"); $alphabet.= " ".$ch." "; if ($c++ == $count){ $alphabet.= ""; $c= 0; } } /* Fill remaining cells */ while ($c++ <= $count){ $alphabet.= " "; } return ($alphabet); } function validate($string) { return (strip_tags(str_replace('\0', '', $string))); } /*! \brief Get the current gosa version */ function get_gosa_version() { global $svn_revision, $svn_path; /* Extract informations */ $revision= preg_replace('/^[^0-9]*([0-9]+)[^0-9]*$/', '\1', $svn_revision); /* Release or development? */ if (preg_match('%/gosa/trunk/%', $svn_path)){ return (sprintf(_("GOsa development snapshot (Rev %s)"), $revision)); } else { $release= preg_replace('%^.*/([^/]+)/include/functions.inc.*$%', '\1', $svn_path); return (sprintf(_("GOsa $release"), $revision)); } } /*! \brief Recursively delete a path in the file system * * Will delete the given path and all its files recursively. * Can also follow links if told so. * * \param string 'path' * \param boolean 'followLinks' TRUE to follow links, FALSE (default) * for not following links */ function rmdirRecursive($path, $followLinks=false) { $dir= opendir($path); while($entry= readdir($dir)) { if(is_file($path."/".$entry) || ((!$followLinks) && is_link($path."/".$entry))) { unlink($path."/".$entry); } elseif (is_dir($path."/".$entry) && $entry!='.' && $entry!='..') { rmdirRecursive($path."/".$entry); } } closedir($dir); return rmdir($path); } /*! \brief Get directory content information * * Returns the content of a directory as an array in an * ascended sorted manner. * * \param string 'path' * \param boolean weither to sort the content descending. */ function scan_directory($path,$sort_desc=false) { $ret = false; /* is this a dir ? */ if(is_dir($path)) { /* is this path a readable one */ if(is_readable($path)){ /* Get contents and write it into an array */ $ret = array(); $dir = opendir($path); /* Is this a correct result ?*/ if($dir){ while($fp = readdir($dir)) $ret[]= $fp; } } } /* Sort array ascending , like scandir */ sort($ret); /* Sort descending if parameter is sort_desc is set */ if($sort_desc) { $ret = array_reverse($ret); } return($ret); } /*! \brief Clean the smarty compile dir */ function clean_smarty_compile_dir($directory) { global $svn_revision; if(is_dir($directory) && is_readable($directory)) { // Set revision filename to REVISION $revision_file= $directory."/REVISION"; /* Is there a stamp containing the current revision? */ if(!file_exists($revision_file)) { // create revision file create_revision($revision_file, $svn_revision); } else { # check for "$config->...['CONFIG']/revision" and the # contents should match the revision number if(!compare_revision($revision_file, $svn_revision)){ // If revision differs, clean compile directory foreach(scan_directory($directory) as $file) { if(($file==".")||($file=="..")) continue; if( is_file($directory."/".$file) && is_writable($directory."/".$file)) { // delete file if(!unlink($directory."/".$file)) { msg_dialog::display(_("Internal error"), sprintf(_("File '%s' could not be deleted."), $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); } else { // Revision matches, nothing to do } } } else { // Smarty compile dir is not accessible // (Smarty will warn about this) } } function create_revision($revision_file, $revision) { $result= false; if(is_dir(dirname($revision_file)) && is_writable(dirname($revision_file))) { if($fh= fopen($revision_file, "w")) { if(fwrite($fh, $revision)) { $result= true; } } fclose($fh); } else { msg_dialog::display(_("Internal error"), _("Cannot write to revision file!"), ERROR_DIALOG); } return $result; } function compare_revision($revision_file, $revision) { // false means revision differs $result= false; if(file_exists($revision_file) && is_readable($revision_file)) { // Open file if($fh= fopen($revision_file, "r")) { // Compare File contents with current revision if($revision == fread($fh, filesize($revision_file))) { $result= true; } } else { msg_dialog::display(_("Internal error"), _("Cannot write to revision file!"), ERROR_DIALOG); } // Close file fclose($fh); } return $result; } /*! \brief Return HTML for a progressbar * * \code * $smarty->assign("installprogress", progressbar($current_progress_in_percent),100,15,true); * \endcode * * \param int 'percentage' Value to display * \param int 'width' width of the resulting output * \param int 'height' height of the resulting output * \param boolean 'showvalue' weither to show the percentage in the progressbar or not * */ function progressbar($percentage,$width=100,$height=15,$showvalue=false) { $str = ""; // Our return value will be saved in this var $color = dechex($percentage+150); $color2 = dechex(150 - $percentage); $bgcolor= $showvalue?"FFFFFF":"DDDDDD"; $progress = (int)(($percentage /100)*$width); /* If theres a better solution for this, use it... */ $str = "\n
"; $str.= "\n
"; if(($height >10)&&($showvalue)){ $str.= "\n "; $str.= "\n ".$percentage."% "; $str.= "\n "; } $str.= "\n
"; $str.= "\n
"; $str.= "\n
"; $str.= "\n
"; return($str); } /*! \brief Lookup a key in an array case-insensitive * * Given an associative array this can lookup the value of * a certain key, regardless of the case. * * \code * $items = array ('FOO' => 'blub', 'bar' => 'blub'); * array_key_ics('foo', $items); # Returns 'blub' * array_key_ics('BAR', $items); # Returns 'blub' * \endcode * * \param string 'key' needle * \param array 'items' haystack */ function array_key_ics($ikey, $items) { $tmp= array_change_key_case($items, CASE_LOWER); $ikey= strtolower($ikey); if (isset($tmp[$ikey])){ return($tmp[$ikey]); } return (''); } /*! \brief Determine if two arrays are different * * \param array 'src' * \param array 'dst' * \return boolean TRUE or FALSE * */ function array_differs($src, $dst) { /* If the count is differing, the arrays differ */ if (count ($src) != count ($dst)){ return (TRUE); } return (count(array_diff($src, $dst)) != 0); } function saveFilter($a_filter, $values) { if (isset($_POST['regexit'])){ $a_filter["regex"]= $_POST['regexit']; foreach($values as $type){ if (isset($_POST[$type])) { $a_filter[$type]= "checked"; } else { $a_filter[$type]= ""; } } } /* React on alphabet links if needed */ if (isset($_GET['search'])){ $s= mb_substr(validate($_GET['search']), 0, 1, "UTF8")."*"; if ($s == "**"){ $s= "*"; } $a_filter['regex']= $s; } return ($a_filter); } /*! \brief Escape all LDAP filter relevant characters */ function normalizeLdap($input) { return (addcslashes($input, '()|')); } /*! \brief Returns the difference between to microtime() results in float */ function get_MicroTimeDiff($start , $stop) { $a = split("\ ",$start); $b = split("\ ",$stop); $secs = $b[1] - $a[1]; $msecs= $b[0] - $a[0]; $ret = (float) ($secs+ $msecs); return($ret); } /*! \brief Return the gosa base directory */ function get_base_dir() { global $BASE_DIR; return $BASE_DIR; } /*! \brief Test weither we are allowed to read the object */ function obj_is_readable($dn, $object, $attribute) { global $ui; return preg_match('/r/', $ui->get_permissions($dn, $object, $attribute)); } /*! \brief Test weither we are allowed to change the object */ function obj_is_writable($dn, $object, $attribute) { global $ui; return preg_match('/w/', $ui->get_permissions($dn, $object, $attribute)); } /*! \brief Explode a DN into its parts * * Similar to explode (http://php.net/explode), but a bit more specific * for the needs when splitting, exploding LDAP DNs. * * \param string 'dn' the DN to split * \param config-object a config object. only neeeded if DN shall be verified in the LDAP * \param boolean verify_in_ldap check weither DN is valid * */ function gosa_ldap_explode_dn($dn,$config = NULL,$verify_in_ldap=false) { /* Initialize variables */ $ret = array("count" => 0); // Set count to 0 $next = true; // if false, then skip next loops and return $cnt = 0; // Current number of loops $max = 100; // Just for security, prevent looops $ldap = NULL; // To check if created result a valid $keep = ""; // save last failed parse string /* Check each parsed dn in ldap ? */ if($config!==NULL && $verify_in_ldap){ $ldap = $config->get_ldap_link(); } /* Lets start */ $called = false; while(preg_match("/,/",$dn) && $next && $cnt < $max){ $cnt ++; if(!preg_match("/,/",$dn)){ $next = false; } $object = preg_replace("/[,].*$/","",$dn); $dn = preg_replace("/^[^,]+,/","",$dn); $called = true; /* Check if current dn is valid */ if($ldap!==NULL){ $ldap->cd($dn); $ldap->cat($dn,array("dn")); if($ldap->count()){ $ret[] = $keep.$object; $keep = ""; }else{ $keep .= $object.","; } }else{ $ret[] = $keep.$object; $keep = ""; } } /* No dn was posted */ if($cnt == 0 && !empty($dn)){ $ret[] = $dn; } /* Append the rest */ $test = $keep.$dn; if($called && !empty($test)){ $ret[] = $keep.$dn; } $ret['count'] = count($ret) - 1; return($ret); } function get_base_from_hook($dn, $attrib) { global $config; if ($config->get_cfg_value("baseIdHook") != ""){ /* Call hook script - if present */ $command= $config->get_cfg_value("baseIdHook"); if ($command != ""){ $command.= " '".LDAP::fix($dn)."' $attrib"; if (check_command($command)){ @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execute"); exec($command, $output); if (preg_match("/^[0-9]+$/", $output[0])){ return ($output[0]); } else { msg_dialog::display(_("Warning"), _("'baseIdHook' is not available. Using default base!"), WARNING_DIALOG); return ($config->get_cfg_value("uidNumberBase")); } } else { msg_dialog::display(_("Warning"), _("'baseIdHook' is not available. Using default base!"), WARNING_DIALOG); return ($config->get_cfg_value("uidNumberBase")); } } else { msg_dialog::display(_("Warning"), _("'baseIdHook' is not available. Using default base!"), WARNING_DIALOG); return ($config->get_cfg_value("uidNumberBase")); } } } /*! \brief Check if schema version matches the requirements */ function check_schema_version($class, $version) { return preg_match("/\(v$version\)/", $class['DESC']); } /*! \brief Check if LDAP schema matches the requirements */ function check_schema($cfg,$rfc2307bis = FALSE) { $messages= array(); /* Get objectclasses */ $ldap = new ldapMultiplexer(new LDAP($cfg['admin'],$cfg['password'],$cfg['connection'] ,FALSE, $cfg['tls'])); $objectclasses = $ldap->get_objectclasses(); if(count($objectclasses) == 0){ msg_dialog::display(_("LDAP warning"), _("Cannot get schema information from server. No schema check possible!"), WARNING_DIALOG); } /* This is the default block used for each entry. * to avoid unset indexes. */ $def_check = array("REQUIRED_VERSION" => "0", "SCHEMA_FILES" => array(), "CLASSES_REQUIRED" => array(), "STATUS" => FALSE, "IS_MUST_HAVE" => FALSE, "MSG" => "", "INFO" => "");#_("There is currently no information specified for this schema extension.")); /* The gosa base schema */ $checks['gosaObject'] = $def_check; $checks['gosaObject']['REQUIRED_VERSION'] = "2.6.1"; $checks['gosaObject']['SCHEMA_FILES'] = array("gosa+samba3.schema","gosa.schema"); $checks['gosaObject']['CLASSES_REQUIRED'] = array("gosaObject"); $checks['gosaObject']['IS_MUST_HAVE'] = TRUE; /* GOsa Account class */ $checks["gosaAccount"]["REQUIRED_VERSION"]= "2.6.1"; $checks["gosaAccount"]["SCHEMA_FILES"] = array("gosa+samba3.schema","gosa.schema"); $checks["gosaAccount"]["CLASSES_REQUIRED"]= array("gosaAccount"); $checks["gosaAccount"]["IS_MUST_HAVE"] = TRUE; $checks["gosaAccount"]["INFO"] = _("Used to store account specific informations."); /* GOsa lock entry, used to mark currently edited objects as 'in use' */ $checks["gosaLockEntry"]["REQUIRED_VERSION"] = "2.6.1"; $checks["gosaLockEntry"]["SCHEMA_FILES"] = array("gosa+samba3.schema","gosa.schema"); $checks["gosaLockEntry"]["CLASSES_REQUIRED"] = array("gosaLockEntry"); $checks["gosaLockEntry"]["IS_MUST_HAVE"] = TRUE; $checks["gosaLockEntry"]["INFO"] = _("Used to lock currently edited entries to avoid multiple changes at the same time."); /* Some other checks */ foreach(array( "gosaCacheEntry" => array("version" => "2.6.1"), "gosaDepartment" => array("version" => "2.6.1"), "goFaxAccount" => array("version" => "1.0.4", "class" => "gofaxAccount","file" => "gofax.schema"), "goFaxSBlock" => array("version" => "1.0.4", "class" => "gofaxAccount","file" => "gofax.schema"), "goFaxRBlock" => array("version" => "1.0.4", "class" => "gofaxAccount","file" => "gofax.schema"), "gosaUserTemplate" => array("version" => "2.6.1", "class" => "posixAccount","file" => "nis.schema"), "gosaMailAccount" => array("version" => "2.6.1", "class" => "mailAccount","file" => "gosa+samba3.schema"), "gosaProxyAccount" => array("version" => "2.6.1", "class" => "proxyAccount","file" => "gosa+samba3.schema"), "gosaApplication" => array("version" => "2.6.1", "class" => "appgroup","file" => "gosa.schema"), "gosaApplicationGroup" => array("version" => "2.6.1", "class" => "appgroup","file" => "gosa.schema"), "GOhard" => array("version" => "2.6.1", "class" => "terminals","file" => "goto.schema"), "gotoTerminal" => array("version" => "2.6.1", "class" => "terminals","file" => "goto.schema"), "goServer" => array("version" => "2.6.1","class" => "server","file" => "goserver.schema"), "goTerminalServer" => array("version" => "2.6.1", "class" => "terminals","file" => "goto.schema"), "goShareServer" => array("version" => "2.6.1", "class" => "terminals","file" => "goto.schema"), "goNtpServer" => array("version" => "2.6.1", "class" => "terminals","file" => "goto.schema"), "goSyslogServer" => array("version" => "2.6.1", "class" => "terminals","file" => "goto.schema"), "goLdapServer" => array("version" => "2.6.1"), "goCupsServer" => array("version" => "2.6.1", "class" => array("posixAccount", "terminals"),), "goImapServer" => array("version" => "2.6.1", "class" => array("mailAccount", "mailgroup"),"file" => "gosa+samba3. schema"), "goKrbServer" => array("version" => "2.6.1"), "goFaxServer" => array("version" => "2.6.1", "class" => "gofaxAccount","file" => "gofax.schema"), ) as $name => $values){ $checks[$name] = $def_check; if(isset($values['version'])){ $checks[$name]["REQUIRED_VERSION"] = $values['version']; } if(isset($values['file'])){ $checks[$name]["SCHEMA_FILES"] = array($values['file']); } $checks[$name]["CLASSES_REQUIRED"] = array($name); } foreach($checks as $name => $value){ foreach($value['CLASSES_REQUIRED'] as $class){ if(!isset($objectclasses[$name])){ $checks[$name]['STATUS'] = FALSE; if($value['IS_MUST_HAVE']){ $checks[$name]['MSG'] = sprintf(_("Missing required object class '%s'!"),$class); }else{ $checks[$name]['MSG'] = sprintf(_("Missing optional object class '%s'!"),$class); } }elseif(!check_schema_version($objectclasses[$name],$value['REQUIRED_VERSION'])){ $checks[$name]['STATUS'] = FALSE; if($value['IS_MUST_HAVE']){ $checks[$name]['MSG'] = sprintf(_("Version mismatch for required object class '%s' (!=%s)!"), $class, $value['REQUIRED_VERSION']); }else{ $checks[$name]['MSG'] = sprintf(_("Version mismatch for optional object class '%s' (!=%s)!"), $class, $value['REQUIRED_VERSION']); } }else{ $checks[$name]['STATUS'] = TRUE; $checks[$name]['MSG'] = sprintf(_("Class(es) available")); } } } $tmp = $objectclasses; /* The gosa base schema */ $checks['posixGroup'] = $def_check; $checks['posixGroup']['REQUIRED_VERSION'] = "2.6.1"; $checks['posixGroup']['SCHEMA_FILES'] = array("gosa+samba3.schema","gosa.schema"); $checks['posixGroup']['CLASSES_REQUIRED'] = array("posixGroup"); $checks['posixGroup']['STATUS'] = TRUE; $checks['posixGroup']['IS_MUST_HAVE'] = TRUE; $checks['posixGroup']['MSG'] = ""; $checks['posixGroup']['INFO'] = ""; /* Depending on selected rfc2307bis mode, we need different schema configurations */ if(isset($tmp['posixGroup'])){ if($rfc2307bis && isset($tmp['posixGroup']['STRUCTURAL'])){ $checks['posixGroup']['STATUS'] = FALSE; $checks['posixGroup']['MSG'] = _("You have enabled the rfc2307bis option on the 'ldap setup' step, but your schema configuration do not support this option."); $checks['posixGroup']['INFO'] = _("In order to use rfc2307bis conform groups the objectClass 'posixGroup' must be AUXILIARY"); } if(!$rfc2307bis && !isset($tmp['posixGroup']['STRUCTURAL'])){ $checks['posixGroup']['STATUS'] = FALSE; $checks['posixGroup']['MSG'] = _("Your schema is configured to support the rfc2307bis group, but you have disabled this option on the 'ldap setup' step."); $checks['posixGroup']['INFO'] = _("The objectClass 'posixGroup' must be STRUCTURAL"); } } return($checks); } function get_languages($languages_in_own_language = FALSE,$strip_region_tag = FALSE) { $tmp = array( "de_DE" => "German", "fr_FR" => "French", "it_IT" => "Italian", "es_ES" => "Spanish", "en_US" => "English", "nl_NL" => "Dutch", "pl_PL" => "Polish", #"sv_SE" => "Swedish", "zh_CN" => "Chinese", "vi_VN" => "Vietnamese", "ru_RU" => "Russian"); $tmp2= array( "de_DE" => _("German"), "fr_FR" => _("French"), "it_IT" => _("Italian"), "es_ES" => _("Spanish"), "en_US" => _("English"), "nl_NL" => _("Dutch"), "pl_PL" => _("Polish"), #"sv_SE" => _("Swedish"), "zh_CN" => _("Chinese"), "vi_VN" => _("Vietnamese"), "ru_RU" => _("Russian")); $ret = array(); if($languages_in_own_language){ $old_lang = setlocale(LC_ALL, 0); /* If the locale wasn't correclty set before, there may be an incorrect locale returned. Something like this: C_CTYPE=de_DE.UTF-8;LC_NUMERIC=C;LC_TIME=de_DE.UTF-8;LC ... Extract the locale name from this string and use it to restore old locale. */ if(preg_match("/LC_CTYPE/",$old_lang)){ $old_lang = preg_replace("/^.*LC_CTYPE=([^;]*).*$/","\\1",$old_lang); } foreach($tmp as $key => $name){ $lang = $key.".UTF-8"; setlocale(LC_ALL, $lang); if($strip_region_tag){ $ret[preg_replace("/^([^_]*).*$/","\\1",$key)] = _($name)." (".$tmp2[$key].")"; }else{ $ret[$key] = _($name)."  (".$tmp2[$key].")"; } } setlocale(LC_ALL, $old_lang); }else{ foreach($tmp as $key => $name){ if($strip_region_tag){ $ret[preg_replace("/^([^_]*).*/","\\1",$key)] = _($name); }else{ $ret[$key] = _($name); } } } return($ret); } /*! \brief Returns contents of the given POST variable and check magic quotes settings * * Depending on the magic quotes settings this returns a stripclashed'ed version of * a certain POST variable. * * \param string 'name' the POST var to return ($_POST[$name]) * \return string * */ 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(get_magic_quotes_gpc()){ return(stripcslashes($_POST[$name])); }else{ return($_POST[$name]); } } /*! \brief Return class name in correct case */ function get_correct_class_name($cls) { global $class_mapping; if(isset($class_mapping) && is_array($class_mapping)){ foreach($class_mapping as $class => $file){ if(preg_match("/^".$cls."$/i",$class)){ return($class); } } } return(FALSE); } /*! \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 */ function change_password ($dn, $password, $mode=0, $hash= "") { 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')); $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 != ""){ /* Check for supported algorithm */ 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); } if($test instanceOf passwordMethod){ $deactivated = $test->is_locked($config,$dn); /* Feed password backends with information */ $test->dn= $dn; $test->attrs= $attrs; $newpass= $test->generate_hash($password); // Update shadow timestamp? if (isset($attrs["shadowLastChange"][0])){ $shadow= (int)(date("U") / 86400); } else { $shadow= 0; } // Write back modified entry $ldap->cd($dn); $attrs= array(); // Not for groups if ($mode == 0){ if ($shadow != 0){ $attrs['shadowLastChange']= $shadow; } // Create SMB Password $attrs= generate_smb_nt_hash($password); } $attrs['userPassword']= array(); $attrs['userPassword']= $newpass; $ldap->modify($attrs); /* Read ! if user was deactivated */ if($deactivated){ $test->lock_account($config,$dn); } new log("modify","users/passwordMethod",$dn,array_keys($attrs),$ldap->get_error()); if (!$ldap->success()) { msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, LDAP_MOD, ERROR_DIALOG)); } else { /* Run backend method for change/create */ if(!$test->set_password($password)){ return(FALSE); } /* Find postmodify entries for this class */ $command= $config->search("password", "POSTMODIFY",array('menu')); if ($command != ""){ /* Walk through attribute list */ $command= preg_replace("/%userPassword/", $password, $command); $command= preg_replace("/%dn/", $dn, $command); if (check_command($command)){ @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execute"); exec($command); } else { $message= sprintf(_("Command '%s', specified as POSTMODIFY for plugin '%s' doesn't seem to exist."), $command, "password"); msg_dialog::display(_("Configuration error"), $message, ERROR_DIALOG); } } } return(TRUE); } } /*! \brief Generate samba hashes * * Given a certain password this constructs an array like * array['sambaLMPassword'] etc. * * \param string 'password' * \return array contains several keys for lmPassword, ntPassword, pwdLastSet, etc. depending * on the samba version */ function generate_smb_nt_hash($password) { global $config; # Try to use gosa-si? if ($config->get_cfg_value("gosaSupportURI") != ""){ $res= gosaSupportDaemon::send("gosa_gen_smb_hash", "GOSA", array("password" => $password), TRUE); if (isset($res['XML']['HASH'])){ $hash= $res['XML']['HASH']; } else { $hash= ""; } if ($hash == "") { msg_dialog::display(_("Configuration error"), _("Cannot generate samba hash!"), ERROR_DIALOG); return (""); } } else { $tmp= $config->get_cfg_value('sambaHashHook')." ".escapeshellarg($password); @DEBUG (DEBUG_LDAP, __LINE__, __FUNCTION__, __FILE__, $tmp, "Execute"); exec($tmp, $ar); flush(); reset($ar); $hash= current($ar); if ($hash == "") { msg_dialog::display(_("Configuration error"), sprintf(_("Cannot generate samba hash: running '%s' failed, check the 'sambaHashHook'!"),$config->get_cfg_value('sambaHashHook')), ERROR_DIALOG); return (""); } } list($lm,$nt)= split (":", trim($hash)); if ($config->get_cfg_value("sambaversion") == 3) { $attrs['sambaLMPassword']= $lm; $attrs['sambaNTPassword']= $nt; $attrs['sambaPwdLastSet']= date('U'); $attrs['sambaBadPasswordCount']= "0"; $attrs['sambaBadPasswordTime']= "0"; } else { $attrs['lmPassword']= $lm; $attrs['ntPassword']= $nt; $attrs['pwdLastSet']= date('U'); } return($attrs); } /*! \brief Get the Change Sequence Number of a certain DN * * To verify if a given object has been changed outside of Gosa * in the meanwhile, this function can be used to get the entryCSN * from the LDAP directory. It uses the attribute as configured * in modificationDetectionAttribute * * \param string 'dn' * \return either the result or "" in any other case */ function getEntryCSN($dn) { global $config; if(empty($dn) || !is_object($config)){ return(""); } /* Get attribute that we should use as serial number */ $attr= $config->get_cfg_value("modificationDetectionAttribute"); if($attr != ""){ $ldap = $config->get_ldap_link(); $ldap->cat($dn,array($attr)); $csn = $ldap->fetch(); if(isset($csn[$attr][0])){ return($csn[$attr][0]); } } return(""); } /*! \brief Add (a) given objectClass(es) to an attrs entry * * The function adds the specified objectClass(es) to the given * attrs entry. * * \param mixed 'classes' Either a single objectClass or several objectClasses * as an array * \param array 'attrs' The attrs array to be modified. * * */ function add_objectClass($classes, &$attrs) { if (is_array($classes)){ $list= $classes; } else { $list= array($classes); } foreach ($list as $class){ $attrs['objectClass'][]= $class; } } /*! \brief Removes a given objectClass from the attrs entry * * Similar to add_objectClass, except that it removes the given * objectClasses. See it for the params. * */ function remove_objectClass($classes, &$attrs) { if (isset($attrs['objectClass'])){ /* Array? */ if (is_array($classes)){ $list= $classes; } else { $list= array($classes); } $tmp= array(); foreach ($attrs['objectClass'] as $oc) { foreach ($list as $class){ if (strtolower($oc) != strtolower($class)){ $tmp[]= $oc; } } } $attrs['objectClass']= $tmp; } } /*! \brief Initialize a file download with given content, name and data type. * \param string data The content to send. * \param string name The name of the file. * \param string type The content identifier, default value is "application/octet-stream"; */ function send_binary_content($data,$name,$type = "application/octet-stream") { header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); header("Cache-Control: no-cache"); header("Pragma: no-cache"); header("Cache-Control: post-check=0, pre-check=0"); header("Content-type: ".$type.""); $HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT']; /* Strip name if it is a complete path */ if (preg_match ("/\//", $name)) { $name= basename($name); } /* force download dialog */ if (preg_match('/MSIE 5.5/', $HTTP_USER_AGENT) || preg_match('/MSIE 6.0/', $HTTP_USER_AGENT)) { header('Content-Disposition: filename="'.$name.'"'); } else { header('Content-Disposition: attachment; filename="'.$name.'"'); } echo $data; exit(); } function reverse_html_entities($str,$type = ENT_QUOTES , $charset = "UTF-8") { if(is_string($str)){ return(htmlentities($str,$type,$charset)); }elseif(is_array($str)){ foreach($str as $name => $value){ $str[$name] = reverse_html_entities($value,$type,$charset); } } return($str); } /*! \brief Encode special string characters so we can use the string in \ HTML output, without breaking quotes. \param string The String we want to encode. \return string The encoded String */ function xmlentities($str) { if(is_string($str)){ static $asc2uni= array(); if (!count($asc2uni)){ for($i=128;$i<256;$i++){ # $asc2uni[chr($i)] = "&#x".dechex($i).";"; } } $str = str_replace("&", "&", $str); $str = str_replace("<", "<", $str); $str = str_replace(">", ">", $str); $str = str_replace("'", "'", $str); $str = str_replace("\"", """, $str); $str = str_replace("\r", "", $str); $str = strtr($str,$asc2uni); return $str; }elseif(is_array($str)){ foreach($str as $name => $value){ $str[$name] = xmlentities($value); } } return($str); } /*! \brief Updates all accessTo attributes from a given value to a new one. For example if a host is renamed. \param String $from The source accessTo name. \param String $to The destination accessTo name. */ function update_accessTo($from,$to) { global $config; $ldap = $config->get_ldap_link(); $ldap->cd($config->current['BASE']); $ldap->search("(&(objectClass=trustAccount)(accessTo=".$from."))",array("objectClass","accessTo")); while($attrs = $ldap->fetch()){ $new_attrs = array("accessTo" => array()); $dn = $attrs['dn']; for($i = 0 ; $i < $attrs['objectClass']['count']; $i++){ $new_attrs['objectClass'][] = $attrs['objectClass'][$i]; } for($i = 0 ; $i < $attrs['accessTo']['count']; $i++){ if($attrs['accessTo'][$i] == $from){ if(!empty($to)){ $new_attrs['accessTo'][] = $to; } }else{ $new_attrs['accessTo'][] = $attrs['accessTo'][$i]; } } $ldap->cd($dn); $ldap->modify($new_attrs); if (!$ldap->success()){ msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, LDAP_MOD, "update_accessTo($from,$to)")); } new log("modify","update_accessTo($from,$to)",$dn,array_keys($new_attrs),$ldap->get_error()); } } /*! \brief Returns a random char */ function get_random_char () { $randno = rand (0, 63); if ($randno < 12) { return (chr ($randno + 46)); // Digits, '/' and '.' } else if ($randno < 38) { return (chr ($randno + 53)); // Uppercase } else { return (chr ($randno + 59)); // Lowercase } } function cred_encrypt($input, $password) { $size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $iv = mcrypt_create_iv($size, MCRYPT_DEV_RANDOM); return bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $password, $input, MCRYPT_MODE_ECB, $iv)); } function cred_decrypt($input,$password) { $size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $iv = mcrypt_create_iv($size, MCRYPT_DEV_RANDOM); return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, pack("H*", $input), MCRYPT_MODE_ECB, $iv); } function get_object_info() { return(session::get('objectinfo')); } function set_object_info($str = "") { session::set('objectinfo',$str); } /*! \brief Compare two strings similar as an LDAP search would do it * * This function compares a string similar as a LDAP equality match * would do. It does so by replacing any occurence of a * with a * regex pattern and test it with preg_match. The default mapping is '.*' * but the test can be extended by changing that mapping, so that the asterisk * can stand for a certain range of chars for example. * * Example: * \code * ldap_equality_check('halut', '*'); # true * ldap_equality_check('halut/1.2.3', 'halut/*'); # true * ldap_equality_check('halut/1.2.3', 'hal\*\/1.2.3'); # true * \endcode * * */ function ldap_equality_check($string1, $string2, $mapping=".*", $case_sensitive=TRUE) { if (!(strstr($string2, '*')) && ($string1 == $string2)) { return TRUE; } /* Set modifier string to i if case insensitivity is requested */ $modifier = $case_sensitive ? "" : "i"; $string2 = preg_quote($string2, "/"); $string2 = str_replace('\*', $mapping, $string2); $result = preg_match('/' . $string2 . '$/' . $modifier, $string1); return $result; } // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler: ?>