An error occurred while loading the file. Please try again.
-
Côme Chilliet authored
Adds FusionDirectoryError base class and one child class for check failures. Use this new class for IntAttribute as first test. Build HTML dialog from error class using attribute data. issue #6071
Unverified172f088f
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2003-2010 Cajus Pollmeier
Copyright (C) 2011-2016 FusionDirectory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*!
* \file functions.inc
* Common functions and named definitions.
*/
/* Define common locations and variables */
require_once('variables.inc');
/* Include required files */
require_once(CACHE_DIR.'/'.CLASS_CACHE);
require_once('functions_debug.inc');
require_once('accept-to-gettext.inc');
/*!
* \brief Does autoloading for classes used in FusionDirectory.
*
* Takes the list generated by 'fusiondirectory-configuration-manager' and loads the
* file containing the requested class.
*
* \param array $class_name list of class name
*/
function fusiondirectory_autoload ($class_name)
{
global $class_mapping, $BASE_DIR, $config;
if ($class_mapping === NULL) {
if (isset($config) && is_object($config) &&
$config->get_cfg_value('displayerrors') == 'TRUE') {
list($trace, ) = html_trace();
echo $trace;
echo "<br/>\n";
}
echo sprintf(_("Fatal error: no class locations defined - please run '%s' to fix this"), "<b>fusiondirectory-configuration-manager --update-cache</b>");
exit;
}
/* Do not try to autoload smarty classes */
if (strpos($class_name, 'Smarty_') === 0) {
return;
}
if (strpos($class_name, 'FusionDirectory\\') === 0) {
$class_name = preg_replace('/^.+\\\\([^\\\\]+)$/', '\\1', "$class_name");
}
if (isset($class_mapping["$class_name"])) {
require_once($BASE_DIR.'/'.$class_mapping["$class_name"]);
} else {
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $class_name, 'Could not load');
if (isset($config) && is_object($config) &&
$config->get_cfg_value('displayerrors') == 'TRUE') {
list($trace, ) = html_trace();
echo $trace;
echo "<br/>\n";
}
echo sprintf(_("Fatal error: cannot instantiate class '%s' - try running '%s' to fix this"), $class_name, "<b>fusiondirectory-configuration-manager --update-cache</b>");
exit;
}
}
spl_autoload_register('fusiondirectory_autoload');
/*!
* \brief Checks if a class is available.
*
* \param string $name The subject of the test
*
* \return boolean Return TRUE if successfull FALSE otherwise
*/
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 Return TRUE if successfull FALSE otherwise
*/
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 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
*/
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
function DEBUG ($level, $line, $function, $file, $data, $info = '')
{
logging::debug($level, $line, $function, $file, $data, $info);
}
/*!
* \brief Return HTML safe copyright notice
*/
function copynotice ()
{
return sprintf(htmlescape(_('%s 2002-%d %sThe FusionDirectory team, %s%s')), '©', date('Y'), '<a href="http://www.fusiondirectory.org">', FD_VERSION, '</a>');
}
/*!
* \brief Return themed path for specified base file
*
* Depending on its parameters, this function returns the full
* path of a template file. First match wins while searching
* in this order:
*
* - load theme depending file
* - load global theme depending file
* - load default theme file
* - load global default theme file
*
* \param string $filename The base file name
*
* \param boolean $plugin Flag to take the plugin directory as search base
*
* \param string $path User specified path to take as search base
*
* \return string Full path to the template file
*/
function get_template_path ($filename = '', $plugin = FALSE, $path = '')
{
global $config, $BASE_DIR;
$default_theme = 'breezy';
/* Set theme */
if (isset($config)) {
$theme = $config->get_cfg_value('theme', $default_theme);
} else {
$theme = $default_theme;
}
/* Return path for empty filename */
if ($filename == '') {
return "themes/$theme/";
}
/* Return plugin dir or root directory? */
if ($plugin) {
if ($path == '') {
$path = session::get('plugin_dir');
$nf = preg_replace('!^'.$BASE_DIR.'/!', '', preg_replace('/^\.\.\//', '', $path));
} else {
$nf = preg_replace('!^'.$BASE_DIR.'/!', '', $path);
}
$paths = [
"$BASE_DIR/ihtml/themes/$theme/$nf/$filename",
"$BASE_DIR/ihtml/themes/$default_theme/$nf/$filename",
"$BASE_DIR/ihtml/themes/default/$nf/$filename",
$path."/$filename"
];
} else {
$paths = [
"themes/$theme/$filename",
"$BASE_DIR/ihtml/themes/$theme/$filename",
"themes/$default_theme/$filename",
"$BASE_DIR/ihtml/themes/$default_theme/$filename",
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
"themes/default/$filename",
"$BASE_DIR/ihtml/themes/default/$filename",
$filename
];
}
foreach ($paths as $path) {
if (file_exists($path)) {
return $path;
}
}
return end($paths);
}
/**
* 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
*
* @return array<int,mixed>
*/
function array_remove_entries (array $needles, array $haystack): array
{
return array_values(array_udiff($haystack, $needles, 'array_cmp_recursive'));
}
/**
* Remove multiple entries from an array (case-insensitive)
*
* Removes every element that is in $needles from the
* array given as $haystack but case insensitive
*
* @param array<int|string|bool|null|float|double|object> $needles array of the entries to remove
*
* @param array<int|string|bool|null|float|double|object> $haystack original array to remove the entries from
*
* @return array<int,int|string|bool|null|float|double|object>
*/
function array_remove_entries_ics (array $needles, array $haystack): array
{
// strcasecmp will work, because we only compare ASCII values here
return array_values(array_udiff($haystack, $needles, 'strcasecmp'));
}
/**
* Merge to array but remove duplicate entries (case-insensitive)
*
* Merges two arrays and removes duplicate entries. Triggers
* an error if first or second parametre is not an array.
*
* @param array<int|string|bool|null|float|double|object> $ar1 first array
*
* @param array<int|string|bool|null|float|double|object> $ar2 second array
*
* @return array<int,int|string|bool|null|float|double|object>
*/
function array_merge_unique (array $ar1, array $ar2): array
{
return array_values(array_unique(array_merge($ar1, $ar2)));
}
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
/*!
* \brief Generate a system log info
*
* Creates a syslog message, containing user information.
*
* \param string $message the message to log
*/
function fusiondirectory_log ($message)
{
global $ui;
/* Preset to something reasonable */
$username = '[unauthenticated]';
/* Replace username if object is present */
if (isset($ui)) {
if ($ui->uid != '') {
$username = '['.$ui->uid.']';
} else {
$username = '[unknown]';
}
}
syslog(LOG_INFO, "FusionDirectory $username: $message");
}
/*!
* \brief Return the current userinfo object
*
* \return return the current userinfo object
*/
function &get_userinfo ()
{
global $ui;
return $ui;
}
/*!
* \brief Get global smarty object
*
* \return return the global 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: $config->current['BASE']
*
* \return a string in the form as described above
*/
function convert_department_dn ($dn, $base = NULL)
{
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
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 (explode(',', $dn) as $rdn) {
$dep = preg_replace("/^[^=]+=/", '', $rdn).'/'.$dep;
}
/* Return and remove accidently trailing slashes */
return trim($dep, '/');
}
/*! \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') . $config->current['BASE'];
* $ldap->cd($base);
* \endcode
*
* \param $name the rdn of the ou you are trying to find
*
* \return the ou associated the the RDN or nothing
*
*/
function get_ou ($name)
{
global $config;
$map = [
'fusiondirectoryRDN' => 'ou=fusiondirectory,',
'lockRDN' => 'ou=locks,',
'recoveryTokenRDN' => 'ou=recovery,',
'reminderTokenRDN' => 'ou=reminder,',
'roleRDN' => 'ou=roles,',
'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,',
'mobilePhoneRDN' => 'ou=mobile,ou=systems,',
'inventoryRDN' => 'ou=inventory,',
'ipmiRDN' => 'ou=ipmi,',
'faxBlocklistRDN' => 'ou=gofax,ou=systems,',
'aclRoleRDN' => 'ou=aclroles,',
'phoneMacroRDN' => 'ou=macros,ou=asterisk,ou=configs,ou=systems,',
'phoneConferenceRDN' => 'ou=conferences,ou=asterisk,ou=configs,ou=systems,',
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
'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,',
'debconfRDN' => 'ou=debconf,',
'supannStructuresRDN' => 'ou=structures,',
'sudoRDN' => 'ou=sudoers,',
'netgroupRDN' => 'ou=netgroup,',
'deviceRDN' => 'ou=devices,',
'aliasRDN' => 'ou=alias,',
'dsaRDN' => 'ou=dsa,',
'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])) {
return $map[$name];
} else {
return NULL;
}
if ($ou != '') {
if (!preg_match('/^[^=]+=[^=]+/', $ou)) {
$ou = "ou=$ou";
} else {
$ou = "$ou";
}
if (preg_match('/'.preg_quote($config->current['BASE'], '/').'$/', $ou)) {
return $ou;
} else {
if (preg_match('/,$/', $ou)) {
return $ou;
} else {
return "$ou,";
}
}
} else {
return '';
}
}
/*!
* \brief Get the OU for users
*
* Function for getting the userRDN
*
* \return the ou of the userRDN
*/
function get_people_ou ()
{
return get_ou('userRDN');
}
/*! \brief Return a base from a given user DN
*
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
* \code
* get_base_from_people('cn=Max Muster,dc=local')
* # Result is 'dc=local'
* \endcode
*
* \param string $dn
*
* \return the base from the 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 */
$departmentInfo = $config->getDepartmentInfo();
if (!isset($departmentInfo[$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;
}
/*!
* \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
* '<br>' for line breaks. If the value is already a string its
* returned unchanged.
*
* \param mixed $value Whatever needs to be printed.
*
* \return string $value in html form.
*/
function to_string ($value)
{
/* If this is an array, generate a text blob */
if (is_array($value)) {
$ret = '';
foreach ($value as $line) {
$ret .= $line."<br>\n";
}
return $ret;
} else {
return $value;
}
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
}
/*! \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)
{
/* Rewrite german 'umlauts' and spanish 'accents'
to get better results */
static $REWRITE = [
'ä' => 'ae',
'ö' => 'oe',
'ü' => 'ue',
'Ä' => 'Ae',
'Ö' => 'Oe',
'Ü' => 'Ue',
'ß' => 'ss',
'á' => 'a',
'é' => 'e',
'í' => 'i',
'ó' => 'o',
'ú' => 'u',
'Á' => 'A',
'É' => 'E',
'Í' => 'I',
'Ó' => 'O',
'Ú' => 'U',
'ñ' => 'ny',
'Ñ' => 'Ny',
];
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
* \param string $ou an ou to remove from the base
*
* \return base of the given DN
*/
function dn2base ($dn, $ou = NULL)
{
if ($ou === NULL) {
if (get_people_ou() != '') {
$dn = preg_replace('/,'.get_people_ou().'/i', ',', $dn);
}
if (get_ou('groupRDN') != '') {
$dn = preg_replace('/,'.get_ou('groupRDN').'/i', ',', $dn);
}
} else {
$dn = preg_replace("/,$ou/i", ',', $dn);
}
return preg_replace('/^[^,]+,/i', '', $dn);
}
/*!
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
* \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 Put netmask in n.n.n.n format
*
* \param string $netmask The netmask
*
* \return string Converted netmask
*/
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 += 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
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
*
* 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
*
* \param $netmask given netmask
*
* \return the number of bits in the netmask
*/
function netmask_to_bits ($netmask)
{
$nm = explode('.', $netmask, 4);
$res = 0;
for ($n = 0; $n < 4; $n++) {
$start = 255;
for ($i = 0; $i < 8; $i++) {
if ($start == (int)($nm[$n])) {
$res += 8 - $i;
break;
}
$start -= 2 ** $i;
}
}
return $res;
}
/*!
* \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;
default:
return $value;
}
return $mult * (int)substr($value, 0, -1);
} else {
return $value;
}
}
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
/*!
* \brief Convert a size in bytes to a human readable version
*
* \param float $bytes size in bytes
*
* \param int $precision number of digits after comma, default is 2
*
* \return Returns something like '9.77KiB' for arguments (10000, 2)
*/
function humanReadableSize ($bytes, $precision = 2)
{
$format = [
_('%sB'),
_('%sKiB'),
_('%sMiB'),
_('%sGiB'),
_('%sTiB'),
_('%sPiB'),
_('%sEiB'),
_('%sZiB'),
_('%sYiB')
];
if ($bytes == 0) {
return sprintf($format[0], '0');
}
$base = log($bytes) / log(1024);
return sprintf($format[floor($base)], round(1024 ** ($base - floor($base)), $precision));
}
/*!
* \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
*
* \return Return TRUE is value is found, FALSE if not.
*/
function in_array_ics ($value, array $items)
{
return preg_grep('/^'.preg_quote($value, '/').'$/i', $items);
}
/*!
* \brief Removes malicious characters from a (POST) string.
*
* \param string $string the string to check for malicious caracters
*
* \return string with caracters removed
*/
function validate ($string)
{
return strip_tags(str_replace('\0', '', $string));
}
/*! \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
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
*/
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
* ascending sorted manner.
*
* \param string $path
*
* \param boolean $sort_desc weither to sort the content descending.
*
* \return array content of directory in ascending sorted manner.
*/
function scan_directory ($path, $sort_desc = FALSE)
{
$ret = FALSE;
/* is this a dir ? */
/* is this path a readable one */
if (is_dir($path) && is_readable($path)) {
/* Get contents and write it into an array */
$ret = [];
$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
*
* \param string $directory smarty compile dir
*/
function clean_smarty_compile_dir ($directory)
{
if (is_dir($directory) && is_readable($directory)) {
// Set revision filename to REVISION
$revision_file = $directory."/REVISION";
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
/* Is there a stamp containing the current revision? */
if (file_exists($revision_file)) {
// check for "$config->...['CONFIG']/revision" and the
// contents should match the revision number
if (!compare_revision($revision_file, FD_VERSION)) {
// If revision differs, clean compile directory
foreach (scan_directory($directory) as $file) {
if (($file == '.') || ($file == '..')) {
continue;
}
if (is_file($directory.'/'.$file) && !unlink($directory.'/'.$file)) {
$error = new FusionDirectoryError(
htmlescape(sprintf(
_('File "%s" could not be deleted. Try "fusiondirectory-configuration-manager --check-directories" to fix permissions.'),
$directory."/".$file
))
);
$error->display();
}
}
} else {
// Revision matches, nothing to do
}
}
/* If the file does not exists or has just been deleted */
if (!file_exists($revision_file)) {
// create revision file
create_revision($revision_file, FD_VERSION);
}
}
}
/*!
* \brief Create the revision file
*
* Create the revision file in FusionDirectory spool dir
*
* \param string $revision_file the name of the revision file
*
* \param string $revision the version of FusionDirectory
*
* \return TRUE if successfully created FALSE otherwise
*/
function create_revision ($revision_file, $revision)
{
$result = FALSE;
if (is_dir(dirname($revision_file)) && is_writable(dirname($revision_file))) {
$fh = fopenWithErrorHandling($revision_file, 'w');
if (is_array($fh)) {
$error = new FusionDirectoryError(
htmlescape(_('Cannot write to revision file:')).'<br/>'.
implode(
'<br/>',
array_map('htmlescape', $fh)
)
);
$error->display();
return $result;
} else {
if (fwrite($fh, $revision)) {
$result = TRUE;
}
fclose($fh);
}
}
if (!$result) {
$error = new FusionDirectoryError(htmlescape(_('Cannot write to revision file!')));
$error->display();
981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
}
return $result;
}
/*!
* \brief Compare the revision file
*
* Create the revision file in FusionDirectory spool dir
*
* \param string $revision_file the name of the revision file
* \param string $revision the version of FusionDirectory
*
* \return TRUE if revision match FALSE otherwise
*/
function compare_revision ($revision_file, $revision)
{
// FALSE means revision differs
$result = FALSE;
if (file_exists($revision_file) && is_readable($revision_file)) {
// Open file
$fh = fopenWithErrorHandling($revision_file, 'r');
if (is_array($fh)) {
$error = new FusionDirectoryError(
htmlescape(_('Cannot read revision file:')).'<br/>'.
implode(
'<br/>',
array_map('htmlescape', $fh)
)
);
$error->display();
} else {
// Compare File contents with current revision
if ($revision == fread($fh, filesize($revision_file))) {
$result = TRUE;
}
// Close file
fclose($fh);
}
}
return $result;
}
/*!
* \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 $ikey needle
*
* \param array $items haystack
*
* \return return key or empty result
*/
function array_key_ics ($ikey, array $items)
{
$tmp = array_change_key_case($items, CASE_LOWER);
$ikey = strtolower($ikey);
if (isset($tmp[$ikey])) {
return $tmp[$ikey];
1051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
}
return '';
}
/**
* Determine if two arrays are different
*
* @param array<int|string|bool|null|float|double|object> $src The source
*
* @param array<int|string|bool|null|float|double|object> $dst The destination
*
* @return bool TRUE or FALSE
*/
function array_differs (array $src, array $dst): bool
{
/* If the count is differing, the arrays differ */
if (count($src) != count($dst)) {
return TRUE;
}
return (count(array_diff($src, $dst)) != 0);
}
/**
* Determine if two arrays are different using recursion for sublevels
*
* @param mixed $src The source
*
* @param mixed $dst The destination
*
* @return bool TRUE or FALSE
*/
function array_differs_recursive ($src, $dst): bool
{
return (array_cmp_recursive($src, $dst) !== 0);
}
/**
* Determine if two arrays are different using recursion for sublevels
*
* @param mixed $src The source
*
* @param mixed $dst The destination
*
* @return int negative, 0 or positive if $src is <, = or > $dst
*/
function array_cmp_recursive ($src, $dst): int
{
if (is_array($src)) {
if (!is_array($dst)) {
return 1;
}
if (count($src) != count($dst)) {
return count($src) - count($dst);
}
foreach ($src as $key => $value) {
if (!isset($dst[$key])) {
return 1;
}
if (($cmp = array_cmp_recursive($dst[$key], $value)) !== 0) {
return $cmp;
}
}
return 0;
}
return strcmp($src, $dst);
}
1121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
/*!
* \brief Escape all LDAP filter relevant characters
*
* \param string $input string where we should add \ before special caracters
*
*/
function normalizeLdap ($input)
{
trigger_error('deprecated, use ldap_escape_f');
return addcslashes($input, '*()\\/');
}
/*!
* \brief Check if LDAP schema matches the requirements
*
* \param string $cfg A config object
*/
function check_schema (array $cfg)
{
$checks = [];
/* Get objectclasses */
$ldapObj = new LDAP($cfg['admin'], $cfg['password'], $cfg['connection'], FALSE, $cfg['tls']);
$ldap = new ldapMultiplexer($ldapObj);
$objectclasses = $ldap->get_objectclasses(TRUE);
if (count($objectclasses) == 0) {
$warning = new FusionDirectoryWarning(htmlescape(_('Cannot get schema information from server. No schema check possible!')));
$warning->display();
return $checks;
}
/* This is the default block used for each entry.
* to avoid unset indexes.
*/
$def_check = [
'SCHEMA_FILE' => '',
'CLASSES_REQUIRED' => [],
'STATUS' => FALSE,
'IS_MUST_HAVE' => FALSE,
'MSG' => '',
'INFO' => ''
];
/* FusionDirectory core schemas */
/* core-fd */
$checks['core-fd'] = $def_check;
$checks['core-fd']['SCHEMA_FILE'] = 'core-fd.schema';
$checks['core-fd']['CLASSES_REQUIRED'] = ['fdLockEntry'];
$checks['core-fd']['IS_MUST_HAVE'] = TRUE;
$checks['core-fd']['INFO'] = _('Main FusionDirectory schema');
/* core-fd-conf */
$checks['core-fd-conf'] = $def_check;
$checks['core-fd-conf']['SCHEMA_FILE'] = 'core-fd-conf.schema';
$checks['core-fd-conf']['CLASSES_REQUIRED'] = ['fusionDirectoryConf'];
$checks['core-fd-conf']['IS_MUST_HAVE'] = TRUE;
$checks['core-fd-conf']['INFO'] = _('Schema used to store FusionDirectory configuration');
/* ldapns */
$checks['ldapns'] = $def_check;
$checks['ldapns']['SCHEMA_FILE'] = 'ldapns.schema';
$checks['ldapns']['CLASSES_REQUIRED'] = ['hostObject'];
$checks['ldapns']['IS_MUST_HAVE'] = FALSE;
$checks['ldapns']['INFO'] = _('Used to store trust mode information in users or groups.');
/* template-fd */
1191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
$checks['template-fd'] = $def_check;
$checks['template-fd']['SCHEMA_FILE'] = 'template-fd.schema';
$checks['template-fd']['CLASSES_REQUIRED'] = ['fdTemplate'];
$checks['template-fd']['IS_MUST_HAVE'] = FALSE;
$checks['template-fd']['INFO'] = _('Used to store templates.');
if (class_available('posixAccount')) {
/* nis */
$checks['nis'] = $def_check;
$checks['nis']['SCHEMA_FILE'] = 'nis.schema';
$checks['nis']['CLASSES_REQUIRED'] = ['posixAccount'];
$checks['nis']['IS_MUST_HAVE'] = FALSE;
$checks['nis']['INFO'] = _('Used to store POSIX information.');
}
foreach ($checks as $name => $value) {
foreach ($value['CLASSES_REQUIRED'] as $class) {
if (!isset($objectclasses[$class])) {
$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);
}
} else {
$checks[$name]['STATUS'] = TRUE;
$checks[$name]['MSG'] = sprintf(_('Class(es) available'));
}
}
}
$checks['posixGroup'] = $def_check;
$checks['posixGroup']['SCHEMA_FILE'] = 'nis.schema';
$checks['posixGroup']['CLASSES_REQUIRED'] = ['posixGroup'];
$checks['posixGroup']['STATUS'] = TRUE;
$checks['posixGroup']['MSG'] = '';
$checks['posixGroup']['INFO'] = '';
if (isset($objectclasses['posixGroup'])) {
$checks['posixGroup']['IS_MUST_HAVE'] = TRUE;
/* Depending on mixed groups plugin installation status, we need different schema configurations */
if (class_available('mixedGroup') && isset($objectclasses['posixGroup']['STRUCTURAL'])) {
$checks['posixGroup']['STATUS'] = FALSE;
$checks['posixGroup']['MSG'] = _('You have installed the mixed groups plugin, but your schema configuration does not support this.');
$checks['posixGroup']['INFO'] = _('In order to use mixed groups the objectClass "posixGroup" must be AUXILIARY');
} elseif (!class_available('mixedGroup') && !isset($objectclasses['posixGroup']['STRUCTURAL'])) {
$checks['posixGroup']['STATUS'] = FALSE;
$checks['posixGroup']['MSG'] = _('Your schema is configured to support mixed groups, but this plugin is not present.');
$checks['posixGroup']['INFO'] = _('The objectClass "posixGroup" must be STRUCTURAL');
}
}
return $checks;
}
/*!
* \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
*/
1261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
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;
}
return validate($_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 (array_keys($class_mapping) as $class) {
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 string $hash which hash to use to encrypt it, default is empty
* for reusing existing hash method for this password (or use the default one).
*
* \return boolean TRUE on success and an error strings array on failure.
*/
function change_password ($dn, $password, $hash = "")
{
$userTabs = objects::open($dn, 'user');
$userTab = $userTabs->getBaseObject();
$userTab->userPassword = [
$hash,
$password,
$password,
$userTab->userPassword,
$userTab->attributesAccess['userPassword']->isLocked()
];
$userTabs->update();
$error = $userTabs->save();
if (!empty($error)) {
return $error;
}
return TRUE;
}
/*!
* \brief Get the Change Sequence Number of a certain DN
*
* To verify if a given object has been changed outside of FusionDirectory
* 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 The dn you want to check
1331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
*
* \return either the result or "" in any other case
*/
function getEntryCSN (string $dn): string
{
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, [$attr]);
$attrs = $ldap->fetch();
if (isset($attrs[$attr][0])) {
return $attrs[$attr][0];
}
}
return '';
}
/*!
* \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);
/* Strip name if it is a complete path */
if (preg_match("/\//", $name)) {
$name = basename($name);
}
/* force download dialog */
header('Content-Disposition: attachment; filename="'.$name.'"');
echo $data;
exit();
}
/*!
* \brief Encode special string characters
*
* Encode the special caracters so we can use the string in
* HTML output, without breaking quotes.
*
* \param string $str The String we want to encode.
*
* \return string The encoded String
*/
function xmlentities ($str)
{
if (is_string($str)) {
return htmlspecialchars($str, ENT_QUOTES);
} elseif (is_array($str)) {
foreach ($str as $name => $value) {
$str[$name] = xmlentities($value);
1401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470
}
}
return $str;
}
/*!
* \brief Returns a random char
*/
function get_random_char ()
{
$randno = rand(0, 63);
if ($randno < 12) {
// Digits, '/' and '.'
return chr($randno + 46);
} elseif ($randno < 38) {
// Uppercase
return chr($randno + 53);
} else {
// Lowercase
return chr($randno + 59);
}
}
/*!
* \brief Decrypt a string with RIJNDAEL_128
*
* \param string $input The string to decrypt.
*
* \param String $password The password used
*/
function cred_decrypt ($input, $password)
{
/************************* Inspired by Crypt/CBC.pm *******************************/
$input = pack('H*', $input);
if (substr($input, 0, 8) != 'Salted__') {
throw new FusionDirectoryException("Invalid hash header: expected 'Salted__', found '".substr($input, 0, 8)."'");
}
$salt = substr($input, 8, 8);
$input = substr($input, 16);
$key_len = 32;
$iv_len = openssl_cipher_iv_length('aes-256-cbc');
$data = '';
$d = '';
while (strlen($data) < $key_len + $iv_len) {
$d = md5($d . $password . $salt, TRUE);
$data .= $d;
}
$key = substr($data, 0, $key_len);
$iv = substr($data, $key_len, $iv_len);
return openssl_decrypt($input, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
}
/*!
* \brief Test if an ip is the network range
*
* \param string $ip The ip address to test.
*
* \param string $net The network to test
*
* \param string $mask The netmask of the network
*/
function isIpInNet ($ip, $net, $mask)
{
// Move to long ints
$ip = ip2long($ip);
$net = ip2long($net);
$mask = ip2long($mask);
1471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
// Mask given IP with mask. If it returns "net", we're in...
return (($ip & $mask) == $net);
}
/*!
* \brief Expands an IP v6
*/
function expandIPv6 ($ip)
{
$hex = unpack('H*hex', inet_pton($ip));
return substr(preg_replace('/([A-f0-9]{4})/', "$1:", $hex['hex']), 0, -1);
}
/* Mark the occurance of a string with a span */
function mark ($needle, $haystack)
{
$result = '';
while (preg_match('/^(.*)('.preg_quote($needle).')(.*)$/i', $haystack, $matches)) {
$result .= $matches[1].'<span class="mark">'.$matches[2].'</span>';
$haystack = $matches[3];
}
return $result.$haystack;
}
function reset_errors ()
{
session::set('errorsAlreadyPosted', []);
}
function load_all_classes ()
{
global $BASE_DIR, $class_list, $class_mapping;
/* Initially load all classes */
$class_list = get_declared_classes();
foreach ($class_mapping as $class => $path) {
if (!in_array($class, $class_list)) {
if (is_readable("$BASE_DIR/$path")) {
require_once("$BASE_DIR/$path");
} else {
throw new FatalError(
sprintf(
htmlescape(_('Cannot locate file "%s" - please run "%s" to fix this')),
htmlescape("$BASE_DIR/$path"),
'<b>fusiondirectory-configuration-manager --update-cache</b>'
)
);
}
}
}
}
function ldap_escape_f ($str, $ignore = '')
{
return ldap_escape($str, $ignore, LDAP_ESCAPE_FILTER);
}
function ldap_escape_dn ($str, $ignore = '')
{
return ldap_escape($str, $ignore, LDAP_ESCAPE_DN);
}
function mail_utf8 ($to, $from_user, $from_email, $subject, $message, $replyto_user = NULL, $replyto_email = NULL, $type = 'plain')
{
$subject = "=?UTF-8?B?".base64_encode($subject)."?=";
if ($replyto_user === NULL) {
$replyto_user = $from_user;
154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609
}
if ($replyto_email === NULL) {
$replyto_email = $from_email;
}
if ($from_user) {
$from_user = "=?UTF-8?B?".base64_encode($from_user)."?=";
$headers = "From: $from_user <$from_email>\r\n";
} else {
$headers = "From: <$from_email>\r\n";
}
if ($replyto_email) {
if ($replyto_user) {
$replyto_user = "=?UTF-8?B?".base64_encode($replyto_user)."?=";
$headers .= "Reply-To: $replyto_user <$replyto_email>\r\n";
} else {
$headers .= "Reply-To: <$replyto_email>\r\n";
}
}
$headers .= "MIME-Version: 1.0" . "\r\n" .
"Content-type: text/$type; charset=UTF-8" . "\r\n";
$additional_parameters = "-f".$from_email;
return mail($to, $subject, $message, $headers, $additional_parameters);
}
/* Calls fopen, gives errors as an array if any, file handle if successful */
function fopenWithErrorHandling (...$args)
{
$errors = [];
set_error_handler(
function (int $errno, string $errstr, string $errfile, int $errline, array $errcontext) use (&$errors): bool
{
$errors[] = $errstr;
return TRUE;
}
);
$fh = @fopen(...$args);
restore_error_handler();
if ($fh !== FALSE) {
return $fh;
}
return $errors;
}
// Check to see if it exists in case PHP has this function later
if (!function_exists('mb_substr_replace')) {
// Same parameters as substr_replace with the extra encoding parameter
function mb_substr_replace (string $string, string $replacement, $start, $length = NULL, $encoding = NULL)
{
if ($encoding === NULL) {
$encoding = mb_internal_encoding();
}
if ($length === NULL) {
return mb_substr($string, 0, $start, $encoding).
$replacement;
} else {
return mb_substr($string, 0, $start, $encoding).
$replacement.
mb_substr($string, $start + $length, NULL, $encoding);
}
}
}