class_config.inc 39.01 KiB
<?php
/*
  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
  Copyright (C) 2003-2010  Cajus Pollmeier
  Copyright (C) 2011-2017  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 class_config.inc
 *  Source code for the class config
/*!
 * \brief This class is responsible for parsing and querying the
 * fusiondirectory configuration file.
class config
  /* XML parser */
  var $parser;
  var $config_found     = FALSE;
  var $tags             = [];
  var $level            = 0;
  var $currentLocation  = '';
  /*!
   * \brief Store configuration for current location
  var $current = [];
  /* Link to LDAP-server */
  protected $ldapLink = NULL;
  var $referrals      = [];
   * \brief Configuration data
   * - $data['SERVERS'] contains server informations.
  var $data = [
    'LOCATIONS' => [],
    'SERVERS'   => [],
    'MAIN'      => [],
  var $basedir        = '';
  /* Keep a copy of the current department list */
  protected $departmentList;
  protected $departmentTree;
  protected $departmentInfo;
  var $filename         = '';
  var $last_modified    = 0;
  /*!
   * \brief Class constructor of the config class
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
* * \param string $filename path to the configuration file * * \param string $basedir base directory */ function __construct ($filename, $basedir = '') { $this->basedir = $basedir; /* Parse config file directly? */ if ($filename != '') { $this->parse($filename); } } /*! * \brief Check and reload the configuration * * This function checks if the configuration has changed, since it was * read the last time and reloads it. It uses the file mtime to check * weither the file changed or not. */ function check_and_reload ($force = FALSE) { /* Check if class_location.inc has changed, this is the case if we have installed or removed plugins. */ $tmp = stat(CACHE_DIR.'/'.CLASS_CACHE); if (session::is_set('class_location.inc:timestamp') && ($tmp['mtime'] != session::get('class_location.inc:timestamp'))) { session::un_set('plist'); } session::set('class_location.inc:timestamp', $tmp['mtime']); if (($this->filename != '') && ((filemtime($this->filename) != $this->last_modified) || $force)) { $this->config_found = FALSE; $this->tags = []; $this->level = 0; $this->currentLocation = ''; $this->parse($this->filename); $this->set_current($this->current['NAME']); } } /*! * \brief Parse the given configuration file * * Parses the configuration file and displays errors if there * is something wrong with it. * * \param string $filename The filename of the configuration file. */ function parse ($filename) { $this->last_modified = filemtime($filename); $this->filename = $filename; $fh = fopen($filename, 'r'); $xmldata = fread($fh, 100000); fclose($fh); $this->parse_data($xmldata); } function parse_data ($xmldata) { $this->data = [ 'LOCATIONS' => [], 'SERVERS' => [], 'MAIN' => [], ];
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
$this->parser = xml_parser_create(); xml_set_object($this->parser, $this); xml_set_element_handler($this->parser, "tag_open", "tag_close"); if (!xml_parse($this->parser, chop($xmldata))) { $msg = sprintf(_('XML error in fusiondirectory.conf: %s at line %d'), xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser)); throw new FatalError(htmlescape($msg)); } xml_parser_free($this->parser); } /*! * \brief Open xml tag when parsing the xml config * * \param string $parser * * \param string $tag * * \param string $attrs */ function tag_open ($parser, $tag, $attrs) { /* Save last and current tag for reference */ $this->tags[$this->level] = $tag; $this->level++; /* Trigger on CONF section */ if ($tag == 'CONF') { $this->config_found = TRUE; } /* Return if we're not in config section */ if (!$this->config_found) { return; } /* yes/no to true/false and upper case TRUE to true and so on*/ foreach ($attrs as $name => $value) { if (preg_match("/^(true|yes)$/i", $value)) { $attrs[$name] = "TRUE"; } elseif (preg_match("/^(false|no)$/i", $value)) { $attrs[$name] = "FALSE"; } } /* Look through attributes */ switch ($this->tags[$this->level - 1]) { /* Handle location */ case 'LOCATION': if ($this->tags[$this->level - 2] == 'MAIN') { $attrs['NAME'] = preg_replace('/[<>"\']/', '', $attrs['NAME']); $this->currentLocation = $attrs['NAME']; /* Add location elements */ $this->data['LOCATIONS'][$attrs['NAME']] = $attrs; } break; /* Handle referral tags */ case 'REFERRAL': if ($this->tags[$this->level - 2] == 'LOCATION') { if (isset($attrs['BASE'])) { $server = $attrs['URI']; } elseif (isset($this->data['LOCATIONS'][$this->currentLocation]['BASE'])) { /* Fallback on location base */ $server = $attrs['URI']; $attrs['BASE'] = $this->data['LOCATIONS'][$this->currentLocation]['BASE'];
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
} else { /* Format from FD<1.3 */ $server = preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $attrs['URI']); $attrs['BASE'] = preg_replace('!^[^:]+://[^/]+/(.*)$!', '\\1', $attrs['URI']); $attrs['URI'] = $server; } /* Add location elements */ if (!isset($this->data['LOCATIONS'][$this->currentLocation]['REFERRAL'])) { $this->data['LOCATIONS'][$this->currentLocation]['REFERRAL'] = []; } $this->data['LOCATIONS'][$this->currentLocation]['REFERRAL'][$server] = $attrs; } break; /* Load main parameters */ case 'MAIN': $this->data['MAIN'] = array_merge($this->data['MAIN'], $attrs); break; /* Ignore other tags */ default: break; } } /*! * \brief Close xml tag when parsing the xml config * * \param string $parser * * \param string $tag */ function tag_close ($parser, $tag) { /* Close config section */ if ($tag == 'CONF') { $this->config_found = FALSE; } $this->level--; } /*! * \brief Get the password when needed from the config file * * This function can be used to get the password associated to * a keyword in the config file * * \param string $creds the keyword associated to the password needed * * \return string the password corresponding to the keyword */ function get_credentials ($creds) { if (isset($_SERVER['HTTP_FDKEY'])) { if (!session::is_set('HTTP_FDKEY_CACHE')) { session::set('HTTP_FDKEY_CACHE', []); } $cache = session::get('HTTP_FDKEY_CACHE'); if (!isset($cache[$creds])) { try { $cache[$creds] = cred_decrypt($creds, $_SERVER['HTTP_FDKEY']); session::set('HTTP_FDKEY_CACHE', $cache); } catch (FusionDirectoryException $e) { $msg = nl2br(htmlescape(sprintf( _('It seems you are trying to decode something which is not encoded : %s'."\n". 'Please check you are not using a fusiondirectory.secrets file while your passwords are not encrypted.'), $e->getMessage() )));
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
throw new FatalError($msg); } } return $cache[$creds]; } return $creds; } /*! * \brief Get a LDAP link object * * This function can be used to get an ldap object, which in turn can * be used to query the LDAP. See the LDAP class for more information * on how to use it. * * Example usage: * \code * $ldap = $config->get_ldap_link(); * \endcode * * \param boolean $sizelimit Weither to impose a sizelimit on the LDAP object or not. * Defaults to false. If set to true, the size limit in the configuration * file will be used to set the option LDAP_OPT_SIZELIMIT. * * \return ldapMultiplexer object */ function get_ldap_link ($sizelimit = FALSE) { global $ui; if ($this->ldapLink === NULL || !is_resource($this->ldapLink->cid)) { /* Build new connection */ $this->ldapLink = ldap_init($this->current['SERVER'], $this->current['BASE'], $this->current['ADMINDN'], $this->get_credentials($this->current['ADMINPASSWORD'])); /* Check for connection */ if (is_null($this->ldapLink) || (is_int($this->ldapLink) && $this->ldapLink == 0)) { throw new FatalError(htmlescape(_('Cannot bind to LDAP. Please contact the system administrator.'))); } /* Move referrals */ if (!isset($this->current['REFERRAL'])) { $this->ldapLink->referrals = []; } else { $this->ldapLink->referrals = $this->current['REFERRAL']; } } $obj = new ldapMultiplexer($this->ldapLink); if ($sizelimit) { $obj->set_size_limit($ui->getSizeLimitHandler()->getSizeLimit()); } else { $obj->set_size_limit(0); } return $obj; } /*! * \brief Set the current location * * \param string $name the name of the location */ function set_current ($name) { global $ui; if (!isset($this->data['LOCATIONS'][$name])) { throw new FatalError(htmlescape(sprintf(_('Location "%s" could not be found in the configuration file'), $name))); } $this->current = $this->data['LOCATIONS'][$name];
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
if (isset($this->current['INITIAL_BASE']) && isset($ui)) { $ui->setCurrentBase($this->current['INITIAL_BASE']); } /* Sort referrals, if present */ if (isset($this->current['REFERRAL'])) { $servers = []; foreach ($this->current['REFERRAL'] as $server => $ref) { $servers[$server] = strlen($ref['BASE']); } asort($servers); reset($servers); } /* SERVER not defined? Load the one with the shortest base */ if (!isset($this->current['SERVER'])) { $this->current['SERVER'] = key($servers); } /* Parse LDAP referral informations */ if (!isset($this->current['ADMINDN']) || !isset($this->current['ADMINPASSWORD'])) { $this->current['BASE'] = $this->current['REFERRAL'][$this->current['SERVER']]['BASE']; $this->current['ADMINDN'] = $this->current['REFERRAL'][$this->current['SERVER']]['ADMINDN']; $this->current['ADMINPASSWORD'] = $this->current['REFERRAL'][$this->current['SERVER']]['ADMINPASSWORD']; } /* Load in-ldap configuration */ $this->load_inldap_config(); /* Parse management config */ $this->loadManagementConfig(); if (class_available('systemManagement')) { /* Load server informations */ $this->load_servers(); } $debugLevel = $this->get_cfg_value('DEBUGLEVEL'); if ($debugLevel & DEBUG_CONFIG) { /* Value from LDAP can't activate DEBUG_CONFIG */ $debugLevel -= DEBUG_CONFIG; } if (isset($this->data['MAIN']['DEBUGLEVEL'])) { $debugLevel |= $this->data['MAIN']['DEBUGLEVEL']; } session::set('DEBUGLEVEL', $debugLevel); IconTheme::loadThemes('themes'); timezone::setDefaultTimezoneFromConfig(); Language::init(); } /*! * \brief Load server information from config/LDAP * * This function searches the LDAP for servers (e.g. goImapServer, goMailServer etc.) * and stores information about them $this->data['SERVERS']. In the case of mailservers * the main section of the configuration file is searched, too. */ function load_servers () { /* Only perform actions if current is set */ if ($this->current === NULL) { return; } $ldap = $this->get_ldap_link();
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
/* Get samba servers from LDAP */ $this->data['SERVERS']['SAMBA'] = []; if (class_available('sambaAccount')) { $ldap->cd($this->current['BASE']); $ldap->search('(objectClass=sambaDomain)'); while ($attrs = $ldap->fetch()) { $this->data['SERVERS']['SAMBA'][$attrs['sambaDomainName'][0]] = [ 'SID' => '','RIDBASE' => '']; if (isset($attrs['sambaSID'][0])) { $this->data['SERVERS']['SAMBA'][$attrs['sambaDomainName'][0]]['SID'] = $attrs['sambaSID'][0]; } if (isset($attrs['sambaAlgorithmicRidBase'][0])) { $this->data['SERVERS']['SAMBA'][$attrs['sambaDomainName'][0]]['RIDBASE'] = $attrs['sambaAlgorithmicRidBase'][0]; } } /* If no samba servers are found, look for configured sid/ridbase */ if ((count($this->data['SERVERS']['SAMBA']) == 0) && isset($this->current['SAMBASID']) && isset($this->current['SAMBARIDBASE'])) { $this->data['SERVERS']['SAMBA']['DEFAULT'] = [ 'SID' => $this->get_cfg_value('SAMBASID'), 'RIDBASE' => $this->get_cfg_value('SAMBARIDBASE') ]; } } } /* Check that configuration is in LDAP, check that no plugin got installed since last configuration update */ function checkLdapConfig ($forceReload = FALSE) { global $ui; $dn = CONFIGRDN.$this->current['BASE']; if (!$forceReload) { $ldap = $this->get_ldap_link(); $ldap->cat($dn, ['fusionConfigMd5']); if (($attrs = $ldap->fetch()) && isset($attrs['fusionConfigMd5'][0]) && ($attrs['fusionConfigMd5'][0] == md5_file(CACHE_DIR.'/'.CLASS_CACHE))) { return; } } add_lock($dn, $ui->dn); $config_plugin = objects::open($dn, 'configuration'); $config_plugin->save_object(); $config_plugin->save(); del_lock($dn); } function load_inldap_config () { $ldap = $this->get_ldap_link(); $ldap->cat(CONFIGRDN.$this->current['BASE']); if ($attrs = $ldap->fetch()) { for ($i = 0; $i < $attrs['count']; $i++) { $key = $attrs[$i]; if (preg_match('/^fdTabHook$/i', $key)) { for ($j = 0; $j < $attrs[$key]['count']; ++$j) { $parts = explode('|', $attrs[$key][$j], 3); $class = strtoupper($parts[0]); $mode = strtoupper($parts[1]); $cmd = $parts[2]; if (!isset($cmd[0]) || ($cmd[0] == '#')) { /* Ignore commented out and empty triggers */ continue; } if (!isset($this->data['HOOKS'][$class])) { $this->data['HOOKS'][$class] = ['CLASS' => $parts[0]]; } if (!isset($this->data['HOOKS'][$class][$mode])) {
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
$this->data['HOOKS'][$class][$mode] = []; } $this->data['HOOKS'][$class][$mode][] = $cmd; } } elseif (preg_match('/^fd/', $key)) { if (isset($attrs[$key]['count']) && ($attrs[$key]['count'] > 1)) { $value = $attrs[$key]; unset($value['count']); } else { $value = $attrs[$key][0]; } $key = strtoupper(preg_replace('/^fd/', '', $key)); $this->current[$key] = $value; } } } } /*! * \brief Loads the management classes config to index them by class */ private function loadManagementConfig () { if (isset($this->current['MANAGEMENTCONFIG'])) { if (!is_array($this->current['MANAGEMENTCONFIG'])) { $this->current['MANAGEMENTCONFIG'] = [$this->current['MANAGEMENTCONFIG']]; } $value = []; foreach ($this->current['MANAGEMENTCONFIG'] as $config) { list($class, $json) = explode(':', $config, 2); $value[$class] = $json; } $this->current['MANAGEMENTCONFIG'] = $value; } if (isset($this->current['MANAGEMENTUSERCONFIG'])) { if (!is_array($this->current['MANAGEMENTUSERCONFIG'])) { $this->current['MANAGEMENTUSERCONFIG'] = [$this->current['MANAGEMENTUSERCONFIG']]; } $value = []; foreach ($this->current['MANAGEMENTUSERCONFIG'] as $config) { list($user, $class, $json) = explode(':', $config, 3); $value[$user][$class] = $json; } $this->current['MANAGEMENTUSERCONFIG'] = $value; } } /*! * \brief Update the management config in the LDAP and the cache */ public function updateManagementConfig (string $managementClass, $managementConfig, bool $userConfig = FALSE): array { global $ui; $changes = []; if ($userConfig) { if (!isset($this->current['MANAGEMENTUSERCONFIG'][$ui->dn])) { $this->current['MANAGEMENTUSERCONFIG'][$ui->dn] = []; } $currentConfig =& $this->current['MANAGEMENTUSERCONFIG'][$ui->dn]; $attrib = 'fdManagementUserConfig'; $prefix = $ui->dn.':'.$managementClass; } else { if (!isset($this->current['MANAGEMENTCONFIG'])) { $this->current['MANAGEMENTCONFIG'] = []; } $currentConfig =& $this->current['MANAGEMENTCONFIG']; $attrib = 'fdManagementConfig'; $prefix = $managementClass; }
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
if ($managementConfig !== NULL) { $managementConfig = json_encode($managementConfig); } if (isset($currentConfig[$managementClass])) { /* If there already was a config for this class, remove it */ if ($currentConfig[$managementClass] === $managementConfig) { /* Unless it's the same one and we've got nothing to do */ return []; } $changes[] = [ 'attrib' => $attrib, 'modtype' => LDAP_MODIFY_BATCH_REMOVE, 'values' => [$prefix.':'.$currentConfig[$managementClass]], ]; } if ($managementConfig !== NULL) { /* Add the new one, if any */ $changes[] = [ 'attrib' => $attrib, 'modtype' => LDAP_MODIFY_BATCH_ADD, 'values' => [$prefix.':'.$managementConfig], ]; } $ldap = $this->get_ldap_link(); $ldap->cd(CONFIGRDN.$this->current['BASE']); if (!$ldap->modify_batch($changes)) { return [$ldap->get_error()]; } if ($managementConfig !== NULL) { $currentConfig[$managementClass] = $managementConfig; } else { unset($currentConfig[$managementClass]); } return []; } /*! * \brief Test if there is a stored management config */ public function hasManagementConfig (string $managementClass, bool $userConfig = FALSE): bool { global $ui; if ($userConfig) { return isset($this->current['MANAGEMENTUSERCONFIG'][$ui->dn][$managementClass]); } else { return isset($this->current['MANAGEMENTCONFIG'][$managementClass]); } } /*! * \brief Returns the config for a management class, or NULL */ public function getManagementConfig ($managementClass) { global $ui; if (isset($this->current['MANAGEMENTUSERCONFIG'][$ui->dn][$managementClass])) { return json_decode($this->current['MANAGEMENTUSERCONFIG'][$ui->dn][$managementClass], TRUE); } elseif (isset($this->current['MANAGEMENTCONFIG'][$managementClass])) { return json_decode($this->current['MANAGEMENTCONFIG'][$managementClass], TRUE); } else { return NULL; } }