Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2529474
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
19 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/composer.json b/composer.json
index 94eb2f0..008fcf4 100644
--- a/composer.json
+++ b/composer.json
@@ -1,23 +1,40 @@
{
- "name": "Kolab Free/Busy Service",
+ "name": "kolab/free-busy",
+ "description": "Kolab Free/Busy Service",
+ "license": "AGPL-3.0",
+ "version": "0.1-alpha",
"repositories": [
{
"type": "composer",
"url": "http://plugins.roundcube.net/"
},
{
"type": "pear",
"url": "http://pear.php.net/"
+ },
+ {
+ "type": "package",
+ "package": {
+ "name": "Net_LDAP3",
+ "version": "dev-master",
+ "source": {
+ "url": "git://git.kolab.org/git/pear/Net_LDAP3",
+ "type": "git",
+ "reference": "master"
+ },
+ "autoload": {
+ "classmap": ["lib/"]
+ }
+ }
}
],
"autoload": {
- "psr-0": { "": "lib/" },
- "classmap": ["vendor/kolab/lib"]
+ "psr-0": { "": "lib/" }
},
"require": {
"php": ">=5.3.3",
- "symfony/yaml": "2.2.*@dev",
- "monolog/monolog": "1.2.*"
+ "monolog/monolog": "1.2.*",
+ "Net_LDAP3": "dev-master"
},
"minimum-stability": "dev"
}
\ No newline at end of file
diff --git a/config/config.ini.sample b/config/config.ini.sample
index 9bc04c3..1c4cb5c 100644
--- a/config/config.ini.sample
+++ b/config/config.ini.sample
@@ -1,34 +1,34 @@
; Kolab Free/Busy Service configuration
; require HTTP authentication to access this service
[httpauth]
type = static
username = <user>
password = <pass>
; allow privileged access from these IPs
[trustednetworks]
allow[] = 127.0.0.1
allow[] = 192.168.0.
allow[] = 10.10.
allow[] = ::1
[log]
driver = file
path = ./log
level = 100 ; Warn
; address directories to resolve email addresses and their f/b source locations
[directory "ldap-1"]
type = ldap
host = ldaps://somehost.tld:389
bind_dn = "uid=anonymous,o=somehost,o=tld"
bind_pw = <password>
filter = "(&(objectClass=kolabInetOrgPerson)(|(uid=%u)(mail=%u)(alias=%u)))"
attributes[] = mail
fbsource = file:/www/kolab-freebusy/data/%mail.ifb
loglevel = 100 ; Debug
[directory "local"]
-type = default
+type = static
fbsource = file:/var/lib/kolab-freebusy/%u.ifb
diff --git a/lib/Kolab/FreeBusy/Config.php b/lib/Kolab/FreeBusy/Config.php
index c0101e9..d80ef7f 100644
--- a/lib/Kolab/FreeBusy/Config.php
+++ b/lib/Kolab/FreeBusy/Config.php
@@ -1,92 +1,122 @@
<?php
-namespace Kolab;
+namespace Kolab\FreeBusy;
/**
* Wrapper class for service configuration
*/
-class FBConfig
+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 FBConfig($dir);
+ self::$instance = new Config($dir);
}
- if ($dir && !self::$instance->valid)
- self::$instance->_load($dir);
+ if ($dir && !self::$instance->valid) {
+ self::$instance->load($configdir . '/config.ini');
+ }
return self::$instance;
}
/**
* Default constructor
- *
- * @param string Path to load config from
*/
function __construct($configdir = null)
{
if ($configdir) {
- $this->_load($configdir);
+ $this->basedir = $configdir;
+ $this->load($configdir . '/config.ini');
}
}
-
- private function _load($configdir)
+
+ /**
+ * Load config from the given .ini file
+ */
+ private function load($inifile)
{
- if ($raw = @parse_ini_file($configdir . '/config.ini', true)) {
+ if ($raw = parse_ini_file($inifile, true)) {
foreach ($raw as $section => $values) {
// check for known sections
if (in_array($section, array('httpauth','trustednetworks','log'))) {
- $this->data[$section] = $values;
+ $config[$section] = $values;
}
- else if (isset($values['fbsource'])){
- $this->data['directories'][] = $values;
+ else if (strpos($section, 'directory') === 0 || isset($values['fbsource'])){
+ $config['directories'][] = $values;
}
}
+ $this->register($config);
$this->valid = !empty($this->data['directories']);
}
else {
- trigger_error("Failed to parse configuration from $config_dir/config.ini", E_USER_ERROR);
+ 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 = '')
+ {
+ 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;
+ }
}
}
/**
* 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 25b9038..c16c666 100644
--- a/lib/Kolab/FreeBusy/Directory.php
+++ b/lib/Kolab/FreeBusy/Directory.php
@@ -1,63 +1,63 @@
<?php
-namespace Kolab;
+namespace Kolab\FreeBusy;
/**
* Abstract class representing an address directory for free/busy data lookups
*/
-abstract class FBDirectory
+abstract class Directory
{
protected $config;
/**
* Factory method creating an instace of FBDirectory according to config
*
* @param array Hash array with config
*/
public static function factory($config)
{
switch (strtolower($config['type'])) {
case 'ldap':
- return new FBDirectoryLDAP($config);
+ return new DirectoryLDAP($config);
- case 'default':
+ case 'static':
case 'external':
- return new FBDirectoryDefault($config);
+ return new DirectoryStatic($config);
default:
trigger_error("Invalid directory type '" . $config['type'] . "'!", E_USER_ERROR);
}
return null;
}
/**
* Resolve the given username to a FBEntity object
*
* @param string Username/Email to resolve
* @return object FBEntity if found, otherwise False
*/
abstract public function resolve($user);
/**
* Retrieve free/busy data for the given user.
*
* @param string Username or email to resolve
* @param boolean Get extemded free-busy if possible
* @return string VCalendar container if found, False otherwise
*/
public function getFreeBusyData($user, $extended = false)
{
// resolve user record first
if ($user = $this->resolve($user)) {
$fbsource = $this->config['fbsource'];
- if ($source = FBSource::Factory($fbsource)) {
+ if ($source = Source::Factory($fbsource)) {
// foward request to FBSource instance
return $source->getFreeBusyData($user, $extended);
}
}
return false;
}
}
\ No newline at end of file
diff --git a/lib/Kolab/FreeBusy/DirectoryLDAP.php b/lib/Kolab/FreeBusy/DirectoryLDAP.php
index f7dfb95..ae80bfc 100644
--- a/lib/Kolab/FreeBusy/DirectoryLDAP.php
+++ b/lib/Kolab/FreeBusy/DirectoryLDAP.php
@@ -1,40 +1,52 @@
<?php
-namespace Kolab;
+namespace Kolab\FreeBusy;
// PEAR modules operate in global namespace
use \Net_LDAP3;
/**
*
*/
-class FBDirectoryLDAP extends FBDirectory
+class DirectoryLDAP extends Directory
{
private $ldap;
+ private $logger;
/**
* Default constructor loading directory configuration
*/
public function __construct($config)
{
$ldap_config = array(
);
+ $this->logger = Logger::get('ldap');
$this->ldap = new Net_LDAP3($ldap_config);
- # $this->ldap->config_set_log_hook();
+ $this->ldap->config_set('log_hook', array($this, 'log'));
$this->ldap->connect();
}
/**
- * @see FBDirectory::resolve()
+ * Callback for Net_LDAP3 logging
+ */
+ public function log($log)
+ {
+ // TODO: map $log[0] levels
+ $msg = is_array($log[1]) ? join('; ', $log[1]) : strval($log[1]);
+ $this->logger->addRecord($log[0], $msg);
+ }
+
+ /**
+ * @see Directory::resolve()
*/
public function resolve($user)
{
$result = array('u' => $user);
return false;
}
}
diff --git a/lib/Kolab/FreeBusy/DirectoryStatic.php b/lib/Kolab/FreeBusy/DirectoryStatic.php
index cb89972..ff55e0e 100644
--- a/lib/Kolab/FreeBusy/DirectoryStatic.php
+++ b/lib/Kolab/FreeBusy/DirectoryStatic.php
@@ -1,35 +1,38 @@
<?php
-namespace Kolab;
+namespace Kolab\FreeBusy;
/**
+ * Implementation of a static address directory for Free/Busy user lookups.
*
+ * This directory basically pipes the username through with an optional check
+ * whether it matches the configured filter rule
*/
-class FBDirectoryDefault extends FBDirectory
+class DirectoryStatic extends Directory
{
/**
* Default constructor loading directory configuration
*/
public function __construct($config)
{
$this->config = $config;
}
/**
- * @see FBDirectory::resolve()
+ * @see Directory::resolve()
*/
public function resolve($user)
{
$result = array('u' => $user);
// check if user matches the filter property (if configured)
if (!empty($this->config['filter'])) {
if (!preg_match('!'.$this->config['filter'].'!i', $user))
$result = false;
}
return $result;
}
}
diff --git a/lib/Kolab/FreeBusy/Logger.php b/lib/Kolab/FreeBusy/Logger.php
index 97596c3..abb0060 100644
--- a/lib/Kolab/FreeBusy/Logger.php
+++ b/lib/Kolab/FreeBusy/Logger.php
@@ -1,55 +1,51 @@
<?php
-namespace Kolab;
-
-use Monolog\Logger;
-use Monolog\Handler\NullHandler;
-use Monolog\Handler\StreamHandler;
+namespace Kolab\FreeBusy;
/**
* Helper class for creating up Monolog instanced with local configration
*/
-class FBLogger
+class Logger
{
private static $instances = array();
/**
* Static getter for a Monolog\Logger instance
*/
public static function get($name, $level = 0)
{
if (!isset(self::$instances[$name])) {
- $logger = new Logger($name);
+ $logger = new \Monolog\Logger($name);
// TODO: support more log drivers
- $config = FBConfig::getInstance();
+ $config = Config::getInstance();
switch ($config->get('log.driver')) {
case 'file':
$logdir = self::realpath($config->get('log.path'));
- $loglevel = $level ?: $config->get("log.level", Logger::INFO);
- $logger->pushHandler(new StreamHandler($logdir . $name. '.log', $loglevel));
+ $loglevel = $level ?: $config->get("log.level", \Monolog\Logger::INFO);
+ $logger->pushHandler(new \Monolog\Handler\StreamHandler($logdir . $name. '.log', $loglevel));
break;
default:
// null handler if logging is disabled
- $logger->pushHandler(new NullHandler);
+ $logger->pushHandler(new \Monolog\Handler\NullHandler);
}
self::$instances[$name] = $logger;
}
return self::$instances[$name];
}
/**
* Resolve the given directory to a real path ending with a /
*/
private static function realpath($dirname)
{
if ($dirname[0] != '/')
$dirname = realpath(KOLAB_FREEBUSY_ROOT . '/' . $dirname);
return rtrim($dirname, '/') . '/';
}
}
diff --git a/lib/Kolab/FreeBusy/Source.php b/lib/Kolab/FreeBusy/Source.php
index 6d5d062..c078b48 100644
--- a/lib/Kolab/FreeBusy/Source.php
+++ b/lib/Kolab/FreeBusy/Source.php
@@ -1,65 +1,65 @@
<?php
-namespace Kolab;
+namespace Kolab\FreeBusy;
/**
* Abstract class to fetch free/busy data from a specific source
*/
-abstract class FBSource
+abstract class Source
{
protected $config = array();
/**
* Factory method creating an instace of FBSource according to config
*
* @param array Hash array with config
*/
public static function factory($url)
{
$config = parse_url($url);
switch ($config['scheme']) {
- case 'file': return new FBSourceFile($config);
- case 'imap': return new FBSourceIMAP($config);
+ case 'file': return new SourceFile($config);
+ case 'imap': return new SourceIMAP($config);
case 'http':
- case 'https': return new FBSourceURL($url);
+ case 'https': return new SourceURL($url);
}
trigger_error("Invalid source configuration: " . $url, E_USER_ERROR);
return null;
}
/**
* Default constructor
*/
public function __construct($config)
{
$this->config = $config;
}
/**
* Retrieve free/busy data for the given user
*
* @param array Hash array with user attributes
*/
abstract public function getFreeBusyData($user, $extended);
/**
* Replace all %varname strings in config with values from $user
*/
protected function getUserConfig($user)
{
$config = array();
foreach ($this->config as $k => $val) {
if (is_string($val) && strpos($val, '%') !== false) {
$val = preg_replace_callback(
'/%\{?([a-z0-9]+)\}?/',
function($m) use ($user) { return $user[$m[1]]; },
$val);
}
$config[$k] = $val;
}
return $config;
}
}
\ No newline at end of file
diff --git a/lib/Kolab/FreeBusy/SourceFile.php b/lib/Kolab/FreeBusy/SourceFile.php
index 0cdaf02..29840a6 100644
--- a/lib/Kolab/FreeBusy/SourceFile.php
+++ b/lib/Kolab/FreeBusy/SourceFile.php
@@ -1,19 +1,26 @@
<?php
-namespace Kolab;
+namespace Kolab\FreeBusy;
/**
- *
+ * Implementation of a Free/Busy data source reading from the local file system
*/
-class FBSourceFile extends FBSource
+class SourceFile extends Source
{
/**
- * @see FBSource::getFreeBusyData()
+ * @see Source::getFreeBusyData()
*/
public function getFreeBusyData($user, $extended)
{
+ // get source config with placeholders replaced
$config = $this->getUserConfig($user);
- // TODO: implement this
+ // deliver file contents if found
+ if (is_readable($config['path'])) {
+ return file_get_contents($config['path']);
+ }
+
+ // not found
+ return false;
}
}
diff --git a/lib/Kolab/FreeBusy/SourceIMAP.php b/lib/Kolab/FreeBusy/SourceIMAP.php
index 90602f8..23200c8 100644
--- a/lib/Kolab/FreeBusy/SourceIMAP.php
+++ b/lib/Kolab/FreeBusy/SourceIMAP.php
@@ -1,19 +1,19 @@
<?php
-namespace Kolab;
+namespace Kolab\FreeBusy;
/**
*
*/
-class FBSourceIMAP extends FBSource
+class SourceIMAP extends Source
{
/**
- * @see FBSource::getFreeBusyData()
+ * @see Source::getFreeBusyData()
*/
public function getFreeBusyData($user, $extended)
{
$config = $this->getUserConfig($user);
// TODO: implement this
}
}
diff --git a/lib/Kolab/FreeBusy/SourceURL.php b/lib/Kolab/FreeBusy/SourceURL.php
index ecd0df5..1051dbf 100644
--- a/lib/Kolab/FreeBusy/SourceURL.php
+++ b/lib/Kolab/FreeBusy/SourceURL.php
@@ -1,19 +1,19 @@
<?php
-namespace Kolab;
+namespace Kolab\FreeBusy;
/**
*
*/
-class FBSourceURL extends FBSource
+class SourceURL extends Source
{
/**
- * @see FBSource::getFreeBusyData()
+ * @see Source::getFreeBusyData()
*/
public function getFreeBusyData($user, $extended)
{
$config = $this->getUserConfig($user);
// TODO: implement this
}
}
diff --git a/web/index.php b/web/index.php
index 03530cd..6f82d91 100644
--- a/web/index.php
+++ b/web/index.php
@@ -1,82 +1,126 @@
<?php
/**
* Kolab Server Free/Busy Service Endpoint
*
* This is the public API to provide Free/Busy information for Kolab users.
*
* @version 0.1.0
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2013, Kolab Systems AG <contact@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/>.
*/
define('KOLAB_FREEBUSY_ROOT', realpath('../'));
+// suppress error notices
+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\FBConfig;
-use Kolab\FBDirectory;
+use Kolab\FreeBusy\Config;
+use Kolab\FreeBusy\Logger;
+use Kolab\FreeBusy\Directory;
#header('Content-type: text/calendar; charset=utf-8', true);
header('Content-type: text/plain', true);
// load config
-$config = FBConfig::getInstance(KOLAB_FREEBUSY_ROOT . '/config');
+$config = Config::getInstance(KOLAB_FREEBUSY_ROOT . '/config');
if ($config->isValid()) {
- print_r($config);
+# print_r($config);
+ $log = Logger::get('service');
+ $log->addInfo('Request: ' . $_SERVER['REDIRECT_URL'], array('ip' => $_SERVER['REMOTE_ADDR']));
// check HTTP auth first
if ($config->httpauth) {
// TODO: implement this
}
// analyse request
$url = array_filter(explode('/', $_SERVER['REDIRECT_URL']));
$user = strtolower(array_pop($url));
$action = strtolower(array_pop($url));
$extended = false;
// remove file extension
if (preg_match('/^(.+)\.([ipx]fb)$/i', $user, $m)) {
$user = $m[1];
$extended = $m[2] == 'xfb';
}
// iterate over directories
- foreach ($config->directories as $dirconfig) {
- $directory = FBDirectory::factory($dirconfig);
+ foreach ($config->directories as $key => $dirconfig) {
+ $log->addDebug("Trying directory $key", $dirconfig);
+
+ $directory = Directory::factory($dirconfig);
if ($fbdata = $directory->getFreeBusyData($user, $extended)) {
+ $log->addInfo("Found valid data for user $user");
echo $fbdata;
exit;
}
}
/*
if ($_SERVER['REMOTE_ADDR'] is in $config->trustednetworks['allow]) {
+ $log->addDebug("Returning '404 Not Found' for user $user");
header($_SERVER['SERVER_PROTOCOL'] . " 404 Not found", true);
}
else {
- // TODO: print empty vfreebusy block
- }
*/
+ $log->addInfo("Returning empty Free/Busy list for user $user");
+
+ $now = time();
+ $dtformat = 'Ymd\THis\Z';
+
+ // NOTE: The following settings should probably correspond with
+ // whatever period of time kolab-freebusyd thinks it should use.
+
+ // Should probably be a setting. For now, do 8 weeks in the past
+ $start = $now - (60 * 60 * 24 * 7 * 8);
+ // Should probably be a setting. For now, do 16 weeks into the future
+ $end = $now + (60 * 60 * 24 * 7 * 16);
+
+ $urlschema = 'http';
+ if ((!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') ||
+ (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') ||
+ $_SERVER['SERVER_PORT'] == '443') {
+ $urlschema = 'https';
+ }
+
+ // Return an apparent empty Free/Busy list.
+ print "BEGIN:VCALENDAR\n";
+ print "VERSION:2.0\n";
+ print "PRODID:-//kolab.org//NONSGML Kolab Server 3//EN\n";
+ print "METHOD:PUBLISH\n";
+ print "BEGIN:VFREEBUSY\n";
+ print "ORGANIZER:MAILTO:" . $user . ".ifb\n";
+ print "DTSTAMP:" . gmdate($dtformat) . "\n";
+ print "URL:$urlschema://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . "\n";
+ print "DTSTART:" . gmdate($dtformat, $start) . "\n";
+ print "DTEND:" . gmdate($dtformat, $end) . "\n";
+ print "COMMENT:This is a dummy vfreebusy that indicates an empty calendar\n";
+ print "FREEBUSY:19700101T000000Z/19700101T000000Z\n";
+ print "END:VFREEBUSY\n";
+ print "END:VCALENDAR\n";
+// }
}
// exit with error
# header($_SERVER['SERVER_PROTOCOL'] . " 500 Internal Server Error", true);
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Feb 2, 3:00 PM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426944
Default Alt Text
(19 KB)
Attached To
Mode
R28 freebusy
Attached
Detach File
Event Timeline
Log In to Comment