Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2529741
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e395922
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+INTALLATION PROCEDURE
+=====================
+
+This package uses [Composer](http://getcomposer.org) to install and maintain required PHP libraries.
+
+1. Install Composer
+
+Execute this in the project root directory:
+
+$ curl -s http://getcomposer.org/installer | php
+
+This will create a file named composer.phar in the project directory.
+
+2. Install Dependencies
+
+$ php composer.phar install
+
+3. Create local config
+
+Copy the config template file to config/config.ini:
+
+$ cp config/config.ini.sample config/config.ini
+
+Edit the local config/config.ini file according to your setup and taste.
+
+4. Give write access for the webserver user to the 'log' folder:
+
+$ chown <www-user> log
+
+6. Configure your webserver to point to the 'web' directory of this package as document root.
+
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..94eb2f0
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,23 @@
+{
+ "name": "Kolab Free/Busy Service",
+ "repositories": [
+ {
+ "type": "composer",
+ "url": "http://plugins.roundcube.net/"
+ },
+ {
+ "type": "pear",
+ "url": "http://pear.php.net/"
+ }
+ ],
+ "autoload": {
+ "psr-0": { "": "lib/" },
+ "classmap": ["vendor/kolab/lib"]
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "symfony/yaml": "2.2.*@dev",
+ "monolog/monolog": "1.2.*"
+ },
+ "minimum-stability": "dev"
+}
\ No newline at end of file
diff --git a/config/.gitignore b/config/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/config/config.ini.sample b/config/config.ini.sample
new file mode 100644
index 0000000..9bc04c3
--- /dev/null
+++ b/config/config.ini.sample
@@ -0,0 +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
+fbsource = file:/var/lib/kolab-freebusy/%u.ifb
diff --git a/lib/Kolab/FreeBusy/Config.php b/lib/Kolab/FreeBusy/Config.php
new file mode 100644
index 0000000..c0101e9
--- /dev/null
+++ b/lib/Kolab/FreeBusy/Config.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Kolab;
+
+/**
+ * Wrapper class for service configuration
+ */
+class FBConfig
+{
+ private static $instance;
+
+ private $data = array();
+ private $valid = false;
+
+ /**
+ * Singelton getter
+ */
+ public static function getInstance($dir = null)
+ {
+ if (!isset(self::$instance)) {
+ self::$instance = new FBConfig($dir);
+ }
+
+ if ($dir && !self::$instance->valid)
+ self::$instance->_load($dir);
+
+ return self::$instance;
+ }
+
+ /**
+ * Default constructor
+ *
+ * @param string Path to load config from
+ */
+ function __construct($configdir = null)
+ {
+ if ($configdir) {
+ $this->_load($configdir);
+ }
+ }
+
+ private function _load($configdir)
+ {
+ if ($raw = @parse_ini_file($configdir . '/config.ini', true)) {
+ foreach ($raw as $section => $values) {
+ // check for known sections
+ if (in_array($section, array('httpauth','trustednetworks','log'))) {
+ $this->data[$section] = $values;
+ }
+ else if (isset($values['fbsource'])){
+ $this->data['directories'][] = $values;
+ }
+ }
+
+ $this->valid = !empty($this->data['directories']);
+ }
+ else {
+ trigger_error("Failed to parse configuration from $config_dir/config.ini", E_USER_ERROR);
+ }
+ }
+
+ /**
+ * 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
new file mode 100644
index 0000000..25b9038
--- /dev/null
+++ b/lib/Kolab/FreeBusy/Directory.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Kolab;
+
+/**
+ * Abstract class representing an address directory for free/busy data lookups
+ */
+abstract class FBDirectory
+{
+ 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);
+
+ case 'default':
+ case 'external':
+ return new FBDirectoryDefault($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)) {
+ // 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
new file mode 100644
index 0000000..f7dfb95
--- /dev/null
+++ b/lib/Kolab/FreeBusy/DirectoryLDAP.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Kolab;
+
+// PEAR modules operate in global namespace
+use \Net_LDAP3;
+
+/**
+ *
+ */
+class FBDirectoryLDAP extends FBDirectory
+{
+ private $ldap;
+
+ /**
+ * Default constructor loading directory configuration
+ */
+ public function __construct($config)
+ {
+ $ldap_config = array(
+
+ );
+
+ $this->ldap = new Net_LDAP3($ldap_config);
+ # $this->ldap->config_set_log_hook();
+ $this->ldap->connect();
+ }
+
+ /**
+ * @see FBDirectory::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
new file mode 100644
index 0000000..cb89972
--- /dev/null
+++ b/lib/Kolab/FreeBusy/DirectoryStatic.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Kolab;
+
+/**
+ *
+ */
+class FBDirectoryDefault extends FBDirectory
+{
+ /**
+ * Default constructor loading directory configuration
+ */
+ public function __construct($config)
+ {
+ $this->config = $config;
+ }
+
+ /**
+ * @see FBDirectory::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
new file mode 100644
index 0000000..97596c3
--- /dev/null
+++ b/lib/Kolab/FreeBusy/Logger.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Kolab;
+
+use Monolog\Logger;
+use Monolog\Handler\NullHandler;
+use Monolog\Handler\StreamHandler;
+
+/**
+ * Helper class for creating up Monolog instanced with local configration
+ */
+class FBLogger
+{
+ 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);
+
+ // TODO: support more log drivers
+ $config = FBConfig::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));
+ break;
+
+ default:
+ // null handler if logging is disabled
+ $logger->pushHandler(new 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
new file mode 100644
index 0000000..6d5d062
--- /dev/null
+++ b/lib/Kolab/FreeBusy/Source.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Kolab;
+
+/**
+ * Abstract class to fetch free/busy data from a specific source
+ */
+abstract class FBSource
+{
+ 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 'http':
+ case 'https': return new FBSourceURL($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
new file mode 100644
index 0000000..0cdaf02
--- /dev/null
+++ b/lib/Kolab/FreeBusy/SourceFile.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kolab;
+
+/**
+ *
+ */
+class FBSourceFile extends FBSource
+{
+ /**
+ * @see FBSource::getFreeBusyData()
+ */
+ public function getFreeBusyData($user, $extended)
+ {
+ $config = $this->getUserConfig($user);
+
+ // TODO: implement this
+ }
+}
diff --git a/lib/Kolab/FreeBusy/SourceIMAP.php b/lib/Kolab/FreeBusy/SourceIMAP.php
new file mode 100644
index 0000000..90602f8
--- /dev/null
+++ b/lib/Kolab/FreeBusy/SourceIMAP.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kolab;
+
+/**
+ *
+ */
+class FBSourceIMAP extends FBSource
+{
+ /**
+ * @see FBSource::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
new file mode 100644
index 0000000..ecd0df5
--- /dev/null
+++ b/lib/Kolab/FreeBusy/SourceURL.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kolab;
+
+/**
+ *
+ */
+class FBSourceURL extends FBSource
+{
+ /**
+ * @see FBSource::getFreeBusyData()
+ */
+ public function getFreeBusyData($user, $extended)
+ {
+ $config = $this->getUserConfig($user);
+
+ // TODO: implement this
+ }
+}
diff --git a/web/.htaccess b/web/.htaccess
new file mode 100644
index 0000000..2b669c8
--- /dev/null
+++ b/web/.htaccess
@@ -0,0 +1,7 @@
+RewriteEngine On
+
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule (.*) index.php [qsappend,last]
+
+
diff --git a/web/index.php b/web/index.php
new file mode 100644
index 0000000..03530cd
--- /dev/null
+++ b/web/index.php
@@ -0,0 +1,82 @@
+<?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('../'));
+
+// use composer's autoloader for both dependencies and local lib
+require_once KOLAB_FREEBUSY_ROOT . '/vendor/autoload.php';
+
+use Kolab\FBConfig;
+use Kolab\FBDirectory;
+
+#header('Content-type: text/calendar; charset=utf-8', true);
+header('Content-type: text/plain', true);
+
+// load config
+$config = FBConfig::getInstance(KOLAB_FREEBUSY_ROOT . '/config');
+if ($config->isValid()) {
+ print_r($config);
+
+ // 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);
+ if ($fbdata = $directory->getFreeBusyData($user, $extended)) {
+ echo $fbdata;
+ exit;
+ }
+ }
+
+/*
+ if ($_SERVER['REMOTE_ADDR'] is in $config->trustednetworks['allow]) {
+ header($_SERVER['SERVER_PROTOCOL'] . " 404 Not found", true);
+ }
+ else {
+ // TODO: print empty vfreebusy block
+ }
+*/
+}
+
+// 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, 9:07 PM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426965
Default Alt Text
(15 KB)
Attached To
Mode
R28 freebusy
Attached
Detach File
Event Timeline
Log In to Comment