composer.json config/config.ini.sample lib/Kolab web/index.php
Thomas Brüderli
bruederli at kolabsys.com
Wed May 1 15:00:09 CEST 2013
composer.json | 2
config/config.ini.sample | 12 -
lib/Kolab/Config.php | 284 +++++++++++++++++++++++++++++++++++
lib/Kolab/FreeBusy/Config.php | 173 ---------------------
lib/Kolab/FreeBusy/Directory.php | 2
lib/Kolab/FreeBusy/DirectoryLDAP.php | 2
web/index.php | 12 -
7 files changed, 299 insertions(+), 188 deletions(-)
New commits:
commit 5b2bbaa400888eb9cdfe1cd512971036f2d494ba
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed May 1 14:59:58 2013 +0200
Use the generic Kolab Config class for .ini file parsing. The user of the class can specify the extected type of a config option Fixes #1825
diff --git a/composer.json b/composer.json
index 910564b..74048b4 100644
--- a/composer.json
+++ b/composer.json
@@ -2,7 +2,7 @@
"name": "kolab/free-busy",
"description": "Kolab Free/Busy Service",
"license": "AGPL-3.0",
- "version": "0.1.1",
+ "version": "0.1.2",
"repositories": [
{
"type": "pear",
diff --git a/config/config.ini.sample b/config/config.ini.sample
index 62437d3..778f15a 100644
--- a/config/config.ini.sample
+++ b/config/config.ini.sample
@@ -19,10 +19,10 @@
;; Allow privileged access from these IPs
[trustednetworks]
-allow[] = 127.0.0.1
-allow[] = 192.168.0.0/16
-allow[] = 10.10.*
-allow[] = ::1
+allow = 127.0.0.1,
+ 192.168.0.0/16,
+ 10.10.*,
+ ::1
;; Logging configuration
[log]
@@ -47,8 +47,8 @@ bind_dn = "uid=kolab-service,ou=Special Users,dc=yourdomain,dc=com"
bind_pw = "<service-bind-pw>"
base_dn = "dc=yourdomain,dc=com"
filter = "(&(objectClass=kolabInetOrgPerson)(|(uid=%s)(mail=%s)(alias=%s)))"
-attributes[] = mail
-lc_attributes[] = mail
+attributes = mail, sn
+lc_attributes = sn
fbsource = file:/www/kolab-freebusy/data/%mail.ifb
loglevel = 100 ; Debug
diff --git a/lib/Kolab/Config.php b/lib/Kolab/Config.php
new file mode 100644
index 0000000..30cebbf
--- /dev/null
+++ b/lib/Kolab/Config.php
@@ -0,0 +1,284 @@
+<?php
+
+/**
+ * Model class to give access to service configuration
+ *
+ * This file is part of the Kolab PHP Utilities library
+ *
+ * @author Thomas Bruederli <bruederli at kolabsys.com>
+ *
+ * Copyright (C) 2013, Kolab Systems AG <contact at kolabsys.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Kolab;
+
+/**
+ * Wrapper class for service configuration
+ */
+class Config
+{
+ const STRING = 0;
+ const BOOL = 1;
+ const INT = 2;
+ const FLOAT = 3;
+ const ARR = 4;
+
+ protected static $instance;
+
+ protected $env = '';
+ protected $basedir = '../config';
+ protected $data = array();
+ protected $valid = false;
+
+ /**
+ * Singelton getter
+ *
+ * @param string Path to load config from
+ */
+ public static function get_instance($env = '', $dir = null)
+ {
+ if (!isset(self::$instance)) {
+ self::$instance = new Config($env);
+ if ($dir) self::$instance->basedir = $dif;
+ }
+
+ if (!self::$instance->valid) {
+ self::$instance->load('config.ini');
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Default constructor
+ */
+ function __construct($env = '')
+ {
+ $this->env = $env;
+ $this->load($this->basedir . '/config.ini');
+ }
+
+ /**
+ * Load config from the given .ini file
+ */
+ private function load($file, $use_env = true)
+ {
+ // check for relative path
+ if (!is_readable($inifile) && is_readable($this->basedir . '/' . $inifile)) {
+ $inifile = $this->basedir . '/' . $inifile;
+ }
+
+ $inifile = $this->resolve_path($file, $use_env);
+ if ($raw = self::parse_ini_file($inifile, true)) {
+ foreach ($raw as $section => $values) {
+ $sub = null;
+ if (preg_match('/^(\w+)\s+"?(.+)"?$/Ui', $section, $m)) {
+ $section = $m[1];
+ $sub = trim($m[2], '"');
+ }
+ if (!empty($sub) && !empty($values)) {
+ $config[$section][$sub] = $values;
+ }
+ else if (!empty($values) && is_array($values)) {
+ $config[$section] = $values;
+ }
+ }
+
+ $this->register($config);
+ $this->valid = !empty($this->data);
+ }
+ else {
+ trigger_error("Failed to parse configuration from $inifile", E_USER_ERROR);
+ }
+ }
+
+ /**
+ * Helper method to resolve the absolute path to the given config file.
+ * This also takes the 'env' property into account.
+ */
+ private function resolve_path($file, $use_env)
+ {
+ if ($file[0] != '/') {
+ $file = realpath($this->basedir . '/' . $file);
+ }
+
+ // check if <file>-env.ini exists
+ if ($file && $use_env && !empty($this->env)) {
+ $envfile = preg_replace('/\.(ini|conf)$/', '-' . $this->env . '.\\1', $file);
+ if (is_file($envfile))
+ return $envfile;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Replacement for PHP's parse_ini_file()
+ */
+ private static function parse_ini_file($filename)
+ {
+ $raw = array();
+ foreach (file($filename) as $_line) {
+ if ($_line[0] == ';') // skip comments
+ continue;
+
+ if (preg_match('/^\[([a-z0-9-_\.]+[^\]]*)\]/', $_line, $matches)) {
+ $_cur_section = $matches[1];
+ $raw[$_cur_section] = array();
+ unset($_cur_key);
+ }
+
+ if (preg_match('/^([a-z0-9\.-_]+)\s*=\s*(.*)/', $_line, $matches)) {
+ if (isset($_cur_section) && !empty($_cur_section)) {
+ $_cur_key = $matches[1];
+ $raw[$_cur_section][$matches[1]] = isset($matches[2]) ? trim($matches[2], ' "') : '';
+ }
+ }
+ else if (preg_match('/^\s+(.*)$/', $_line, $matches)) {
+ if (isset($_cur_key) && !empty($_cur_key)) {
+ $raw[$_cur_section][$_cur_key] .= $matches[1];
+ }
+ }
+ }
+
+ return $raw;
+ }
+
+ /**
+ * Dump the hierarchical structure of config options into a flat list with keys delimited by dots
+ */
+ private function register($config, $prefix = '')
+ {
+ // merge the new config values over existing data
+ if (empty($prefix)) {
+ $this->data = array_replace_recursive($this->data, $config);
+ }
+ else if (is_array($config)) {
+ $pkey = rtrim($prefix, '.');
+ $this->data[$pkey] = is_array($this->data[$pkey]) ? array_replace_recursive((array)$this->data[$pkey], $config) : $config;
+ }
+
+ foreach ((array)$config as $key => $val) {
+ if (is_array($val)) {
+ $this->register($val, "$prefix$key.");
+ }
+ else {
+ $this->data[$prefix.$key] = $val;
+ }
+ }
+
+ // resolve references in config options (e.g. %(foo.bar))
+ if (empty($prefix)) {
+ array_walk_recursive($this->data, array($this, 'resolve_reference'));
+ }
+ }
+
+ /**
+ * Callback to resolve references in the given config option value
+ */
+ private function resolve_reference(&$value, $key)
+ {
+ if (is_string($value)) {
+ $value = preg_replace_callback('/%[({]([\w.]+)[})]/i', array($this, 'replace_reference'), $value);
+ }
+ }
+
+ /**
+ * Callback function to replace the given reference with the read config value
+ */
+ private function replace_reference($m)
+ {
+ return $this->data[$m[1]];
+ }
+
+ /**
+ * Magic getter for direct read-only access to config options
+ */
+ public function __get($name)
+ {
+ return $this->data[$name];
+ }
+
+ /**
+ * Magic isset check
+ */
+ public function __isset($name)
+ {
+ return array_key_exists($name, $this->data);
+ }
+
+ /**
+ * Common getter for config options with fallback to default values
+ *
+ * @param string Config option name
+ * @param mixed Default value if option isn't set in config
+ * @param integer Expected variable type
+ * @return mixed Config option value
+ */
+ public function get($name, $default = null, $type = null)
+ {
+ switch ($name) {
+ case 'output.tempdir':
+ case 'session.savepath':
+ // return an absolute path for relative directory properties
+ if (isset($this->data[$name]) && $this->data[$name][0] != '/') {
+ $value = realpath(INSTALL_PATH . '/' . $this->data[$name]);
+ break;
+ }
+
+ default:
+ $value = array_key_exists($name, $this->data) ? $this->data[$name] : $default;
+ }
+
+ // convert value to the requested type
+ return $type ? self::convert($value, $type) : value;
+ }
+
+ /**
+ * Determines whether we have a valid configuration loaded
+ *
+ * @return boolean True if valid, False otherwise
+ */
+ public function valid()
+ {
+ return !empty($this->data);
+ }
+
+ /**
+ * Convert the given (string) value to the requested type
+ *
+ * @param string Config value
+ * @param int Output type (one of this class constants)
+ * @return mixed The converted value
+ */
+ public static function convert($value, $type)
+ {
+ // convert value to the requested type
+ switch ($type) {
+ case self::INT:
+ return intval($value);
+ case self::FLOAT:
+ return floatval($value);
+ case self::BOOL:
+ return (bool)preg_match('/^(true|1|on|enabled|yes)$/i', $value);
+ case self::ARR:
+ return preg_split('/,\s*/', strval($value));
+ }
+
+ return $value;
+ }
+}
+
diff --git a/lib/Kolab/FreeBusy/Config.php b/lib/Kolab/FreeBusy/Config.php
deleted file mode 100644
index ab82a1a..0000000
--- a/lib/Kolab/FreeBusy/Config.php
+++ /dev/null
@@ -1,173 +0,0 @@
-<?php
-
-/**
- * This file is part of the Kolab Server Free/Busy Service
- *
- * @author Thomas Bruederli <bruederli at kolabsys.com>
- *
- * Copyright (C) 2013, Kolab Systems AG <contact at kolabsys.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-namespace Kolab\FreeBusy;
-
-/**
- * Wrapper class for service configuration
- */
-class Config
-{
- private static $instance;
-
- private $basedir = '.';
- private $data = array();
- private $valid = false;
-
- /**
- * Singelton getter
- *
- * @param string Path to load config from
- */
- public static function getInstance($dir = null)
- {
- if (!isset(self::$instance)) {
- self::$instance = new Config($dir);
- }
-
- if ($dir && !self::$instance->valid) {
- self::$instance->load($configdir . '/config.ini');
- }
-
- return self::$instance;
- }
-
- /**
- * Default constructor
- */
- function __construct($configdir = null)
- {
- if ($configdir) {
- $this->basedir = $configdir;
- $this->load($configdir . '/config.ini');
- }
- }
-
- /**
- * Load config from the given .ini file
- */
- private function load($inifile)
- {
- if ($raw = parse_ini_file($inifile, true)) {
- $config['directories'] = array();
- foreach ($raw as $section => $values) {
- // check for known sections
- if (in_array($section, array('httpauth','trustednetworks','log'))) {
- $config[$section] = $values;
- }
- else if (strpos($section, 'directory') === 0 || isset($values['fbsource'])) {
- $sect = preg_replace('/^directory\s*/', '', $section);
- $key = strlen($sect) ? $sect : count($config['directories']);
- $config['directories'][$key] = $values;
- }
- else if (!empty($values) && is_array($values)) {
- $config[$section] = $values;
- }
- }
-
- $this->register($config);
- $this->valid = !empty($this->data['directories']);
- }
- else {
- trigger_error("Failed to parse configuration from $inifile", E_USER_ERROR);
- }
- }
-
- /**
- * Dump the hierarchical structure of config options into a flat list with keys delimited by dots
- */
- private function register($config, $prefix = '')
- {
- // merge the new config values over existing data
- if (empty($prefix)) {
- $this->data = array_replace_recursive($this->data, $config);
- }
- else if (is_array($config)) {
- $pkey = rtrim($prefix, '.');
- $this->data[$pkey] = is_array($this->data[$pkey]) ? array_replace_recursive((array)$this->data[$pkey], $config) : $config;
- }
-
- foreach ((array)$config as $key => $val) {
- if (is_array($val)) {
- $this->register($val, "$prefix$key.");
- }
- else {
- $this->data[$prefix.$key] = $val;
- }
- }
-
- // resolve references in config options (e.g. %(foo.bar))
- if (empty($prefix)) {
- array_walk_recursive($this->data, array($this, 'resolve_reference'));
- }
- }
-
- /**
- * Callback to resolve references in the given config option value
- */
- private function resolve_reference(&$value, $key)
- {
- if (is_string($value)) {
- $value = preg_replace_callback('/%[({]([\w.]+)[})]/i', array($this, 'replace_reference'), $value);
- }
- }
-
- /**
- * Callback function to replace the given reference with the read config value
- */
- private function replace_reference($m)
- {
- return $this->data[$m[1]];
- }
-
- /**
- * Magic getter for direct read-only access to config options
- */
- public function __get($name)
- {
- return $this->data[$name];
- }
-
- /**
- * Common getter for config options with fallback in default values
- *
- * @param string Config option name
- * @param mixed Default value if option isn't set in config
- * @return mixed Config option value
- */
- public function get($name, $default = null)
- {
- return array_key_exists($name, $this->data) ? $this->data[$name] : $default;
- }
-
- /**
- * Determines whether we have a valid configuration loaded
- *
- * @return boolean True if valid, False otherwise
- */
- public function isValid()
- {
- return !empty($this->data);
- }
-}
-
diff --git a/lib/Kolab/FreeBusy/Directory.php b/lib/Kolab/FreeBusy/Directory.php
index 1c70f75..6478e85 100644
--- a/lib/Kolab/FreeBusy/Directory.php
+++ b/lib/Kolab/FreeBusy/Directory.php
@@ -93,7 +93,7 @@ abstract class Directory
protected function postprocessAttrib($attrib)
{
if (!empty($this->config['lc_attributes'])) {
- foreach ((array)$this->config['lc_attributes'] as $key) {
+ foreach (Config::convert($this->config['lc_attributes'], Config::ARR) as $key) {
if (!empty($attrib[$key]))
$attrib[$key] = strtolower($attrib[$key]);
}
diff --git a/lib/Kolab/FreeBusy/DirectoryLDAP.php b/lib/Kolab/FreeBusy/DirectoryLDAP.php
index 5e31e24..f462f79 100644
--- a/lib/Kolab/FreeBusy/DirectoryLDAP.php
+++ b/lib/Kolab/FreeBusy/DirectoryLDAP.php
@@ -49,7 +49,7 @@ class DirectoryLDAP extends Directory
'port' => $host['port'] ?: 389,
'use_tls' => $host['scheme'] == 'tls' || $host['scheme'] == 'ldaps',
'root_dn' => $config['base_dn'],
- 'return_attributes' => (array)$config['attributes'],
+ 'return_attributes' => Config::convert($config['attributes'], Config::ARR),
'log_hook' => array($this, 'log'),
) + $config;
diff --git a/web/index.php b/web/index.php
index 93a998a..fcbce69 100644
--- a/web/index.php
+++ b/web/index.php
@@ -5,7 +5,7 @@
*
* This is the public API to provide Free/Busy information for Kolab users.
*
- * @version 0.1.1
+ * @version 0.1.2
* @author Thomas Bruederli <bruederli at kolabsys.com>
*
* Copyright (C) 2013, Kolab Systems AG <contact at kolabsys.com>
@@ -33,19 +33,19 @@ ini_set('error_reporting', E_ALL &~ E_NOTICE);
// use composer's autoloader for both dependencies and local lib
require_once KOLAB_FREEBUSY_ROOT . '/vendor/autoload.php';
+use Kolab\Config;
use Kolab\FreeBusy\Utils;
-use Kolab\FreeBusy\Config;
use Kolab\FreeBusy\Logger;
use Kolab\FreeBusy\Directory;
use Kolab\FreeBusy\HTTPAuth;
// load config
-$config = Config::getInstance(KOLAB_FREEBUSY_ROOT . '/config');
-if ($config->isValid()) {
+$config = Config::get_instance(KOLAB_FREEBUSY_ROOT . '/config');
+if ($config->valid()) {
// check for trusted IP first
$remote_ip = Utils::remoteIP();
- $trusted_ip = $config->trustednetworks ? Utils::checkIPRange($remote_ip, $config->trustednetworks['allow']) : false;
+ $trusted_ip = $config->trustednetworks ? Utils::checkIPRange($remote_ip, $config->get('trustednetworks.allow', array(), Config::ARR)) : false;
$log = Logger::get('web');
@@ -85,7 +85,7 @@ if ($config->isValid()) {
}
// iterate over directories
- foreach ($config->directories as $key => $dirconfig) {
+ foreach ($config->directory as $key => $dirconfig) {
$log->addDebug("Trying directory $key", $dirconfig);
$directory = Directory::factory($dirconfig);
More information about the commits
mailing list