Page MenuHomePhorge

No OneTemporary

Size
39 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/Kolab/FreeBusy/Config.php b/lib/Kolab/FreeBusy/Config.php
index 7b2ff5f..ab82a1a 100644
--- a/lib/Kolab/FreeBusy/Config.php
+++ b/lib/Kolab/FreeBusy/Config.php
@@ -1,152 +1,173 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
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 dba6014..1c70f75 100644
--- a/lib/Kolab/FreeBusy/Directory.php
+++ b/lib/Kolab/FreeBusy/Directory.php
@@ -1,84 +1,105 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
/**
* Abstract class representing an address directory for free/busy data lookups
*/
abstract class Directory
{
protected $config;
/**
* Factory method creating an instace of Directory according to config
*
* @param array Hash array with config
*/
public static function factory($config)
{
switch (strtolower($config['type'])) {
case 'ldap':
return new DirectoryLDAP($config);
case 'static':
case 'external':
return new DirectoryStatic($config);
default:
Logger::get('directory')->addError("Invalid directory type '" . $config['type'] . "'!");
}
return null;
}
/**
* Resolve the given username to a Entity object
*
* @param string Username/Email to resolve
* @return object Entity 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 = Source::Factory($fbsource)) {
// forward request to Source instance
if ($data = $source->getFreeBusyData($this->postprocessAttrib($user), $extended)) {
// send data through the according format converter
$converter = Format::factory($this->config['format']);
$data = $converter->toVCalendar($data);
}
return $data;
}
}
return false;
}
/**
* Modify attribute values according to config
*/
protected function postprocessAttrib($attrib)
{
if (!empty($this->config['lc_attributes'])) {
foreach ((array)$this->config['lc_attributes'] as $key) {
if (!empty($attrib[$key]))
$attrib[$key] = strtolower($attrib[$key]);
}
}
return $attrib;
}
}
\ No newline at end of file
diff --git a/lib/Kolab/FreeBusy/DirectoryLDAP.php b/lib/Kolab/FreeBusy/DirectoryLDAP.php
index 00bba1a..5e31e24 100644
--- a/lib/Kolab/FreeBusy/DirectoryLDAP.php
+++ b/lib/Kolab/FreeBusy/DirectoryLDAP.php
@@ -1,108 +1,129 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
// PEAR modules operate in global namespace
use \Net_LDAP3;
use \Monolog\Logger as Monolog;
/**
- *
+ * Implementation of an address lookup using an LDAP directory
*/
class DirectoryLDAP extends Directory
{
private $ldap;
private $logger;
private $ready = false;
/**
* Default constructor loading directory configuration
*/
public function __construct($config)
{
$this->config = $config;
$host = parse_url($config['host']);
$ldap_config = array(
'hosts' => array($host['host']),
'port' => $host['port'] ?: 389,
'use_tls' => $host['scheme'] == 'tls' || $host['scheme'] == 'ldaps',
'root_dn' => $config['base_dn'],
'return_attributes' => (array)$config['attributes'],
'log_hook' => array($this, 'log'),
) + $config;
// instantiate Net_LDAP3 and connect with logger
$this->logger = Logger::get('ldap', intval($config['loglevel']));
$this->ldap = new Net_LDAP3($ldap_config);
// connect + bind to LDAP server
if ($this->ldap->connect()) {
$this->ready = $this->ldap->bind($config['bind_dn'], $config['bind_pw']);
}
if ($this->ready) {
$this->logger->addInfo("Connected to $config[host] with '$config[bind_dn]'");
}
else {
$this->logger->addWarning("Connectiion to $config[host] with '$config[bind_dn]' failed!");
}
}
/**
* Callback for Net_LDAP3 logging
*/
public function log($level, $msg)
{
// map PHP log levels to Monolog levels
static $loglevels = array(
LOG_DEBUG => Monolog::DEBUG,
LOG_NOTICE => Monolog::NOTICE,
LOG_INFO => Monolog::INFO,
LOG_WARNING => Monolog::WARNING,
LOG_ERR => Monolog::ERROR,
LOG_CRIT => Monolog::CRITICAL,
LOG_ALERT => Monolog::ALERT,
LOG_EMERG => Monolog::EMERGENCY,
);
$msg = is_array($msg) ? join('; ', $msg) : strval($msg);
$this->logger->addRecord($loglevels[$level], $msg);
}
/**
* @see Directory::resolve()
*/
public function resolve($user)
{
$result = array('s' => $user);
if ($this->ready) {
// search with configured filter
$filter = preg_replace('/%s/i', $user, $this->config['filter']);
$ldapresult = $this->ldap->search($this->config['base_dn'], $filter, 'sub');
// got a valid result
if ($ldapresult && $ldapresult->count()) {
$ldapresult->rewind();
$entry = Net_LDAP3::normalize_entry($ldapresult->current()); // get the first entry
$this->logger->addInfo("Found " . $ldapresult->count() . " entries for $filter", $entry);
// convert entry attributes to strings and add them to the final result hash array
foreach ($entry as $k => $v) {
if (!empty($v)) {
$result[$k] = strval(is_array($v) ? $v[0] : $v);
}
}
return $result;
}
$this->logger->addInfo("No entry found for $filter");
}
return false;
}
}
diff --git a/lib/Kolab/FreeBusy/DirectoryStatic.php b/lib/Kolab/FreeBusy/DirectoryStatic.php
index 8424332..663f206 100644
--- a/lib/Kolab/FreeBusy/DirectoryStatic.php
+++ b/lib/Kolab/FreeBusy/DirectoryStatic.php
@@ -1,38 +1,59 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
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 DirectoryStatic extends Directory
{
/**
* Default constructor loading directory configuration
*/
public function __construct($config)
{
$this->config = $config;
}
/**
* @see Directory::resolve()
*/
public function resolve($user)
{
$result = array('s' => $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/Format.php b/lib/Kolab/FreeBusy/Format.php
index b5ebfda..58a95a4 100644
--- a/lib/Kolab/FreeBusy/Format.php
+++ b/lib/Kolab/FreeBusy/Format.php
@@ -1,46 +1,67 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
/**
* Base class to handle free/busy data format conversion
*/
class Format
{
protected $config;
/**
* Factory method creating an instace of Format according to the given type
*
* @param string Format identifier
*/
public static function factory($type)
{
switch (strtolower($type)) {
case 'exchange2010':
return new FormatExchange2010;
default:
if (!empty($type)) {
Logger::get('format')->addError("Unknown format type '$type'!");
}
return new Format;
}
return null;
}
/**
* Convert the given free/busy data stream to iCal format
*
* @param string Input data stream
* @return string iCal formatted free/busy list
*/
public function toVCalendar($input)
{
// default: no format changes
return $input;
}
}
\ No newline at end of file
diff --git a/lib/Kolab/FreeBusy/FormatExchange2010.php b/lib/Kolab/FreeBusy/FormatExchange2010.php
index 8534df2..55a04dd 100644
--- a/lib/Kolab/FreeBusy/FormatExchange2010.php
+++ b/lib/Kolab/FreeBusy/FormatExchange2010.php
@@ -1,137 +1,158 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
use Sabre\VObject\Reader as VCalReader;
use Sabre\VObject\FreeBusyGenerator;
use Sabre\VObject\ParseException;
use Desarrolla2\Cache\Cache;
use Desarrolla2\Cache\Adapter\File as FileCache;
use \SimpleXMLElement;
/**
* Implementation of a data converter reading Exchange 2010 Internet Calendar Publishing files
*/
class FormatExchange2010 extends Format
{
private $tzmap;
/**
* @see Format::toVCalendar()
*/
public function toVCalendar($input)
{
// convert Microsoft timezone identifiers to Olson standard
// do this before parsing to create correct DateTime values
$input = preg_replace_callback('/(TZID[=:])([-\w ]+)\b/i', array($this, 'convertTZID'), $input);
try {
// parse vcalendar data
$calendar = VCalReader::read($input);
// map X-MICROSOFT-CDO-* attributes into iCal equivalents
foreach ($calendar->VEVENT as $vevent) {
if ($busystatus = reset($vevent->select('X-MICROSOFT-CDO-BUSYSTATUS'))) {
$vevent->STATUS->value = $busystatus->value;
}
}
// feed the calendar object into the free/busy generator
// we must specify a start and end date, because recurring events are expanded. nice!
$utc = new \DateTimezone('UTC');
$fbgen = new FreeBusyGenerator(
new \DateTime('now - 8 weeks 00:00:00', $utc),
new \DateTime('now + 16 weeks 00:00:00', $utc),
$calendar
);
// get the freebusy report
$freebusy = $fbgen->getResult();
$freebusy->PRODID = '-//kolab.org//NONSGML Kolab Server 3//EN';
$freebusy->METHOD = 'PUBLISH';
// serialize to VCALENDAR format
return $freebusy->serialize();
}
catch (ParseException $e) {
Logger::get('format.Exchange2010')->addError("iCal parse error: " . $e->getMessage());
}
return false;
}
/**
* preg_replace callback function to map Timezone identifiers
*/
private function convertTZID($m)
{
if (!isset($this->tzmap)) {
$this->getTZMAP();
}
$key = strtolower($m[2]);
if ($this->tzmap[$key]) {
$m[2] = $this->tzmap[$key];
}
return $m[1] . $m[2] . $m[3];
}
/**
* Generate a Microsoft => Olson Timezone mapping table from an official source
*/
private function getTZMAP()
{
if (!isset($this->tzmap)) {
$log = Logger::get('format.Exchange2010');
$cache = new Cache(new FileCache(sys_get_temp_dir()));
// read from cache
$this->tzmap = $cache->get('windows-timezones');
// fetch timezones map from source
if (empty($this->tzmap)) {
$this->tzmap = array();
$zones_url = 'http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml';
if ($xml = @file_get_contents($zones_url)) {
try {
$zonedata = new SimpleXMLElement($xml, LIBXML_NOWARNING | LIBXML_NOERROR);
foreach ($zonedata->windowsZones[0]->mapTimezones[0]->mapZone as $map) {
$other = strtolower(strval($map['other']));
$region = strval($map['territory']);
$words = explode(' ', $other);
$olson = explode(' ', strval($map['type']));
// skip invalid entries
if (empty($other) || empty($olson))
continue;
// create an entry for all substrings
for ($i = 1; $i <= count($words); $i++) {
$last = $i == count($words);
$key = join(' ', array_slice($words, 0, $i));
if ($region == '001' || ($last && empty($this->tzmap[$key]))) {
$this->tzmap[$key] = $olson[0];
}
}
}
// cache the mapping for one week
$cache->set('windows-timezones', $this->tzmap, 7 * 86400);
$log->addInfo("Updated Windows Timezones Map from source", array($zones_url));
}
catch (\Exception $e) {
$log->addError("Failed parse Windows Timezones Map: " . $e->getMessage());
}
}
else {
$log->addError("Failed to load Windows Timezones Map from source", array($zones_url));
}
}
}
return $this->tzmap;
}
}
diff --git a/lib/Kolab/FreeBusy/HTTPAuth.php b/lib/Kolab/FreeBusy/HTTPAuth.php
index b9dce22..c4bd288 100644
--- a/lib/Kolab/FreeBusy/HTTPAuth.php
+++ b/lib/Kolab/FreeBusy/HTTPAuth.php
@@ -1,115 +1,136 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
use \Net_LDAP3;
use \Monolog\Logger as Monolog;
/**
* Static class to process HTTP authentication to this service
*/
class HTTPAuth
{
private static $logger;
/**
* Validate HTTP basic auth against the configured backend
*/
public static function check($config)
{
$logger = Logger::get('httpauth');
// no http auth submitted, abort!
if (empty($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
$logger->addDebug('No HTTP auth submitted');
return false;
}
switch ($config['type']) {
case 'static':
return self::checkStatic($config, $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
case 'ldap':
return self::checkLDAP($config, $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
default:
$logger->addWarning('Unsupported auth type ' . $config['type']);
break;
}
return false;
}
/**
* Validate static user credentials from config
*/
private static function checkStatic($config, $user, $pass)
{
$valid = $user == $config['username'] && $pass == $config['password'];
Logger::get('httpauth')->addInfo("Static: authenticating user '$user': " . ($valid ? 'SUCCESS' : 'FAILURE'));
return $valid;
}
/**
* Validate user credentials against the configured LDAP backend
*/
private static function checkLDAP($config, $user, $pass)
{
self::$logger = Logger::get('httpauth', intval($config['loglevel']));
$host = parse_url($config['host']);
$ldap_config = array(
'hosts' => array($host['host']),
'port' => $host['port'] ?: 389,
'use_tls' => $host['scheme'] == 'tls' || $host['scheme'] == 'ldaps',
'root_dn' => $config['base_dn'],
'filter' => $config['filter'],
'service_bind_dn' => $config['bind_dn'],
'service_bind_pw' => $config['bind_pw'],
'log_hook' => 'Kolab\FreeBusy\HTTPAuth::ldapLog',
);
// instantiate Net_LDAP3 and connect with logger
$ldap = new Net_LDAP3($ldap_config);
// connect + bind to LDAP server
if ($ldap->connect()) {
self::$logger->addDebug("LDAP: connected to $config[host] with '$config[bind_dn]'");
// extract domain part from base_dn
$dn_domain = ldap_explode_dn($config['base_dn'], 1);
unset($dn_domain['count']);
$domain = join('.', $dn_domain);
$valid = (bool)$ldap->login($user, $pass, $domain);
}
else {
self::$logger->addWarning("LDAP: connectiion to $config[host] with '$config[bind_dn]' failed!");
}
self::$logger->addInfo("LDAP: authenticating user '$user': " . ($valid ? 'SUCCESS' : 'FAILURE'));
return $valid;
}
/**
* Callback for Net_LDAP3 logging
*/
public static function ldapLog($level, $msg)
{
// map PHP log levels to Monolog levels
static $loglevels = array(
LOG_DEBUG => Monolog::DEBUG,
LOG_NOTICE => Monolog::NOTICE,
LOG_INFO => Monolog::INFO,
LOG_WARNING => Monolog::WARNING,
LOG_ERR => Monolog::ERROR,
LOG_CRIT => Monolog::CRITICAL,
LOG_ALERT => Monolog::ALERT,
LOG_EMERG => Monolog::EMERGENCY,
);
$msg = is_array($msg) ? join('; ', $msg) : strval($msg);
self::$logger->addRecord($loglevels[$level], $msg);
}
}
\ No newline at end of file
diff --git a/lib/Kolab/FreeBusy/Logger.php b/lib/Kolab/FreeBusy/Logger.php
index 8af607c..618003b 100644
--- a/lib/Kolab/FreeBusy/Logger.php
+++ b/lib/Kolab/FreeBusy/Logger.php
@@ -1,52 +1,73 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
use Monolog\Logger as Monologger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogHandler;
use Monolog\Handler\NullHandler;
/**
* Helper class for creating up Monolog instanced with local configration
*/
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]) || ($level && !self::$instances[$name]->isHandling($level))) {
$logger = new Monologger($name);
// read log config
$config = Config::getInstance();
$identity = $config->get('log.name', 'freebusy');
$loglevel = $level ?: $config->get('log.level', Monologger::INFO);
switch ($config->get('log.driver')) {
case 'file':
$logdir = Utils::abspath($config->get('log.path'), '/');
$logger->pushHandler(new StreamHandler($logdir . $identity . '.log', $loglevel));
break;
case 'syslog':
$logger->pushHahdler(new SyslogHandler($identity, $config->get('log.facility', 'user'), $loglevel));
break;
default:
// null handler if logging is disabled
$logger->pushHandler(new NullHandler);
}
self::$instances[$name] = $logger;
}
return self::$instances[$name];
}
}
diff --git a/lib/Kolab/FreeBusy/Source.php b/lib/Kolab/FreeBusy/Source.php
index 96bb547..3a256a3 100644
--- a/lib/Kolab/FreeBusy/Source.php
+++ b/lib/Kolab/FreeBusy/Source.php
@@ -1,67 +1,88 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
/**
* Abstract class to fetch free/busy data from a specific source
*/
abstract class Source
{
protected $config = array();
/**
* Factory method creating an instace of Source according to config
*
* @param array Hash array with config
*/
public static function factory($url)
{
$config = parse_url($url);
$config['url'] = $url;
switch ($config['scheme']) {
case 'file': return new SourceFile($config);
case 'imap':
case 'imaps': return new SourceIMAP($config);
case 'http':
case 'https': return new SourceURL($config);
}
Logger::get('source')->addError("Invalid source configuration: " . $url);
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 29840a6..af25b6e 100644
--- a/lib/Kolab/FreeBusy/SourceFile.php
+++ b/lib/Kolab/FreeBusy/SourceFile.php
@@ -1,26 +1,47 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
/**
* Implementation of a Free/Busy data source reading from the local file system
*/
class SourceFile extends Source
{
/**
* @see Source::getFreeBusyData()
*/
public function getFreeBusyData($user, $extended)
{
// get source config with placeholders replaced
$config = $this->getUserConfig($user);
// 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 23200c8..c545423 100644
--- a/lib/Kolab/FreeBusy/SourceIMAP.php
+++ b/lib/Kolab/FreeBusy/SourceIMAP.php
@@ -1,19 +1,41 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
/**
- *
+ * Implementation of a Free/Busy data source reading from IMAP
+ * (not yet implemented!)
*/
class SourceIMAP extends Source
{
/**
* @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 d62a520..02f73d7 100644
--- a/lib/Kolab/FreeBusy/SourceURL.php
+++ b/lib/Kolab/FreeBusy/SourceURL.php
@@ -1,55 +1,76 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
/**
* Implementation of a Free/Busy data source reading from remote URLs through HTTP
*/
class SourceURL extends Source
{
/**
* @see Source::getFreeBusyData()
*/
public function getFreeBusyData($user, $extended)
{
$config = $this->getUserConfig($user);
// prepare HTTP stream context
$context = stream_context_create(array(
'http' => array(
'user_agent' => "Kolab Free-Busy Service/0.1.0",
'timeout' => 10,
),
));
// set HTTP auth credentials
if (!empty($config['user'])) {
stream_context_set_option($context, array(
'http' => array(
'header' => "Authorization: Basic " . base64_encode($config['user'] . ':' . $config['pass']) . "\r\n",
),
));
$config['url'] = self::composeUrl($config); // re-compose url without user:pass
}
$data = @file_get_contents($config['url'], false, $context);
// log this...
Logger::get('url')->addInfo("Fetching data from " . $config['url'] . ": " . ($data ? 'OK' : 'FAILED'));
return $data;
}
/**
* Compose a full url from the given config (previously extracted with parse_url())
*/
private static function composeUrl($config)
{
$scheme = isset($config['scheme']) ? $config['scheme'] . '://' : '';
$host = isset($config['host']) ? $config['host'] : '';
$port = isset($config['port']) ? ':' . $config['port'] : '';
$path = isset($config['path']) ? $config['path'] : '';
$query = isset($config['query']) ? '?' . $config['query'] : '';
return $scheme . $host . $port . $path . $query;
}
}
diff --git a/lib/Kolab/FreeBusy/Utils.php b/lib/Kolab/FreeBusy/Utils.php
index ba1e736..3fea324 100644
--- a/lib/Kolab/FreeBusy/Utils.php
+++ b/lib/Kolab/FreeBusy/Utils.php
@@ -1,118 +1,139 @@
<?php
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @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/>.
+ */
+
namespace Kolab\FreeBusy;
/**
* Static calss providing utility functions for the Free/Busy service
*/
class Utils
{
/**
* Resolve the given directory to a real path ending with $append
*
* @param string Arbitrary directory directory path
* @param string Make path end with this string/character
* @return string Absolute file system path
*/
public static function abspath($dirname, $append = '')
{
if ($dirname[0] != '/')
$dirname = realpath(KOLAB_FREEBUSY_ROOT . '/' . $dirname);
return rtrim($dirname, '/') . $append;
}
/**
* Returns remote IP address and forwarded addresses if found
*
* @return string Remote IP address(es)
*/
public static function remoteIP()
{
$address = $_SERVER['REMOTE_ADDR'];
// use the NGINX X-Real-IP header, if set
if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$address = $_SERVER['HTTP_X_REAL_IP'];
}
// use the X-Forwarded-For header, if set
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$address = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
return $address;
}
/**
* Checks if the given IP address is in one of the provided ranges
*
* @param string IP address
* @param array List of IP ranges/subnets to check against
* @return boolean True if in range, False if not
*/
public static function checkIPRange($ip, $ranges)
{
$ipv6 = strpos($ip, ':') !== false;
$ipbin = $ipv6 ? self::ip6net2bits($ip) : ip2long($ip);
foreach ((array)$ranges as $range) {
// don't compare IPv4 and IPv6 addresses/ranges
$rangev6 = strpos($range, ':') !== false;
if ($ipv6 != $rangev6) {
continue;
}
// quick substring check (e.g. 192.168.0.)
if (( $ipv6 && strpos($ipbin, self::ip6net2bits($range)) === 0) ||
(!$ipv6 && strpos($ip, rtrim($range, '*')) === 0)) {
return true;
}
// range from-to specified (IPv4 only)
list($lower, $upper) = explode('-', $range);
if (strlen($upper) && !$ipv6) {
if ($ipbin >= ip2long(trim($lower)) && $ipbin <= ip2long(trim($upper))) {
return true;
}
}
// subnet/length is given
list($subnet, $bits) = explode('/', $range);
// IPv6 subnet
if (strlen($bits) && $ipv6) {
$subnetbin = self::ip6net2bits($subnet);
if (substr($ipbin, 0, $bits) === substr($subnetbin, 0, $bits)) {
return true;
}
}
// IPv4 subnet
else if (strlen($bits)) {
$subnet = ip2long($subnet);
$mask = -1 << $bits;
$subnet &= $mask; // just in case the supplied subnet wasn't correctly aligned
if (($ipbin & $mask) == $subnet) {
return true;
}
}
}
return false;
}
/**
* Convert the given IPv6 address to a binary string representation.
* (from http://stackoverflow.com/questions/7951061/matching-ipv6-address-to-a-cidr-subnet)
*/
public static function ip6net2bits($inet)
{
$binaryip = '';
$unpacked = @unpack('A16', inet_pton($inet));
foreach (str_split($unpacked[1]) as $char) {
$binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
}
return $binaryip;
}
}
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jan 31, 3:38 PM (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426365
Default Alt Text
(39 KB)

Event Timeline