Page MenuHomePhorge

No OneTemporary

Size
15 KB
Referenced Files
None
Subscribers
None
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

Mime Type
text/x-diff
Expires
Mon, Feb 2, 7:45 PM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426965
Default Alt Text
(15 KB)

Event Timeline