Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2527847
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
39 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jan 31, 3:38 PM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426365
Default Alt Text
(39 KB)
Attached To
Mode
R28 freebusy
Attached
Detach File
Event Timeline
Log In to Comment