diff --git a/composer.json b/composer.json index 053ce31..25546d2 100644 --- a/composer.json +++ b/composer.json @@ -1,29 +1,28 @@ { "name": "kolab/irony", "description": "iRony - The Kolab WebDAV/CalDAV/CardDAV Server", "license": "AGPL-3.0", "version": "0.4-dev", "repositories": [ { "type": "pear", "url": "http://pear.php.net/" }, { "type": "vcs", "url": "git://git.kolab.org/git/pear/Net_LDAP3" } ], "require": { "php": ">=5.4.1", "sabre/dav" : "~2.1.0" }, "require-dev": { "pear/mail_mime": ">=1.8.9", "pear/mail_mime-decode": ">=1.5.5", - "pear/http_request2": ">=2.1.1", "pear-pear.php.net/net_idna2": ">=0.1.1", "pear-pear.php.net/net_ldap2": ">=2.0.12", "kolab/Net_LDAP3": "dev-master" }, "minimum-stability": "dev" } diff --git a/lib/Kolab/CalDAV/Plugin.php b/lib/Kolab/CalDAV/Plugin.php index 815f37b..3f71070 100644 --- a/lib/Kolab/CalDAV/Plugin.php +++ b/lib/Kolab/CalDAV/Plugin.php @@ -1,239 +1,171 @@ * * Copyright (C) 2013, Kolab Systems AG * * 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 . */ namespace Kolab\CalDAV; use Sabre\DAV; use Sabre\CalDAV; use Sabre\VObject; use Kolab\DAV\Auth\HTTPBasic; /** * Extended CalDAV plugin to tweak data validation */ class Plugin extends CalDAV\Plugin { // make already parsed text/calednar blocks available for later use public static $parsed_vcalendar; public static $parsed_vevent; // allow the backend to force a redirect Location public static $redirect_basename; /** * Initializes the plugin * * @param DAV\Server $server * @return void */ public function initialize(DAV\Server $server) { parent::initialize($server); $server->on('afterCreateFile', array($this, 'afterWriteContent')); $server->on('afterWriteContent', array($this, 'afterWriteContent')); } /** * Inject some additional HTTP response headers */ public function afterWriteContent($uri, $node) { // send Location: header to corrected URI if (self::$redirect_basename) { $path = explode('/', $uri); array_pop($path); array_push($path, self::$redirect_basename); $this->server->httpResponse->setHeader('Location', $this->server->getBaseUri() . join('/', array_map('urlencode', $path))); self::$redirect_basename = null; } } /** * Checks if the submitted iCalendar data is in fact, valid. * * An exception is thrown if it's not. * * @param resource|string $data * @param string $path * @return void */ protected function validateICalendar(&$data, $path) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } // Converting the data to unicode, if needed. $data = DAV\StringUtil::ensureUTF8($data); try { // modification: Set options to be more tolerant when parsing extended or invalid properties $vobj = VObject\Reader::read($data, VObject\Reader::OPTION_FORGIVING | VObject\Reader::OPTION_IGNORE_INVALID_LINES); // keep the parsed object in memory for later processing if ($vobj->name == 'VCALENDAR') { self::$parsed_vcalendar = $vobj; foreach ($vobj->getBaseComponents() as $vevent) { if ($vevent->name == 'VEVENT' || $vevent->name == 'VTODO') { self::$parsed_vevent = $vevent; break; } } } } catch (VObject\ParseException $e) { throw new DAV\Exception\UnsupportedMediaType('This resource requires valid iCalendar 2.0 data. Parse error: ' . $e->getMessage()); } if ($vobj->name !== 'VCALENDAR') { throw new DAV\Exception\UnsupportedMediaType('This collection can only support iCalendar objects.'); } // Get the Supported Components for the target calendar list($parentPath,$object) = DAV\URLUtil::splitPath($path); $calendarProperties = $this->server->getProperties($parentPath,array('{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set')); $supportedComponents = $calendarProperties['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue(); $foundType = null; $foundUID = null; foreach($vobj->getComponents() as $component) { switch($component->name) { case 'VTIMEZONE': continue 2; case 'VEVENT': case 'VTODO': case 'VJOURNAL': if (is_null($foundType)) { $foundType = $component->name; if (!in_array($foundType, $supportedComponents)) { throw new CalDAV\Exception\InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType); } if (!isset($component->UID)) { throw new DAV\Exception\BadRequest('Every ' . $component->name . ' component must have an UID'); } $foundUID = (string)$component->UID; } else { if ($foundType !== $component->name) { throw new DAV\Exception\BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType); } if ($foundUID !== (string)$component->UID) { throw new DAV\Exception\BadRequest('Every ' . $component->name . ' in this object must have identical UIDs'); } } break; default: throw new DAV\Exception\BadRequest('You are not allowed to create components of type: ' . $component->name . ' here'); } } if (!$foundType) throw new DAV\Exception\BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL'); } /** * Returns a list of features for the DAV: HTTP header. * Including 'calendar-schedule' to enable scheduling support in Thunderbird Lightning. * * @return array */ public function getFeatures() { $features = parent::getFeatures(); $features[] = 'calendar-schedule'; return $features; } - /** - * Returns free-busy information for a specific address. The returned - * data is an array containing the following properties: - * - * calendar-data : A VFREEBUSY VObject - * request-status : an iTip status code. - * href: The principal's email address, as requested - * - * @param string $email address - * @param \DateTime $start - * @param \DateTime $end - * @param VObject\Component $request - * @return array - */ - protected function getFreeBusyForEmail($email, \DateTime $start, \DateTime $end, VObject\Component $request) - { - console(__METHOD__, $email, $start, $end); - - $email = preg_replace('/^mailto:/', '', $email); - - // pass-through the pre-generatd free/busy feed from Kolab's free/busy service - if ($fburl = \kolab_storage::get_freebusy_url($email)) { - // use PEAR::HTTP_Request2 for data fetching - // @include_once('HTTP/Request2.php'); - - try { - $rcube = \rcube::get_instance(); - $request = new \HTTP_Request2($fburl); - $request->setConfig(array( - 'store_body' => true, - 'follow_redirects' => true, - 'ssl_verify_peer' => $rcube->config->get('kolab_ssl_verify_peer', true), - )); - - $response = $request->send(); - - // authentication required - if ($response->getStatus() == 401) { - $request->setAuth(HTTPBasic::$current_user, HTTPBasic::$current_pass); - $response = $request->send(); - } - - // success! - if ($response->getStatus() == 200) { - $vcalendar = VObject\Reader::read($response->getBody(), VObject\Reader::OPTION_FORGIVING | VObject\Reader::OPTION_IGNORE_INVALID_LINES); - return array( - 'calendar-data' => $vcalendar, - 'request-status' => '2.0;Success', - 'href' => 'mailto:' . $email, - ); - } - } - catch (\Exception $e) { - // log failures - \rcube::raise_error($e, true, false); - } - } - else { - // generate free/busy data from this user's calendars - return parent::getFreeBusyForEmail($email, $start, $end, $request); - } - - // return "not found" - return array( - 'request-status' => '3.7;Could not find principal', - 'href' => 'mailto:' . $email, - ); - } } \ No newline at end of file diff --git a/lib/Kolab/CalDAV/SchedulePlugin.php b/lib/Kolab/CalDAV/SchedulePlugin.php new file mode 100644 index 0000000..6ae5151 --- /dev/null +++ b/lib/Kolab/CalDAV/SchedulePlugin.php @@ -0,0 +1,100 @@ + + * + * Copyright (C) 2014, Kolab Systems AG + * + * 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 . + */ + +namespace Kolab\CalDAV; + +use Sabre\DAV; +use Sabre\CalDAV; +use Sabre\VObject; +use Sabre\HTTP; + +/** + * Extended CalDAV Schedule plugin + */ +class SchedulePlugin extends CalDAV\Schedule\Plugin +{ + /** + * Returns free-busy information for a specific address. The returned + * data is an array containing the following properties: + * + * calendar-data : A VFREEBUSY VObject + * request-status : an iTip status code. + * href: The principal's email address, as requested + * + * @param string $email address + * @param \DateTime $start + * @param \DateTime $end + * @param VObject\Component $request + * @return array + */ + protected function getFreeBusyForEmail($email, \DateTime $start, \DateTime $end, VObject\Component $request) + { + console(__METHOD__, $email, $start, $end); + + $email = preg_replace('!^mailto:!', '', $email); + + // pass-through the pre-generatd free/busy feed from Kolab's free/busy service + if ($fburl = \kolab_storage::get_freebusy_url($email)) { + try { + $rcube = \rcube::get_instance(); + $client = new HTTP\Client(); + $client->addCurlSetting(CURLOPT_SSL_VERIFYPEER, $rcube->config->get('kolab_ssl_verify_peer', true)); + + // authentication required + $client->on('error:401', function($request, $response, &$retry, $retryCount) { + if ($retryCount <= 1) { + // We're only going to retry exactly once. + $request->setHeader('Authorization', 'Basic ' . base64_encode(HTTPBasic::$current_user . ':' . HTTPBasic::$current_pass)); + $retry = true; + } + }); + + $response = $client->send(new HTTP\Request('GET', $fburl)); + + // success! + if ($response->getStatus() == 200) { + $vcalendar = VObject\Reader::read($response->getBodyAsString(), VObject\Reader::OPTION_FORGIVING | VObject\Reader::OPTION_IGNORE_INVALID_LINES); + return array( + 'calendar-data' => $vcalendar, + 'request-status' => '2.0;Success', + 'href' => 'mailto:' . $email, + ); + } + } + catch (\Exception $e) { + // log failures + \rcube::raise_error($e, true, false); + } + } + else { + // generate free/busy data from this user's calendars + return parent::getFreeBusyForEmail($email, $start, $end, $request); + } + + // return "not found" + return array( + 'request-status' => '3.7;Could not find principal', + 'href' => 'mailto:' . $email, + ); + } +} \ No newline at end of file diff --git a/lib/Kolab/CalDAV/UserCalendars.php b/lib/Kolab/CalDAV/UserCalendars.php index dcd2aff..3573baa 100644 --- a/lib/Kolab/CalDAV/UserCalendars.php +++ b/lib/Kolab/CalDAV/UserCalendars.php @@ -1,166 +1,167 @@ * * Copyright (C) 2013, Kolab Systems AG * * 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 . */ namespace Kolab\CalDAV; use Sabre\DAV; use Sabre\DAVACL; use Sabre\CalDAV\Backend; use Sabre\CalDAV\Schedule; use Kolab\CalDAV\Calendar; /** * The UserCalenders class contains all calendars associated to one user * */ class UserCalendars extends \Sabre\CalDAV\UserCalendars implements DAV\IExtendedCollection, DAVACL\IACL { private $outbox; /** * Returns a list of calendars * * @return array */ public function getChildren() { $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); $objs = array(); foreach ($calendars as $calendar) { // TODO: (later) add sharing support by implenting this all if ($this->caldavBackend instanceof Backend\SharingSupport) { if (isset($calendar['{http://calendarserver.org/ns/}shared-url'])) { $objs[] = new SharedCalendar($this->caldavBackend, $calendar); } else { $objs[] = new ShareableCalendar($this->caldavBackend, $calendar); } } else { $objs[] = new Calendar($this->caldavBackend, $calendar); } } // add support for scheduling AKA free/busy + // TODO: remove when CalendarBackend implements SchedulingSupport $objs[] = new Schedule\Outbox($this->principalInfo['uri']); // TODO: add notification support (check with clients first, if anybody supports it) if ($this->caldavBackend instanceof Backend\NotificationSupport) { $objs[] = new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); } return $objs; } /** * Returns a single calendar, by name * * @param string $name * @return Calendar */ public function getChild($name) { if ($name == 'outbox') { return new Schedule\Outbox($this->principalInfo['uri']); } if ($calendar = $this->caldavBackend->getCalendarByName($name)) { $calendar['principaluri'] = $this->principalInfo['uri']; return new Calendar($this->caldavBackend, $calendar); } throw new DAV\Exception\NotFound('Calendar with name \'' . $name . '\' could not be found'); } /** * Checks if a calendar exists. * * @param string $name * @return bool */ public function childExists($name) { if ($this->caldavBackend->getCalendarByName($name)) { return true; } return false; } /** * Returns a list of ACE's for this node. * * Each ACE has the following properties: * - 'privilege', a string such as {DAV:}read or {DAV:}write. These are currently the only supported privileges * - 'principal', a url to the principal who owns the node * - 'protected' (optional), indicating that this ACE is not allowed to be updated. * * @return array */ public function getACL() { // define rights for the user's calendar root (which is in fact INBOX) return array( array( 'privilege' => '{DAV:}read', 'principal' => $this->principalInfo['uri'], 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => $this->principalInfo['uri'], 'protected' => true, ), /* TODO: implement sharing support array( 'privilege' => '{DAV:}read', 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}write', 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', 'protected' => true, ), array( 'privilege' => '{DAV:}read', 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read', 'protected' => true, ), */ ); } /** * Updates the ACL * * This method will receive a list of new ACE's. * * @param array $acl * @return void */ public function setACL(array $acl) { // TODO: implement this throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported'); } } diff --git a/lib/Kolab/Utils/DAVLogger.php b/lib/Kolab/Utils/DAVLogger.php index 94a39ba..6e2f608 100644 --- a/lib/Kolab/Utils/DAVLogger.php +++ b/lib/Kolab/Utils/DAVLogger.php @@ -1,195 +1,194 @@ * * Copyright (C) 2013, Kolab Systems AG * * 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 . */ namespace Kolab\Utils; use \rcube; use Sabre\DAV; use Kolab\DAV\Auth\HTTPBasic; /** * Utility class to log debug information about processed DAV requests */ class DAVLogger extends DAV\ServerPlugin { const CONSOLE = 1; const HTTP_REQUEST = 2; const HTTP_RESPONSE = 4; private $rcube; private $server; private $method; private $loglevel; /** * Default constructor */ public function __construct($level = 1) { $this->rcube = rcube::get_instance(); $this->loglevel = $level; } /** * This initializes the plugin. * This method should set up the required event subscriptions. * * @param Server $server */ public function initialize(DAV\Server $server) { $this->server = $server; $server->on('beforeMethod', array($this, '_beforeMethod'), 15); $server->on('exception', array($this, '_exception')); $server->on('exit', array($this, '_exit')); // replace $server->httpResponse with a derived class that can do logging $server->httpResponse = new HTTPResponse(); } /** * Handler for 'beforeMethod' events */ - public function _beforeMethod($method, $uri) + public function _beforeMethod($request, $response) { - $this->method = $method; + $this->method = $request->getMethod(); // turn on per-user http logging if the destination file exists if ($this->loglevel < 2 && $this->rcube->config->get('kolabdav_user_debug', false) && ($log_dir = $this->user_log_dir()) && file_exists($log_dir . '/httpraw')) { $this->loglevel |= (self::HTTP_REQUEST | self::HTTP_RESPONSE); } // log full HTTP request data if ($this->loglevel & self::HTTP_REQUEST) { - $request = $this->server->httpRequest; - $content_type = $request->getHeader('CONTENT_TYPE'); + $content_type = $request->getHeader('Content-Type'); if (strpos($content_type, 'text/') === 0 || strpos($content_type, 'application/xml') === 0) { - $http_body = $request->getBody(true); + $http_body = $request->getBodyAsString(); // Hack for reading php:://input because that stream can only be read once. // This is why we re-populate the request body with the existing data. $request->setBody($http_body); } else if (!empty($content_type)) { $http_body = '[binary data]'; } // catch all headers $http_headers = array(); foreach (apache_request_headers() as $hdr => $value) { if (strtolower($hdr) == 'authorization') { $method = preg_match('/^((basic|digest)\s+)/i', $value, $m) ? $m[1] : ''; $value = $method . str_repeat('*', strlen($value) - strlen($method)); } $http_headers[$hdr] = "$hdr: $value"; } $this->write_log('httpraw', $request->getMethod() . ' ' . $request->getUrl() . ' ' . $_SERVER['SERVER_PROTOCOL'] . "\n" . join("\n", $http_headers) . "\n\n" . $http_body); } // log to console if ($this->loglevel & self::CONSOLE) { - $this->write_log('console', $method . ' ' . $uri); + $this->write_log('console', $this->method . ' ' . $request->getUrl()); } } /** * Handler for 'exception' events */ public function _exception($e) { // log to console $this->console(get_class($e) . ' (EXCEPTION)', $e->getMessage() /*, $e->getTraceAsString()*/); } /** * Handler for 'exit' events */ public function _exit() { if ($this->loglevel & self::CONSOLE) { $time = microtime(true) - KOLAB_DAV_START; if (function_exists('memory_get_usage')) $mem = round(memory_get_usage() / 1024 / 1024, 1) . 'MB'; if (function_exists('memory_get_peak_usage')) $mem .= '/' . round(memory_get_peak_usage() / 1024 / 1024, 1) . 'MB'; $this->write_log('console', sprintf("/%s: %0.4f sec; %s", $this->method, $time, $mem)); } // log full HTTP reponse if ($this->loglevel & self::HTTP_RESPONSE) { $this->write_log('httpraw', "RESPONSE: " . $this->server->httpResponse->dump()); } } /** * Wrapper for rcube::cosole() to write per-user logs */ public function console(/* ... */) { if ($this->loglevel & self::CONSOLE) { $msg = array(); foreach (func_get_args() as $arg) { $msg[] = !is_string($arg) ? var_export($arg, true) : $arg; } $this->write_log('console', join(";\n", $msg)); } } /** * Wrapper for rcube::write_log() that can write per-user logs */ public function write_log($filename, $msg) { // dump data per user if ($this->rcube->config->get('kolabdav_user_debug', false)) { if ($this->user_log_dir()) { $filename = HTTPBasic::$current_user . '/' . $filename; } else { return; // don't log } } rcube::write_log($filename, $msg); } /** * Get the per-user log directory */ private function user_log_dir() { $log_dir = $this->rcube->config->get('log_dir', RCUBE_INSTALL_PATH . 'logs'); $user_log_dir = $log_dir . '/' . HTTPBasic::$current_user; return HTTPBasic::$current_user && is_writable($user_log_dir) ? $user_log_dir : false; } } \ No newline at end of file diff --git a/public_html/index.php b/public_html/index.php index 9b9c201..348c52c 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -1,197 +1,199 @@ * * Copyright (C) 2013-2014, Kolab Systems AG * * 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 . */ // define some environment variables used throughout the app and libraries define('KOLAB_DAV_ROOT', realpath('../')); define('KOLAB_DAV_VERSION', '0.4-dev'); define('KOLAB_DAV_START', microtime(true)); define('RCUBE_INSTALL_PATH', KOLAB_DAV_ROOT . '/'); define('RCUBE_CONFIG_DIR', KOLAB_DAV_ROOT . '/config/'); define('RCUBE_PLUGINS_DIR', KOLAB_DAV_ROOT . '/lib/plugins/'); // suppress error notices ini_set('error_reporting', E_ALL &~ E_NOTICE &~ E_STRICT); /** * Mapping PHP errors to exceptions. * * While this is not strictly needed, it makes a lot of sense to do so. If an * E_NOTICE or anything appears in your code, this allows SabreDAV to intercept * the issue and send a proper response back to the client (HTTP/1.1 500). */ function exception_error_handler($errno, $errstr, $errfile, $errline ) { throw new ErrorException($errstr, 0, $errno, $errfile, $errline); } //set_error_handler("exception_error_handler"); // use composer's autoloader for dependencies $loader = require_once(KOLAB_DAV_ROOT . '/vendor/autoload.php'); $loader->set('Kolab', array(KOLAB_DAV_ROOT . '/lib')); // register iRony namespace(s) $loader->setUseIncludePath(true); // enable include_path to load PEAR classes from their default location // load the Roundcube framework with its autoloader require_once KOLAB_DAV_ROOT . '/lib/Roundcube/bootstrap.php'; // Roundcube framework initialization $rcube = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS); $rcube->config->load_from_file(RCUBE_CONFIG_DIR . 'dav.inc.php'); // Load plugins $plugins = (array)$rcube->config->get('kolabdav_plugins', array('kolab_auth')); $required = array('libkolab', 'libcalendaring'); $rcube->plugins->init($rcube); $rcube->plugins->load_plugins($plugins, $required); // enable logger if ($rcube->config->get('kolabdav_console') || $rcube->config->get('kolabdav_user_debug')) { $logger = new \Kolab\Utils\DAVLogger((\Kolab\Utils\DAVLogger::CONSOLE | $rcube->config->get('kolabdav_http_log', 0))); } // convenience function, you know it well :-) function console() { global $logger; if ($logger) { call_user_func_array(array($logger, 'console'), func_get_args()); } } // Make sure this setting is turned on and reflects the root url of the *DAV server. $base_uri = $rcube->config->get('base_uri', slashify(substr(dirname($_SERVER['SCRIPT_FILENAME']), strlen($_SERVER['DOCUMENT_ROOT'])))); // add filename to base URI when called without mod_rewrite (e.g. /dav/index.php/calendar) if (strpos($_SERVER['REQUEST_URI'], 'index.php')) $base_uri .= 'index.php/'; // create the various backend instances $auth_backend = new \Kolab\DAV\Auth\HTTPBasic(); $principal_backend = new \Kolab\DAVACL\PrincipalBackend(); $services = array(); foreach (array('CALDAV','CARDDAV','WEBDAV') as $skey) { if (getenv($skey)) $services[$skey] = 1; } // no config means *all* services if (empty($services)) $services = array('CALDAV' => 1, 'CARDDAV' => 1, 'WEBDAV' => 1); // add chwala directories to include path for autoloading if ($services['WEBDAV']) { $include_path = ini_get('include_path') . PATH_SEPARATOR; $include_path .= KOLAB_DAV_ROOT . '/lib/FileAPI' . PATH_SEPARATOR; $include_path .= KOLAB_DAV_ROOT . '/lib/FileAPI/kolab' . PATH_SEPARATOR; $include_path .= KOLAB_DAV_ROOT . '/lib/FileAPI/ext'; set_include_path($include_path); } // Build the directory tree // This is an array which contains the 'top-level' directories in the WebDAV server. if ($services['CALDAV'] || $services['CARDDAV']) { $nodes = array( new \Kolab\CalDAV\Principal\Collection($principal_backend), ); if ($services['CALDAV']) { $caldav_backend = new \Kolab\CalDAV\CalendarBackend(); $caldav_backend->setUserAgent($_SERVER['HTTP_USER_AGENT']); $nodes[] = new \Kolab\CalDAV\CalendarRootNode($principal_backend, $caldav_backend); } if ($services['CARDDAV']) { $carddav_backend = new \Kolab\CardDAV\ContactsBackend(); $carddav_backend->setUserAgent($_SERVER['HTTP_USER_AGENT']); $nodes[] = new \Kolab\CardDAV\AddressBookRoot($principal_backend, $carddav_backend); } if ($services['WEBDAV']) { $nodes[] = new \Kolab\DAV\Collection(\Kolab\DAV\Collection::ROOT_DIRECTORY); } } // register WebDAV service as root else if ($services['WEBDAV']) { $nodes = new \Kolab\DAV\Collection(''); } // the object tree needs in turn to be passed to the server class $server = new \Sabre\DAV\Server($nodes); $server->setBaseUri($base_uri); // connect logger if (is_object($logger)) { $server->addPlugin($logger); } // register some plugins $server->addPlugin(new \Sabre\DAV\Auth\Plugin($auth_backend, 'KolabDAV')); $server->addPlugin(new \Sabre\DAVACL\Plugin()); if ($services['CALDAV']) { $caldav_plugin = new \Kolab\CalDAV\Plugin(); - #$caldav_plugin->setIMipHandler(new \Kolab\CalDAV\IMip()); $server->addPlugin($caldav_plugin); + + $server->addPlugin(new \Kolab\CalDAV\SchedulePlugin()); + #$server->addPlugin(new \Kolab\CalDAV\IMipPlugin()); } if ($services['CARDDAV']) { $server->addPlugin(new \Kolab\CardDAV\Plugin()); } if ($services['WEBDAV']) { // the lock manager is responsible for making sure users don't overwrite each others changes. $locks_backend = new \Kolab\DAV\Locks\Chwala(\Kolab\DAV\Collection::ROOT_DIRECTORY); $server->addPlugin(new \Sabre\DAV\Locks\Plugin($locks_backend)); // intercept some of the garbage files operation systems tend to generate when mounting a WebDAV share $server->addPlugin(new \Kolab\DAV\TempFilesPlugin(KOLAB_DAV_ROOT . '/temp')); } // HTML UI for browser-based access (recommended only for development) if (getenv('DAVBROWSER')) { $server->addPlugin(new \Sabre\DAV\Browser\Plugin()); } // log exceptions in iRony error log $server->on('exception', function($e){ if (!($e instanceof \Sabre\DAV\Exception) || $e->getHTTPCode() == 500) { rcube::raise_error(array( 'code' => 500, 'type' => 'php', 'file' => $e->getFile(), 'line' => $e->getLine(), 'message' => $e->getMessage() . " (error 500)\n" . $e->getTraceAsString(), ), true, false); } }); // finally, process the request $server->exec(); // trigger log $server->emit('exit', array());