Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117752152
D2954.1775188122.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
26 KB
Referenced Files
None
Subscribers
None
D2954.1775188122.diff
View Options
diff --git a/composer.json b/composer.json
--- a/composer.json
+++ b/composer.json
@@ -2,12 +2,7 @@
"name": "kolab/free-busy",
"description": "Kolab Free/Busy Service",
"license": "AGPL-3.0",
- "version": "1.1.0",
"repositories": [
- {
- "type": "pear",
- "url": "http://pear.php.net/"
- },
{
"type": "vcs",
"url": "https://git.kolab.org/diffusion/PNL/php-net_ldap.git"
@@ -15,13 +10,13 @@
],
"require": {
"php": ">=5.3.3",
- "monolog/monolog": "1.2.*",
- "desarrolla2/cache": "dev-master",
- "sabre/vobject": "~3.3.3"
+ "monolog/monolog": "~1.2",
+ "sabre/vobject": "~3.3.3",
+ "garethp/php-ews": "~0.10.1"
},
"require-dev": {
- "pear-pear.php.net/net_ldap2": ">=2.0.12",
- "kolab/Net_LDAP3": "dev-master"
+ "pear/net_ldap2": "~2.2.0",
+ "kolab/net_ldap3": "~1.1.3"
},
- "minimum-stability": "dev"
+ "minimum-stability": "stable"
}
diff --git a/composer.json-dist b/composer.json-dist
--- a/composer.json-dist
+++ b/composer.json-dist
@@ -2,7 +2,6 @@
"name": "kolab/free-busy",
"description": "Kolab Free/Busy Service",
"license": "AGPL-3.0",
- "version": "1.1.0",
"autoload": {
"psr-0": {
"": "/usr/share/pear/"
diff --git a/lib/Kolab/Config.php b/lib/Kolab/Config.php
--- a/lib/Kolab/Config.php
+++ b/lib/Kolab/Config.php
@@ -175,7 +175,9 @@
}
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;
+ $this->data[$pkey] = isset($this->data[$pkey]) && is_array($this->data[$pkey])
+ ? array_replace_recursive((array)$this->data[$pkey], $config)
+ : $config;
}
foreach ((array)$config as $key => $val) {
@@ -216,7 +218,7 @@
*/
public function __get($name)
{
- return $this->data[$name];
+ return isset($this->data[$name]) ? $this->data[$name] : null;
}
/**
diff --git a/lib/Kolab/FreeBusy/Directory.php b/lib/Kolab/FreeBusy/Directory.php
--- a/lib/Kolab/FreeBusy/Directory.php
+++ b/lib/Kolab/FreeBusy/Directory.php
@@ -30,91 +30,108 @@
*/
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, $this->config)) {
- // 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);
-
- // cache the generated data
- if ($data && $this->config['cacheto'] && !$source->isCached()) {
- $path = preg_replace_callback(
- '/%\{?([a-z0-9]+)\}?/',
- function($m) use ($user) { return $user[$m[1]]; },
- $this->config['cacheto']
- );
-
- if (!@file_put_contents($path, $data, LOCK_EX)) {
- Logger::get('directory')->addError("Failed to write to cache file '" . $path . "'!");
- }
- }
- }
-
- return $data;
- }
- }
-
- return false;
- }
-
- /**
- * Modify attribute values according to config
- */
- protected function postprocessAttrib($attrib)
- {
- if (!empty($this->config['lc_attributes'])) {
- foreach (Config::convert($this->config['lc_attributes'], Config::ARR) as $key) {
- if (!empty($attrib[$key]))
- $attrib[$key] = strtolower($attrib[$key]);
- }
- }
-
- return $attrib;
- }
-
-}
\ No newline at end of file
+ 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)
+ {
+ $logger = Logger::get('directory');
+
+ // resolve user record first
+ if ($user = $this->resolve($user)) {
+ $fbsource = $this->config['fbsource'];
+ if ($source = Source::Factory($fbsource, $this->config)) {
+ $user = $this->postprocessAttrib($user);
+
+ // Find the cached data
+ if (!empty($this->config['cacheto'])) {
+ $path = preg_replace_callback(
+ '/%\{?([a-z0-9]+)\}?/',
+ function($m) use ($user) { return $user[$m[1]]; },
+ $this->config['cacheto']
+ );
+
+ // check for cached data
+ if (file_exists($path)) {
+ if (empty($this->config['expires']) || filemtime($path) + Utils::getOffsetSec($this->config['expires']) >= time()) {
+ $logger->addInfo("Deliver cached data from {$path}");
+ return file_get_contents($path);
+ }
+ }
+ }
+
+ // forward request to Source instance
+ if ($data = $source->getFreeBusyData($user, $extended)) {
+ // send data through the according format converter
+ $converter = Format::factory($this->config['format']);
+ $data = $converter->toVCalendar($data);
+
+ // cache the generated data
+ if ($data && !empty($path)) {
+ if (!file_put_contents($path, $data, LOCK_EX)) {
+ $logger->addError("Failed to write to cache in {$path}");
+ } else {
+ $logger->addInfo("Cached data in {$path}");
+ }
+ }
+ }
+
+ return $data;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Modify attribute values according to config
+ */
+ protected function postprocessAttrib($attrib)
+ {
+ if (!empty($this->config['lc_attributes'])) {
+ foreach (Config::convert($this->config['lc_attributes'], Config::ARR) as $key) {
+ if (!empty($attrib[$key])) {
+ $attrib[$key] = strtolower($attrib[$key]);
+ }
+ }
+ }
+
+ return $attrib;
+ }
+}
diff --git a/lib/Kolab/FreeBusy/DirectoryStatic.php b/lib/Kolab/FreeBusy/DirectoryStatic.php
--- a/lib/Kolab/FreeBusy/DirectoryStatic.php
+++ b/lib/Kolab/FreeBusy/DirectoryStatic.php
@@ -31,29 +31,28 @@
*/
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;
- }
-
+ /**
+ * 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/Source.php b/lib/Kolab/FreeBusy/Source.php
--- a/lib/Kolab/FreeBusy/Source.php
+++ b/lib/Kolab/FreeBusy/Source.php
@@ -29,7 +29,6 @@
abstract class Source
{
protected $config = array();
- protected $cached = false;
/**
* Factory method creating an instace of Source according to config
@@ -51,6 +50,7 @@
case 'fbd':
case 'fbdaemon': return new SourceFBDaemon($config + $conf);
case 'aggregate': return new SourceAggregator($config + $conf);
+ case 'ews': return new SourceEWS($config + $conf);
}
Logger::get('source')->addError("Invalid source configuration: " . $url);
@@ -68,7 +68,7 @@
/**
* Retrieve free/busy data for the given user
*
- * @param array Hash array with user attributes
+ * @param false|string vCalendar (ICS) file output, or False on error
*/
abstract public function getFreeBusyData($user, $extended);
@@ -81,7 +81,7 @@
foreach ($this->config as $k => $val) {
if (is_string($val) && strpos($val, '%') !== false) {
$val = preg_replace_callback(
- '/%\{?([a-z0-9]+)\}?/',
+ '/%\{?([a-z]{1}[a-z0-9]*)\}?/',
function($m) use ($k, $user) {
$enc = $k == 'url' || $k == 'query' || $k == 'fbsource';
return $enc ? urlencode($user[$m[1]]) : $user[$m[1]];
@@ -94,30 +94,4 @@
return $config;
}
-
- /**
- * Helper method to check if a cached file exists and is still valid
- *
- * @param array Hash array with (replaced) config properties
- * @return string Cached free-busy data or false if cache file doesn't exist or is expired
- */
- protected function getCached($config)
- {
- if ($config['cacheto'] && file_exists($config['cacheto'])) {
- if (empty($config['expires']) || filemtime($config['cacheto']) + Utils::getOffsetSec($config['expires']) >= time()) {
- $this->cached = true;
- return file_get_contents($config['cacheto']);
- }
- }
-
- return false;
- }
-
- /**
- * Return the value of the 'cached' flag
- */
- public function isCached()
- {
- return $this->cached;
- }
}
diff --git a/lib/Kolab/FreeBusy/SourceAggregator.php b/lib/Kolab/FreeBusy/SourceAggregator.php
--- a/lib/Kolab/FreeBusy/SourceAggregator.php
+++ b/lib/Kolab/FreeBusy/SourceAggregator.php
@@ -40,7 +40,7 @@
public function getFreeBusyData($user, $extended)
{
$log = Logger::get('aggregate', intval($this->config['loglevel']));
- # $config = $this->getUserConfig($user);
+ // $config = $this->getUserConfig($user);
$attr = str_replace('%', '', strval($this->config['path'] ?: $this->config['host']));
if (!empty($user[$attr])) {
diff --git a/lib/Kolab/FreeBusy/SourceEWS.php b/lib/Kolab/FreeBusy/SourceEWS.php
new file mode 100644
--- /dev/null
+++ b/lib/Kolab/FreeBusy/SourceEWS.php
@@ -0,0 +1,202 @@
+<?php
+
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @author Aleksander Machniak <machniak@kolabsys.com>
+ *
+ * Copyright (C) 2013-2021, Apheleia IT AG <contact@apheleia.ch>
+ *
+ * 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 garethp\ews\API;
+use garethp\ews\API\Exception;
+
+/**
+ * Implementation of a Free/Busy data source reading from an EWS server
+ */
+class SourceEWS extends Source
+{
+ /**
+ * @see Source::getFreeBusyData()
+ */
+ public function getFreeBusyData($user, $extended)
+ {
+ $config = $this->getUserConfig($user);
+ $user = is_array($user) ? $user['s'] : $user;
+
+ // Get start and end date for the request
+ list($start, $end) = $this->getPeriod();
+
+ // Fetch the availability info from the EWS server
+ $data = $this->fetchData($user, $start, $end, $config);
+
+ if ($data === false) {
+ return false;
+ }
+
+ // Process the data (convert to ics)
+ foreach ($data as $idx => $event) {
+ // Map the busy-type property
+ switch ($event->getBusyType()) {
+ case 'Free':
+ $fbtype = 'FREE';
+ break;
+
+ case 'Tentative':
+ $fbtype = 'BUSY-TENTATIVE';
+ break;
+
+ case 'Busy':
+ // case 'OOF':
+ // case 'NoData':
+ default:
+ $fbtype = 'BUSY';
+ break;
+ }
+
+ $startTime = new \DateTime($event->getStartTime(), new \DateTimeZone('UTC'));
+ $endTime = new \DateTime($event->getEndTime(), new \DateTimeZone('UTC'));
+
+ $data[$idx] = sprintf(
+ "FREEBUSY;FBTYPE=%s:%s/%s",
+ $fbtype,
+ $startTime->format('Ymd\THis\Z'),
+ $endTime->format('Ymd\THis\Z'),
+ );
+ }
+
+ return sprintf(
+ "BEGIN:VCALENDAR\n"
+ . "PRODID:%s\n"
+ . "VERSION:2.0\n"
+ . "METHOD:PUBLISH\n"
+ . "BEGIN:VFREEBUSY\n"
+ . "ORGANIZER:mailto:%s\n"
+ . "UID:%s\n"
+ . "DTSTAMP:%s\n"
+ . "DTSTART:%s\n"
+ . "DTEND:%s\n"
+ . "%s\n"
+ . "END:VFREEBUSY\n"
+ . "END:VCALENDAR\n",
+ Utils::PRODID,
+ $user,
+ \date('YmdHi') . '-' . \substr(\md5($user), 0, 16),
+ $start->format('Ymd\THis\Z'),
+ $start->format('Ymd\THis\Z'),
+ $end->format('Ymd\THis\Z'),
+ implode("\n", $data)
+ );
+ }
+
+ /**
+ * Fetches the user availability data from the EWS server
+ */
+ private function fetchData($user, $startdate, $enddate, $config)
+ {
+ $logger = Logger::get('ews');
+
+ $logger->addDebug(sprintf("Fetching data from %s for %s...", $config['host'], $user));
+
+ try {
+ // Create and build the client
+ $api = API::withUsernameAndPassword(
+ $config['host'],
+ urldecode($config['user']),
+ urldecode($config['pass']),
+ array(
+ // return date-time in UTC
+ 'timezone' => 'UTC',
+ // act as the requested user
+ 'impersonation' => $user
+ )
+ );
+
+ $calendar = $api->getCalendar();
+
+ // This is using the EWS's GetUserAvailability API
+ $options = array();
+/*
+ $options = array(
+ 'FreeBusyViewOptions' => array(
+ 'MergedFreeBusyIntervalInMinutes' => 15 // default: 30
+ )
+ );
+*/
+ $availability = $calendar->getAvailabilityFor($startdate, $enddate, array($user), $options);
+
+ // Check response status
+ $responseMessage = $availability->getFreeBusyResponseArray()->FreeBusyResponse->getResponseMessage();
+ if ($responseMessage->getResponseClass() === 'Error') {
+ throw new Exception\ExchangeException($responseMessage);
+ }
+
+ $items = $availability->getFreeBusyResponseArray()->FreeBusyResponse->getFreeBusyView()->getCalendarEventArray();
+
+ if ($items && $items->CalendarEvent) {
+ $logger->addDebug(sprintf("Found %d events", count($items->CalendarEvent)));
+ return $items->CalendarEvent;
+ }
+
+ $logger->addDebug("Found 0 events");
+ return array();
+ } catch (Exception\UnauthorizedException $e) {
+ $logger->addError("EWS access unauthorized");
+ } catch (Exception\ServiceUnavailableException $e) {
+ $logger->addError("EWS service unavailable");
+ } catch (Exception\NoResponseReturnedException $e) {
+ $logger->addError("EWS no response");
+ } catch (Exception\AutodiscoverFailed $e) {
+ $logger->addError("EWS autodiscovery failed");
+ } catch (\Exception $e) {
+ $logger->addError($e->getMessage() ?: "EWS unknown error");
+ }
+
+ return false;
+ }
+
+ /**
+ * Get request period start and end
+ */
+ private function getPeriod()
+ {
+ // use date from HTTP query
+ if (!empty($_GET['dtstart']) &&
+ ($dtstart = \filter_input(INPUT_GET, 'dtstart', FILTER_SANITIZE_STRING))
+ ) {
+ try {
+ $start = new \DateTime($dtstart, new \DateTimezone('UTC'));
+ } catch (Exception $e) {
+ // ignore
+ }
+ }
+
+ if (empty($start)) {
+ $start = new \DateTime('now', new \DateTimeZone('UTC'));
+ }
+
+ // Set the period to 7 days in the past and 42-7 days in the future
+ // Note: 42 days is max. supported period on EWS
+
+ $start->sub(new \DateInterval('P7D'));
+ $end = clone $start;
+ $end->add(new \DateInterval('P42D'));
+
+ return array($start, $end);
+ }
+}
diff --git a/lib/Kolab/FreeBusy/SourceFBDaemon.php b/lib/Kolab/FreeBusy/SourceFBDaemon.php
--- a/lib/Kolab/FreeBusy/SourceFBDaemon.php
+++ b/lib/Kolab/FreeBusy/SourceFBDaemon.php
@@ -42,23 +42,6 @@
// log this...
$log->addInfo("Fetching data for ", $config);
- // caching is enabled
- if (!empty($config['cacheto'])) {
- // check for cached data
- if ($cached = $this->getCached($config)) {
- $log->addInfo("Deliver cached data from " . $config['cacheto']);
- return $cached;
- }
- // touch cache file to avoid multiple requests generating the same data
- if (file_exists($config['cacheto'])) {
- touch($config['cacheto']);
- }
- else {
- file_put_contents($config['cacheto'], Utils::dummyVFreebusy($user['mail']));
- $tempfile = $config['cacheto'];
- }
- }
-
// compose command for freebusyd
if (!empty($config['folder'])) {
$cmd = 'FOLDER';
@@ -128,10 +111,6 @@
}
return $fbdata;
}
- // remove (temporary) cache file again
- else if ($tempfile) {
- unlink($tempfile);
- }
// not found
return false;
diff --git a/lib/Kolab/FreeBusy/SourceIMAP.php b/lib/Kolab/FreeBusy/SourceIMAP.php
--- a/lib/Kolab/FreeBusy/SourceIMAP.php
+++ b/lib/Kolab/FreeBusy/SourceIMAP.php
@@ -79,23 +79,6 @@
// log this...
$log->addInfo("Fetching data for ", $config);
- // caching is enabled
- if (!empty($config['cacheto'])) {
- // check for cached data
- if ($cached = $this->getCached($config)) {
- $log->addInfo("Deliver cached data from " . $config['cacheto']);
- return $cached;
- }
- // touch cache file to avoid multiple requests generating the same data
- if (file_exists($config['cacheto'])) {
- touch($config['cacheto']);
- }
- else {
- file_put_contents($config['cacheto'], Utils::dummyVFreebusy($user['mail']));
- $tempfile = $config['cacheto'];
- }
- }
-
// compose a list of user email addresses
$user_email = array();
foreach (Config::convert($this->config['mail_attributes'], Config::ARR) as $key) {
@@ -258,10 +241,6 @@
// serialize to VCALENDAR format
return $freebusy->serialize();
}
- // remove (temporary) cache file again
- else if ($tempfile) {
- unlink($tempfile);
- }
return false;
}
diff --git a/lib/Kolab/FreeBusy/Utils.php b/lib/Kolab/FreeBusy/Utils.php
--- a/lib/Kolab/FreeBusy/Utils.php
+++ b/lib/Kolab/FreeBusy/Utils.php
@@ -249,7 +249,7 @@
}
}
- // Should probably be a setting. For now, do 8 weeks in the past
+ // Should probably be a setting. For now, do 16 weeks into the future
return new \DateTime('now + 16 weeks 00:00:00', new \DateTimezone('UTC'));
}
diff --git a/public_html/index.php b/public_html/index.php
--- a/public_html/index.php
+++ b/public_html/index.php
@@ -50,79 +50,77 @@
// load config
$config = Config::get_instance(KOLAB_FREEBUSY_ROOT . '/config');
if ($config->valid()) {
- // check for trusted IP first
- $remote_ip = Utils::remoteIP();
- $trusted_ip = $config->trustednetworks ? Utils::checkIPRange($remote_ip, $config->get('trustednetworks.allow', array(), Config::ARR)) : false;
-
- $log = Logger::get('web');
-
- $uri = $_SERVER['REDIRECT_URL'];
-
- // we're not always redirected here
- if (empty($uri)) {
- $uri = $_SERVER['REQUEST_URI'];
- $log->addDebug('Request (direct): ' . $uri, array('ip' => $remote_ip, 'trusted' => $trusted_ip));
- } else {
- $log->addDebug('Request (redirect): ' . $uri, array('ip' => $remote_ip, 'trusted' => $trusted_ip));
- }
-
- list($uri, $args) = explode('?', $uri);
-
- // check HTTP authentication
- if (!$trusted_ip && $config->httpauth) {
- $_SERVER['FREEBUSY_URI'] = urldecode(rtrim($uri, '/'));
-
- if (!HTTPAuth::check($config->httpauth)) {
- $log->addDebug("Abort with 401 Unauthorized");
- header('WWW-Authenticate: Basic realm="Kolab Free/Busy Service"');
- header($_SERVER['SERVER_PROTOCOL'] . " 401 Unauthorized", true);
- exit;
- }
- }
-
- #header('Content-type: text/calendar; charset=utf-8', true);
- header('Content-type: text/plain; charset=utf-8', true);
-
- // analyse request
- $user = $_SERVER['FREEBUSY_USER'];
- $extended = !empty($_SERVER['FREEBUSY_EXTENDED']);
-
- if (!$user) {
- $url = array_filter(explode('/', $uri));
- $user = strtolower(array_pop($url));
-
- // remove file extension
- if (preg_match('/^(.+)\.([ipx]fb)$/i', $user, $m)) {
- $user = urldecode($m[1]);
- $extended = $m[2] == 'xfb';
- }
- }
-
- // iterate over directories
- foreach ($config->directory as $key => $dirconfig) {
- $log->addDebug("Trying directory $key", $dirconfig);
-
- $directory = Directory::factory($dirconfig);
- if ($directory && ($fbdata = $directory->getFreeBusyData($user, $extended))) {
- $log->addInfo("Found valid data for user $user in directory $key");
- echo $fbdata;
- exit;
- }
- }
-
- // return 404 if request was sent from a trusted IP
- if ($trusted_ip) {
- $log->addDebug("Returning '404 Not Found' for user $user");
- header($_SERVER['SERVER_PROTOCOL'] . " 404 Not found", true);
- }
- else {
- $log->addInfo("Returning empty Free/Busy list for user $user");
-
- // Return an apparent empty Free/Busy list.
- print Utils::dummyVFreebusy($user);
- }
+ // check for trusted IP first
+ $remote_ip = Utils::remoteIP();
+ $trusted_ip = $config->trustednetworks ? Utils::checkIPRange($remote_ip, $config->get('trustednetworks.allow', array(), Config::ARR)) : false;
+
+ $log = Logger::get('web');
+
+ $uri = isset($_SERVER['REDIRECT_URL']) ? $_SERVER['REDIRECT_URL'] : '';
+
+ // we're not always redirected here
+ if (empty($uri)) {
+ $uri = $_SERVER['REQUEST_URI'];
+ $log->addDebug('Request (direct): ' . $uri, array('ip' => $remote_ip, 'trusted' => $trusted_ip));
+ } else {
+ $log->addDebug('Request (redirect): ' . $uri, array('ip' => $remote_ip, 'trusted' => $trusted_ip));
+ }
+
+ list($uri, ) = explode('?', $uri);
+
+ // check HTTP authentication
+ if (!$trusted_ip && $config->httpauth) {
+ $_SERVER['FREEBUSY_URI'] = urldecode(rtrim($uri, '/'));
+
+ if (!HTTPAuth::check($config->httpauth)) {
+ $log->addDebug("Abort with 401 Unauthorized");
+ header('WWW-Authenticate: Basic realm="Kolab Free/Busy Service"');
+ header($_SERVER['SERVER_PROTOCOL'] . " 401 Unauthorized", true);
+ exit;
+ }
+ }
+
+ //header('Content-type: text/calendar; charset=utf-8', true);
+ header('Content-type: text/plain; charset=utf-8', true);
+
+ // analyse request
+ $user = isset($_SERVER['FREEBUSY_USER']) ? $_SERVER['FREEBUSY_USER'] : null;
+ $extended = !empty($_SERVER['FREEBUSY_EXTENDED']);
+
+ if (!$user) {
+ $url = array_filter(explode('/', $uri));
+ $user = strtolower(array_pop($url));
+
+ // remove file extension
+ if (preg_match('/^(.+)\.([ipx]fb)$/i', $user, $m)) {
+ $user = urldecode($m[1]);
+ $extended = $m[2] == 'xfb';
+ }
+ }
+
+ // iterate over directories
+ foreach ($config->directory as $key => $dirconfig) {
+ $log->addDebug("Trying directory $key", $dirconfig);
+
+ $directory = Directory::factory($dirconfig);
+ if ($directory && ($fbdata = $directory->getFreeBusyData($user, $extended))) {
+ $log->addInfo("Found valid data for user $user in directory $key");
+ echo $fbdata;
+ exit;
+ }
+ }
+
+ // return 404 if request was sent from a trusted IP
+ if ($trusted_ip) {
+ $log->addDebug("Returning '404 Not Found' for user $user");
+ header($_SERVER['SERVER_PROTOCOL'] . " 404 Not found", true);
+ } else {
+ $log->addInfo("Returning empty Free/Busy list for user $user");
+
+ // Return an apparent empty Free/Busy list.
+ print Utils::dummyVFreebusy($user);
+ }
}
// exit with error
# header($_SERVER['SERVER_PROTOCOL'] . " 500 Internal Server Error", true);
-
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 3, 3:48 AM (10 h, 27 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822477
Default Alt Text
D2954.1775188122.diff (26 KB)
Attached To
Mode
D2954: EWS source implementation
Attached
Detach File
Event Timeline