diff --git a/application/controllers/Test.php b/application/controllers/Test.php index 0454d16f4..f3803bdae 100644 --- a/application/controllers/Test.php +++ b/application/controllers/Test.php @@ -22,12 +22,12 @@ class Test extends FHC_Controller { $authBackend = new SabreDAVAuthLib(); - $principalBackend = new SabreDAVACLPrincipalBackendLib($authBackend); - $calendarBackend = new SabreDAVCalDAVBackendLib($authBackend); + $principalBackend = new SabreDAVACLPrincipalBackend($authBackend); + $calendarBackend = new SabreDAVCalDAVBackend($authBackend); $tree = array( new \Sabre\CalDAV\Principal\Collection($principalBackend), - new SabreDAVReadOnlyCalendarRootLib($principalBackend, $calendarBackend) + new SabreDAVReadOnlyCalendarRoot($principalBackend, $calendarBackend) ); $server = new \Sabre\DAV\Server($tree); @@ -39,8 +39,6 @@ class Test extends FHC_Controller $authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend); $server->addPlugin($authPlugin); - $this->_registerReadOnlyWriteInterceptor($server); - $caldavPlugin = new \Sabre\CalDAV\Plugin(); $server->addPlugin($caldavPlugin); @@ -52,94 +50,4 @@ class Test extends FHC_Controller $server->exec(); } - - private function _registerReadOnlyWriteInterceptor($server) - { - $callback = function() use ($server) { - return $this->_discardReadOnlyWriteRequest($server, func_get_args()); - }; - - if (method_exists($server, 'on')) - { - $server->on('beforeMethod', $callback, 20); - return; - } - - if (method_exists($server, 'subscribeEvent')) - { - $server->subscribeEvent('beforeMethod', $callback, 20); - } - } - - private function _discardReadOnlyWriteRequest($server, array $eventArgs) - { - $method = $this->_getDavRequestMethod($eventArgs); - if (!in_array($method, array('PUT', 'DELETE', 'PROPPATCH', 'MKCOL', 'MOVE', 'COPY'), true)) - return true; - - $this->_sendReadOnlyWriteAcceptedResponse($server, $eventArgs); - return false; - } - - private function _getDavRequestMethod(array $eventArgs) - { - foreach ($eventArgs as $argument) - { - if (is_string($argument)) - return strtoupper($argument); - - if (is_object($argument) && method_exists($argument, 'getMethod')) - return strtoupper($argument->getMethod()); - } - - return isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : ''; - } - - private function _sendReadOnlyWriteAcceptedResponse($server, array $eventArgs) - { - $response = $this->_getDavResponse($server, $eventArgs); - - if (is_object($response)) - { - if (method_exists($response, 'setStatus')) - { - $response->setStatus(204); - if (method_exists($response, 'setHeader')) - $response->setHeader('Content-Length', '0'); - if (method_exists($response, 'setBody')) - $response->setBody(''); - return; - } - - if (method_exists($response, 'sendStatus')) - { - if (method_exists($response, 'setHeader')) - $response->setHeader('Content-Length', '0'); - $response->sendStatus(204); - return; - } - } - - if (!headers_sent()) - { - header('HTTP/1.1 204 No Content'); - header('Content-Length: 0'); - } - } - - private function _getDavResponse($server, array $eventArgs) - { - foreach ($eventArgs as $argument) - { - if (is_object($argument) && ( - method_exists($argument, 'setStatus') - || method_exists($argument, 'sendStatus') - )) - { - return $argument; - } - } - - return isset($server->httpResponse) ? $server->httpResponse : null; - } } diff --git a/application/libraries/sabredav/SabreDAVCalDAVLib.php b/application/libraries/sabredav/SabreDAVCalDAVLib.php index e79f8a6e6..0417f7002 100644 --- a/application/libraries/sabredav/SabreDAVCalDAVLib.php +++ b/application/libraries/sabredav/SabreDAVCalDAVLib.php @@ -1,971 +1,16 @@ auth = $auth; - - /* - $ma = new mitarbeiter(); - $this->result_ma = $ma->getMitarbeiter(null,null,null); - */ - } - - /** - * Liefert den eingeloggten User - */ - function getUser() - { - return $this->auth->getCurrentUser(); - } - - - /** - * Returns a list of principals based on a prefix. - * - * This prefix will often contain something like 'principals'. You are only - * expected to return principals that are in this base path. - * - * You are expected to return at least a 'uri' for every user, you can - * return any additional properties if you wish so. Common properties are: - * {DAV:}displayname - * {http://sabredav.org/ns}email-address - This is a custom SabreDAV - * field that's actualy injected in a number of other properties. If - * you have an email address, use this property. - * - * @param string $prefixPath - * @return array - */ - public function getPrincipalsByPrefix($prefixPath) - { - //$prefixPath = principals - //error_log('Principal.php/getPrincipalsByPrefix('.$prefixPath.')'); - $principals = array(); - $user = $this->getUser(); - - if($prefixPath=='principals') - { - - $principals[] = array( - 'id' => $user, - 'uri' => 'principals/'.$user, - '{DAV:}displayname' => $user, - '{http://sabredav.org/ns}email-address' => $user.'@example.com', - ); - } - - return $principals; - - } - - /** - * Returns a specific principal, specified by it's path. - * The returned structure should be the exact same as from - * getPrincipalsByPrefix. - * - * @param string $path - * @return array - */ - public function getPrincipalByPath($path) - { - $user = mb_substr($path,11); - - $result = array( - 'id' => $user, - 'uri' => 'principals/'.$user, - '{DAV:}displayname' => $user, - '{http://sabredav.org/ns}email-address' => $user.'@example.com', - ); - - return $result; - } - - /** - * Returns the list of members for a group-principal - * - * @param string $principal - * @return array - */ - public function getGroupMemberSet($principal) - { - $result = array(); - - return $result; - } - - /** - * Returns the list of groups a principal is a member of - * - * @param string $principal - * @return array - */ - public function getGroupMembership($principal) - { - $result = array(); - if(preg_match('/^principals\/[0-9A-Za-z\-]*$/',$principal)) - { - $user = mb_substr($principal,11); - } - return $result; - - } - - /** - * Updates the list of group members for a group principal. - * - * The principals should be passed as a list of uri's. - * - * @param string $principal - * @param array $members - * @return void - */ - public function setGroupMemberSet($principal, array $members) - { - throw new \Sabre\DAV\Exception('Not implemented'); - } - - public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) - { - $propPatch->setRemainingResultCode(403); - } - - public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') - { - return array(); - } - - public function findByUri($uri, $principalPrefix) - { - return null; - } -} - -/** - * CalDAV backend - */ -class SabreDAVCalDAVBackendLib extends \Sabre\CalDAV\Backend\AbstractBackend -{ - protected $auth; - - protected $CI; - - /** - * Creates the backend - * - * @param AuthBackend $auth - */ - public function __construct($auth) - { - $this->auth = $auth; - - $this->CI =& get_instance(); - } - - /** - * Liefert den eingeloggten User - */ - function getUser() - { - return $this->auth->getCurrentUser(); - } - - /** - * Returns a list of calendars for a principal. - * - * Every project is an array with the following keys: - * * id, a unique id that will be used by other functions to modify the - * calendar. This can be the same as the uri or a database key. - * * uri, which the basename of the uri with which the calendar is - * accessed. - * * principalUri. The owner of the calendar. Almost always the same as - * principalUri passed to this method. - * - * Furthermore it can contain webdav properties in clark notation. A very - * common one is '{DAV:}displayname'. - * - * @param string $principalUri - * @return array - */ - public function getCalendarsForUser($principalUri) - { - $user = mb_substr($principalUri,11); - $calendars = array(); - $calendar = array( - 'id' => $user, - 'uri' => 'LVPlan-'.$user, - 'principaluri' => 'principals/'.$user, - '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $this->buildCalendarCtag($user), - '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet(array('VEVENT')), - '{DAV:}displayname' => 'LVPlan', - '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description comes here', - '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'Europe/Vienna', - '{http://apple.com/ns/ical/}calendar-order' => '1', - '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', - '{http://sabredav.org/ns}read-only' => 1 - ); - $calendars[] = $calendar; - - return $calendars; - } - - /** - * Creates a new calendar for a principal. - * - * If the creation was a success, an id must be returned that can be used to reference - * this calendar in other methods, such as updateCalendar - * - * @param string $principalUri - * @param string $calendarUri - * @param array $properties - */ - public function createCalendar($principalUri,$calendarUri, array $properties) - { - return $calendarUri; - } - - /** - * Updates properties for a calendar. - * - * The mutations array uses the propertyName in clark-notation as key, - * and the array value for the property value. In the case a property - * should be deleted, the property value will be null. - * - * This method must be atomic. If one property cannot be changed, the - * entire operation must fail. - * - * If the operation was successful, true can be returned. - * If the operation failed, false can be returned. - * - * Deletion of a non-existant property is always succesful. - * - * Lastly, it is optional to return detailed information about any - * failures. In this case an array should be returned with the following - * structure: - * - * array( - * 403 => array( - * '{DAV:}displayname' => null, - * ), - * 424 => array( - * '{DAV:}owner' => null, - * ) - * ) - * - * In this example it was forbidden to update {DAV:}displayname. - * (403 Forbidden), which in turn also caused {DAV:}owner to fail - * (424 Failed Dependency) because the request needs to be atomic. - * - * @param string $calendarId - * @param array $mutations - * @return bool|array - */ - public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) - { - $propPatch->setRemainingResultCode(200); - } - - /** - * Delete a calendar and all it's objects - * - * @param string $calendarId - * @return void - */ - public function deleteCalendar($calendarId) - { - return null; - } - - - public function getCalendarData($userUID, $objectUri=null) - { - $this->CI->load->model("ressource/Mitarbeiter_model", "MitarbeiterModel"); - $this->CI->load->model("person/Benutzer_model", "BenutzerModel"); - - $user = $this->CI->BenutzerModel->loadWhere(array("uid" => $userUID)); - if(!$user) - die('User invalid'); - - $this->CI->load->library('KalenderLib', ['uid' => $userUID]); - - $isUserEmployeeResult = $this->CI->MitarbeiterModel->isMitarbeiter($userUID); - if (isError($isUserEmployeeResult)) { - return; - } - $isUserEmployee = getData($isUserEmployeeResult); - - $startDate = date('Y-m-d', mktime(0,0,0,date('m'),date('d')-14,date('Y'))); - $endDate = date('Y-m-d', mktime(0,0,0,date('m')+6,date('d'),date('Y'))); - - $data = []; - - if ($isUserEmployee) { - $data = $this->CI->kalenderlib->getPlanForLecturerByLecturer($startDate, $endDate, $userUID); - } else { - $data = $this->CI->kalenderlib->getPlanForStudentByStudent($startDate, $endDate, $userUID); - } - - if(!is_array($data)) - $data = array(); - - foreach($data as $item) - { - $item->calendarDataFragment = $this->buildCalendarDataFragment($item); - } - - if (is_null($objectUri)) - { - return $data; - } - - $normalizedObjectUri = $this->normalizeObjectUri($objectUri); - foreach($data as $row) - { - if(md5($row->eindeutige_gruppen_id) == $normalizedObjectUri) - { - return $row; - } - } - - return null; - } - - public function makeCal($event) - { - return $this->buildICalLine('BEGIN', 'VCALENDAR') - .$this->buildICalLine('VERSION', '2.0') - .$this->buildICalTextLine('PRODID', 'FH Technikum Wien') - .$this->buildICalLine('BEGIN', 'VTIMEZONE') - .$this->buildICalLine('TZID', 'Europe/Vienna') - .$this->buildICalLine('BEGIN', 'DAYLIGHT') - .$this->buildICalLine('TZOFFSETFROM', '+0100') - .$this->buildICalLine('RRULE', 'FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU') - .$this->buildICalLine('DTSTART', '19810329T020000') - .$this->buildICalLine('TZNAME', 'GMT+02:00') - .$this->buildICalLine('TZOFFSETTO', '+0200') - .$this->buildICalLine('END', 'DAYLIGHT') - .$this->buildICalLine('BEGIN', 'STANDARD') - .$this->buildICalLine('TZOFFSETFROM', '+0200') - .$this->buildICalLine('RRULE', 'FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU') - .$this->buildICalLine('DTSTART', '19961027T030000') - .$this->buildICalLine('TZNAME', 'GMT+01:00') - .$this->buildICalLine('TZOFFSETTO', '+0100') - .$this->buildICalLine('END', 'STANDARD') - .$this->buildICalLine('END', 'VTIMEZONE') - .$event - .$this->buildICalLine('END', 'VCALENDAR'); - } - - protected function buildCalendarDataFragment($item) - { - $summary = $item->type == 'reservierung' ? $item->titel : $item->topic; - - $description = ""; - if ($item->type == 'reservierung') { - $description = $item->beschreibung; - } else { - $description = $item->lehrfach_bez . "\r\n"; - - if (isset($item->lektor) && is_array($item->lektor) && count($item->lektor) > 0) { - $description .= "Lehrer: " . join(", ", array_map(function($teacher) { return $teacher["kurzbz"]; }, $item->lektor)) . "\r\n"; - } - } - - $parsedStartDate = $this->formatICalLocalDateTime($item->isostart); - $parsedEndDate = $this->formatICalLocalDateTime($item->isoend); - $lastModified = $this->formatICalUtcDateTime(isset($item->updateamum) ? $item->updateamum : null); - $dtStamp = $lastModified ?: $this->formatICalUtcDateTime($item->isostart); - - if($dtStamp === null) - $dtStamp = '19700101T000000Z'; - - $fragment = $this->buildICalLine('BEGIN', 'VEVENT') - .$this->buildICalTextLine('UID', $item->eindeutige_gruppen_id) - .$this->buildICalLine('SEQUENCE', (int)$item->kalender_id) - .$this->buildICalTextLine('SUMMARY', $summary) - .$this->buildICalTextLine('DESCRIPTION', 'TODO') - .$this->buildICalTextLine('LOCATION', isset($item->ort_kurzbz) ? $item->ort_kurzbz : '') - .$this->buildICalTextLine('CATEGORIES', 'Stundenplans') - .$this->buildICalLine('DTSTART', $parsedStartDate, array('TZID' => 'Europe/Vienna')) - .$this->buildICalLine('DTEND', $parsedEndDate, array('TZID' => 'Europe/Vienna')); - - if($lastModified !== null) - $fragment .= $this->buildICalLine('LAST-MODIFIED', $lastModified); - - return $fragment - .$this->buildICalLine('DTSTAMP', $dtStamp) - .$this->buildICalLine('END', 'VEVENT'); - } - - protected function buildCalendarCtag($userUID) - { - $data = $this->getCalendarData($userUID); - if(!is_array($data)) - $data = array(); - - $fragments = array(); - foreach($data as $row) - { - $lastModified = isset($row->updateamum) ? $this->getLastModifiedTimestamp($row->updateamum) : null; - $calendarData = isset($row->calendarDataFragment) ? $this->makeCal($row->calendarDataFragment) : ''; - $fragments[] = $this->getObjectUri($row).':'.$lastModified.':'.md5($calendarData); - } - - sort($fragments, SORT_STRING); - return 'LVPlan-'.$userUID.'-'.md5(implode('|', $fragments)); - } - - protected function buildICalTextLine($name, $value, array $parameters = array()) - { - return $this->buildICalLine($name, $this->escapeICalText($value), $parameters); - } - - protected function buildICalLine($name, $value, array $parameters = array()) - { - $line = strtoupper($name); - - foreach($parameters as $parameterName => $parameterValue) - { - $parameterValues = is_array($parameterValue) ? $parameterValue : array($parameterValue); - $escapedValues = array(); - - foreach($parameterValues as $singleParameterValue) - $escapedValues[] = $this->escapeICalParameterValue($singleParameterValue); - - $line .= ';'.strtoupper($parameterName).'='.implode(',', $escapedValues); - } - - return $this->foldICalLine($line.':'.(string)$value); - } - - protected function foldICalLine($line) - { - $line = (string)$line; - $folded = ''; - - while(strlen($line) > 75) - { - $part = mb_strcut($line, 0, 75, 'UTF-8'); - $folded .= $part."\r\n"; - $line = ' '.mb_strcut($line, strlen($part), strlen($line) - strlen($part), 'UTF-8'); - } - - return $folded.$line."\r\n"; - } - - protected function escapeICalText($value) - { - $value = (string)$value; - $value = str_replace('\\', '\\\\', $value); - $value = str_replace(array("\r\n", "\r", "\n"), '\n', $value); - return str_replace(array(';', ','), array('\;', '\,'), $value); - } - - protected function escapeICalParameterValue($value) - { - $value = (string)$value; - $value = str_replace(array('\\', '"', "\r", "\n"), array('\\\\', '\"', '', ''), $value); - - if(preg_match('/[;:,]/', $value)) - return '"'.$value.'"'; - - return $value; - } - - protected function formatICalLocalDateTime($dateTime) - { - $dateTime = $this->createDateTime($dateTime); - if($dateTime === null) - return null; - - $dateTime->setTimezone(new DateTimeZone('Europe/Vienna')); - return $dateTime->format('Ymd\THis'); - } - - protected function formatICalUtcDateTime($dateTime) - { - $dateTime = $this->createDateTime($dateTime); - if($dateTime === null) - return null; - - $dateTime->setTimezone(new DateTimeZone('UTC')); - return $dateTime->format('Ymd\THis\Z'); - } - - protected function createDateTime($dateTime) - { - if($dateTime instanceof DateTime) - return clone $dateTime; - - if($dateTime === null || $dateTime === '') - return null; - - try - { - if(is_numeric($dateTime)) - return new DateTime('@'.(int)$dateTime); - - return new DateTime($dateTime); - } - catch(Exception $e) - { - return null; - } - } - - protected function getObjectUri($row) - { - return md5($row->eindeutige_gruppen_id).'.ics'; - } - - protected function normalizeObjectUri($objectUri) - { - $normalizedObjectUri = (string)$objectUri; - - if(substr($normalizedObjectUri, -4) === '.ics') - $normalizedObjectUri = substr($normalizedObjectUri, 0, -4); - - if(mb_strpos($normalizedObjectUri, '@') !== false) - $normalizedObjectUri = mb_substr($normalizedObjectUri, mb_strpos($normalizedObjectUri, '@') + 1); - - if($this->hasObjectUriDatePrefix($normalizedObjectUri)) - return mb_substr($normalizedObjectUri, mb_strpos($normalizedObjectUri, '-') + 1); - - return $normalizedObjectUri; - } - - protected function hasObjectUriDatePrefix($objectUri) - { - $hyphenPosition = mb_strpos($objectUri, '-'); - if($hyphenPosition !== 15) - return false; - - return preg_match('/^\d{8}T\d{6}$/', mb_substr($objectUri, 0, 15)) === 1; - } - - protected function buildCalendarObject($row, $calendarId) - { - $calendarData = $this->makeCal($row->calendarDataFragment); - - return array( - "id"=>$row->eindeutige_gruppen_id, - "calendardata"=>$calendarData, - "uri"=>$this->getObjectUri($row), - "lastmodified"=>$this->getLastModifiedTimestamp($row->updateamum), - "etag"=>'"'.md5($calendarData).'"', - "calendarid"=>$calendarId, - "size"=>strlen($calendarData), - "component"=>'vevent' - ); - } - - protected function getLastModifiedTimestamp($lastModified) - { - if($lastModified === null || $lastModified === '') - return null; - - if(is_numeric($lastModified)) - return (int)$lastModified; - - $timestamp = strtotime($lastModified); - if($timestamp === false) - return null; - - return $timestamp; - } - - /** - * Returns all calendar objects within a calendar. - * - * Every item contains an array with the following keys: - * * id - unique identifier which will be used for subsequent updates - * * calendardata - The iCalendar-compatible calnedar data - * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. - * * lastmodified - a timestamp of the last modification time - * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: - * ' "abcdef"') - * * calendarid - The calendarid as it was passed to this function. - * - * Note that the etag is optional, but it's highly encouraged to return for - * speed reasons. - * - * The calendardata is also optional. If it's not returned - * 'getCalendarObject' will be called later, which *is* expected to return - * calendardata. - * - * @param string $calendarId - * @return array - */ - public function getCalendarObjects($calendarId) - { - //$user = $this->getUser(); - $user = $calendarId; - $data = $this->getCalendarData($user); - - //error_log("Caldav_Backend.php/getCalendarObjects($calendarId) "); - $return = array(); - - if(!is_array($data)) - return $return; - - foreach($data as $row) - { - $return[] = $this->buildCalendarObject($row, $calendarId); - } - return $return; - } - - /** - * Returns information from a single calendar object, based on it's object - * uri. - * - * The returned array must have the same keys as getCalendarObjects. The - * 'calendardata' object is required here though, while it's not required - * for getCalendarObjects. - * - * @param string $calendarId - * @param string $objectUri - * @return array - */ - public function getCalendarObject($calendarId,$objectUri) - { - $user = $calendarId; - $data = $this->getCalendarData($user,$objectUri); - - if(empty($data)) - { - return null; - } - elseif(is_object($data)) - { - $ret = $this->buildCalendarObject($data, $calendarId); - } - - return isset($ret) ? $ret : null; - } - - /** - * Creates a new calendar object. - * - * @param string $calendarId - * @param string $objectUri - * @param string $calendarData - * @return void - */ - public function createCalendarObject($calendarId,$objectUri,$calendarData) - { - return null; - } - - /** - * Updates an existing calendarobject, based on it's uri. - * - * @param string $calendarId - * @param string $objectUri - * @param string $calendarData - * @return void - */ - public function updateCalendarObject($calendarId,$objectUri,$calendarData) - { - if(is_resource($calendarData)) - $calendarData = stream_get_contents($calendarData); - - return '"'.md5((string)$calendarData).'"'; - } - - /** - * Deletes an existing calendar object. - * - * @param string $calendarId - * @param string $objectUri - * @return void - */ - public function deleteCalendarObject($calendarId,$objectUri) - { - return null; - } -} - -/** - * Read-only CalDAV node wrappers for DB-owned calendars. - */ -class SabreDAVReadOnlyACLLib -{ - public static function getAcl($principalUri, $includeFreeBusy = false) - { - $acl = array( - array( - 'privilege' => '{DAV:}read', - 'principal' => $principalUri, - 'protected' => true, - ), - array( - 'privilege' => '{DAV:}read', - 'principal' => $principalUri.'/calendar-proxy-read', - 'protected' => true, - ), - ); - - if($includeFreeBusy) - { - $acl[] = array( - 'privilege' => '{'.\Sabre\CalDAV\Plugin::NS_CALDAV.'}read-free-busy', - 'principal' => '{DAV:}authenticated', - 'protected' => true, - ); - } - - return $acl; - } - - public static function ignoreWrite() - { - return null; - } -} - -class SabreDAVReadOnlyCalendarRootLib extends \Sabre\CalDAV\CalendarRoot -{ - protected $readOnlyCaldavBackend; - - public function __construct(\Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend, \Sabre\CalDAV\Backend\BackendInterface $caldavBackend, $principalPrefix = 'principals') - { - parent::__construct($principalBackend, $caldavBackend, $principalPrefix); - $this->readOnlyCaldavBackend = $caldavBackend; - } - - public function getChildForPrincipal(array $principal) - { - return new SabreDAVReadOnlyCalendarHomeLib($this->readOnlyCaldavBackend, $principal); - } -} - -class SabreDAVReadOnlyCalendarHomeLib extends \Sabre\CalDAV\CalendarHome -{ - protected $readOnlyCaldavBackend; - protected $readOnlyPrincipalInfo; - - public function __construct(\Sabre\CalDAV\Backend\BackendInterface $caldavBackend, $principalInfo) - { - parent::__construct($caldavBackend, $principalInfo); - $this->readOnlyCaldavBackend = $caldavBackend; - $this->readOnlyPrincipalInfo = $principalInfo; - } - - public function getChildren() - { - $calendars = $this->readOnlyCaldavBackend->getCalendarsForUser($this->readOnlyPrincipalInfo['uri']); - $objs = array(); - - foreach($calendars as $calendar) - { - $objs[] = new SabreDAVReadOnlyCalendarLib($this->readOnlyCaldavBackend, $calendar); - } - - return $objs; - } - - public function createExtendedCollection($name, \Sabre\DAV\MkCol $mkCol) - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function createFile($filename, $data = null) - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function createDirectory($filename) - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function delete() - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function setName($name) - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function getACL() - { - return SabreDAVReadOnlyACLLib::getAcl($this->readOnlyPrincipalInfo['uri']); - } -} - -class SabreDAVReadOnlyCalendarLib extends \Sabre\CalDAV\Calendar -{ - protected $readOnlyCaldavBackend; - protected $readOnlyCalendarInfo; - - public function __construct(\Sabre\CalDAV\Backend\BackendInterface $caldavBackend, $calendarInfo) - { - parent::__construct($caldavBackend, $calendarInfo); - $this->readOnlyCaldavBackend = $caldavBackend; - $this->readOnlyCalendarInfo = $calendarInfo; - } - - public function getChild($name) - { - $obj = $this->readOnlyCaldavBackend->getCalendarObject($this->readOnlyCalendarInfo['id'], $name); - - if(!$obj) - throw new \Sabre\DAV\Exception\NotFound('Calendar object not found'); - - $obj['acl'] = $this->getCalendarObjectAcl(); - - return new SabreDAVReadOnlyCalendarObjectLib($this->readOnlyCaldavBackend, $this->readOnlyCalendarInfo, $obj); - } - - public function getChildren() - { - $objs = $this->readOnlyCaldavBackend->getCalendarObjects($this->readOnlyCalendarInfo['id']); - $children = array(); - - foreach($objs as $obj) - { - $obj['acl'] = $this->getCalendarObjectAcl(); - $children[] = new SabreDAVReadOnlyCalendarObjectLib($this->readOnlyCaldavBackend, $this->readOnlyCalendarInfo, $obj); - } - - return $children; - } - - public function createFile($name, $calendarData = null) - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function createDirectory($name) - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function delete() - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function setName($newName) - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function updateProperties($mutations) - { - return true; - } - - public function propPatch(\Sabre\DAV\PropPatch $propPatch) - { - $propPatch->setRemainingResultCode(200); - } - - public function getACL() - { - return SabreDAVReadOnlyACLLib::getAcl($this->getOwner(), true); - } - - protected function getCalendarObjectAcl() - { - return SabreDAVReadOnlyACLLib::getAcl($this->getOwner()); - } -} - -class SabreDAVReadOnlyCalendarObjectLib extends \Sabre\CalDAV\CalendarObject -{ - protected $readOnlyCalendarInfo; - - public function __construct(\Sabre\CalDAV\Backend\BackendInterface $caldavBackend, array $calendarInfo, array $objectData) - { - parent::__construct($caldavBackend, $calendarInfo, $objectData); - $this->readOnlyCalendarInfo = $calendarInfo; - } - - public function put($calendarData) - { - return $this->getETag(); - } - - public function getETag() - { - if(isset($_SERVER['REQUEST_METHOD']) && in_array($_SERVER['REQUEST_METHOD'], array('PUT', 'DELETE'), true)) - { - $ifMatch = isset($_SERVER['HTTP_IF_MATCH']) ? trim($_SERVER['HTTP_IF_MATCH']) : ''; - if($ifMatch !== '' && $ifMatch !== '*') - { - $requestedEtags = explode(',', $ifMatch); - foreach($requestedEtags as $requestedEtag) - { - $requestedEtag = trim($requestedEtag); - if($requestedEtag !== '') - return $requestedEtag; - } - } - } - - return parent::getETag(); - } - - public function delete() - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function setName($name) - { - return SabreDAVReadOnlyACLLib::ignoreWrite(); - } - - public function getACL() - { - return SabreDAVReadOnlyACLLib::getAcl($this->readOnlyCalendarInfo['principaluri']); - } -} diff --git a/application/libraries/sabredav/caldav/SabreDAVACLPrincipalBackend.php b/application/libraries/sabredav/caldav/SabreDAVACLPrincipalBackend.php new file mode 100644 index 000000000..822955c8d --- /dev/null +++ b/application/libraries/sabredav/caldav/SabreDAVACLPrincipalBackend.php @@ -0,0 +1,179 @@ +auth = $auth; + + /* + $ma = new mitarbeiter(); + $this->result_ma = $ma->getMitarbeiter(null,null,null); + */ + } + + /** + * Liefert den eingeloggten User + */ + function getUser() + { + return $this->auth->getCurrentUser(); + } + + + /** + * Returns a list of principals based on a prefix. + * + * This prefix will often contain something like 'principals'. You are only + * expected to return principals that are in this base path. + * + * You are expected to return at least a 'uri' for every user, you can + * return any additional properties if you wish so. Common properties are: + * {DAV:}displayname + * {http://sabredav.org/ns}email-address - This is a custom SabreDAV + * field that's actualy injected in a number of other properties. If + * you have an email address, use this property. + * + * @param string $prefixPath + * @return array + */ + public function getPrincipalsByPrefix($prefixPath) + { + //$prefixPath = principals + //error_log('Principal.php/getPrincipalsByPrefix('.$prefixPath.')'); + $principals = array(); + $user = $this->getUser(); + + if($prefixPath=='principals') + { + + $principals[] = array( + 'id' => $user, + 'uri' => 'principals/'.$user, + '{DAV:}displayname' => $user, + '{http://sabredav.org/ns}email-address' => $user.'@example.com', + ); + } + + return $principals; + + } + + /** + * Returns a specific principal, specified by it's path. + * The returned structure should be the exact same as from + * getPrincipalsByPrefix. + * + * @param string $path + * @return array + */ + public function getPrincipalByPath($path) + { + $user = mb_substr($path,11); + + $result = array( + 'id' => $user, + 'uri' => 'principals/'.$user, + '{DAV:}displayname' => $user, + '{http://sabredav.org/ns}email-address' => $user.'@example.com', + ); + + return $result; + } + + /** + * Returns the list of members for a group-principal + * + * @param string $principal + * @return array + */ + public function getGroupMemberSet($principal) + { + $result = array(); + + return $result; + } + + /** + * Returns the list of groups a principal is a member of + * + * @param string $principal + * @return array + */ + public function getGroupMembership($principal) + { + $result = array(); + if(preg_match('/^principals\/[0-9A-Za-z\-]*$/',$principal)) + { + $user = mb_substr($principal,11); + } + return $result; + + } + + /** + * Updates the list of group members for a group principal. + * + * The principals should be passed as a list of uri's. + * + * @param string $principal + * @param array $members + * @return void + */ + public function setGroupMemberSet($principal, array $members) + { + throw new \Sabre\DAV\Exception('Not implemented'); + } + + public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) + { + $propPatch->setRemainingResultCode(403); + } + + public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') + { + return array(); + } + + public function findByUri($uri, $principalPrefix) + { + return null; + } +} diff --git a/application/libraries/sabredav/caldav/SabreDAVCalDAVBackend.php b/application/libraries/sabredav/caldav/SabreDAVCalDAVBackend.php new file mode 100644 index 000000000..2bfa12804 --- /dev/null +++ b/application/libraries/sabredav/caldav/SabreDAVCalDAVBackend.php @@ -0,0 +1,546 @@ +auth = $auth; + + $this->CI =& get_instance(); + } + + /** + * Liefert den eingeloggten User + */ + function getUser() + { + return $this->auth->getCurrentUser(); + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principalUri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + public function getCalendarsForUser($principalUri) + { + $user = mb_substr($principalUri,11); + $calendars = array(); + $calendar = array( + 'id' => $user, + 'uri' => 'LVPlan-'.$user, + 'principaluri' => 'principals/'.$user, + '{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $this->buildCalendarCtag($user), + '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet(array('VEVENT')), + '{DAV:}displayname' => 'LVPlan', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description comes here', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'Europe/Vienna', + '{http://apple.com/ns/ical/}calendar-order' => '1', + '{http://apple.com/ns/ical/}calendar-color' => '#FF0000', + '{http://sabredav.org/ns}read-only' => 1 + ); + $calendars[] = $calendar; + + return $calendars; + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this calendar in other methods, such as updateCalendar + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + */ + public function createCalendar($principalUri,$calendarUri, array $properties) + { + return $calendarUri; + } + + /** + * Updates properties for a calendar. + * + * The mutations array uses the propertyName in clark-notation as key, + * and the array value for the property value. In the case a property + * should be deleted, the property value will be null. + * + * This method must be atomic. If one property cannot be changed, the + * entire operation must fail. + * + * If the operation was successful, true can be returned. + * If the operation failed, false can be returned. + * + * Deletion of a non-existant property is always succesful. + * + * Lastly, it is optional to return detailed information about any + * failures. In this case an array should be returned with the following + * structure: + * + * array( + * 403 => array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param string $calendarId + * @param array $mutations + * @return bool|array + */ + public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) + { + $propPatch->setRemainingResultCode(200); + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + public function deleteCalendar($calendarId) + { + return null; + } + + + public function getCalendarData($userUID, $objectUri=null) + { + $this->CI->load->model("ressource/Mitarbeiter_model", "MitarbeiterModel"); + $this->CI->load->model("person/Benutzer_model", "BenutzerModel"); + + $user = $this->CI->BenutzerModel->loadWhere(array("uid" => $userUID)); + if(!$user) + die('User invalid'); + + $this->CI->load->library('KalenderLib', ['uid' => $userUID]); + + $isUserEmployeeResult = $this->CI->MitarbeiterModel->isMitarbeiter($userUID); + if (isError($isUserEmployeeResult)) { + return; + } + $isUserEmployee = getData($isUserEmployeeResult); + + $startDate = date('Y-m-d', mktime(0,0,0,date('m'),date('d')-14,date('Y'))); + $endDate = date('Y-m-d', mktime(0,0,0,date('m')+6,date('d'),date('Y'))); + + $data = []; + + if ($isUserEmployee) { + $data = $this->CI->kalenderlib->getPlanForLecturerByLecturer($startDate, $endDate, $userUID); + } else { + $data = $this->CI->kalenderlib->getPlanForStudentByStudent($startDate, $endDate, $userUID); + } + + if(!is_array($data)) + $data = array(); + + foreach($data as $item) + { + $item->calendarDataFragment = $this->buildCalendarDataFragment($item); + } + + if (is_null($objectUri)) + { + return $data; + } + + $normalizedObjectUri = $this->normalizeObjectUri($objectUri); + foreach($data as $row) + { + if(md5($row->eindeutige_gruppen_id) == $normalizedObjectUri) + { + return $row; + } + } + + return null; + } + + public function makeCal($event) + { + return $this->buildICalLine('BEGIN', 'VCALENDAR') + .$this->buildICalLine('VERSION', '2.0') + .$this->buildICalTextLine('PRODID', 'FH Technikum Wien') + .$this->buildICalLine('BEGIN', 'VTIMEZONE') + .$this->buildICalLine('TZID', 'Europe/Vienna') + .$this->buildICalLine('BEGIN', 'DAYLIGHT') + .$this->buildICalLine('TZOFFSETFROM', '+0100') + .$this->buildICalLine('RRULE', 'FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU') + .$this->buildICalLine('DTSTART', '19810329T020000') + .$this->buildICalLine('TZNAME', 'GMT+02:00') + .$this->buildICalLine('TZOFFSETTO', '+0200') + .$this->buildICalLine('END', 'DAYLIGHT') + .$this->buildICalLine('BEGIN', 'STANDARD') + .$this->buildICalLine('TZOFFSETFROM', '+0200') + .$this->buildICalLine('RRULE', 'FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU') + .$this->buildICalLine('DTSTART', '19961027T030000') + .$this->buildICalLine('TZNAME', 'GMT+01:00') + .$this->buildICalLine('TZOFFSETTO', '+0100') + .$this->buildICalLine('END', 'STANDARD') + .$this->buildICalLine('END', 'VTIMEZONE') + .$event + .$this->buildICalLine('END', 'VCALENDAR'); + } + + protected function buildCalendarDataFragment($item) + { + $summary = $item->type == 'reservierung' ? $item->titel : $item->topic; + + $description = ""; + if ($item->type == 'reservierung') { + $description = $item->beschreibung; + } else { + $description = $item->lehrfach_bez . "\r\n"; + + if (isset($item->lektor) && is_array($item->lektor) && count($item->lektor) > 0) { + $description .= "Lehrer: " . join(", ", array_map(function($teacher) { return $teacher["kurzbz"]; }, $item->lektor)) . "\r\n"; + } + } + + $parsedStartDate = $this->formatICalLocalDateTime($item->isostart); + $parsedEndDate = $this->formatICalLocalDateTime($item->isoend); + $lastModified = $this->formatICalUtcDateTime(isset($item->updateamum) ? $item->updateamum : null); + $dtStamp = $lastModified ?: $this->formatICalUtcDateTime($item->isostart); + + if($dtStamp === null) + $dtStamp = '19700101T000000Z'; + + $fragment = $this->buildICalLine('BEGIN', 'VEVENT') + .$this->buildICalTextLine('UID', $item->eindeutige_gruppen_id) + .$this->buildICalLine('SEQUENCE', (int)$item->kalender_id) + .$this->buildICalTextLine('SUMMARY', $summary) + .$this->buildICalTextLine('DESCRIPTION', $description) + .$this->buildICalTextLine('LOCATION', isset($item->ort_kurzbz) ? $item->ort_kurzbz : '') + .$this->buildICalTextLine('CATEGORIES', 'Stundenplans') + .$this->buildICalLine('DTSTART', $parsedStartDate, array('TZID' => 'Europe/Vienna')) + .$this->buildICalLine('DTEND', $parsedEndDate, array('TZID' => 'Europe/Vienna')); + + if($lastModified !== null) + $fragment .= $this->buildICalLine('LAST-MODIFIED', $lastModified); + + return $fragment + .$this->buildICalLine('DTSTAMP', $dtStamp) + .$this->buildICalLine('END', 'VEVENT'); + } + + protected function buildCalendarCtag($userUID) + { + $data = $this->getCalendarData($userUID); + if(!is_array($data)) + $data = array(); + + $fragments = array(); + foreach($data as $row) + { + $lastModified = isset($row->updateamum) ? $this->getLastModifiedTimestamp($row->updateamum) : null; + $calendarData = isset($row->calendarDataFragment) ? $this->makeCal($row->calendarDataFragment) : ''; + $fragments[] = $this->getObjectUri($row).':'.$lastModified.':'.md5($calendarData); + } + + sort($fragments, SORT_STRING); + return 'LVPlan-'.$userUID.'-'.md5(implode('|', $fragments)); + } + + protected function buildICalTextLine($name, $value, array $parameters = array()) + { + return $this->buildICalLine($name, $this->escapeICalText($value), $parameters); + } + + protected function buildICalLine($name, $value, array $parameters = array()) + { + $line = strtoupper($name); + + foreach($parameters as $parameterName => $parameterValue) + { + $parameterValues = is_array($parameterValue) ? $parameterValue : array($parameterValue); + $escapedValues = array(); + + foreach($parameterValues as $singleParameterValue) + $escapedValues[] = $this->escapeICalParameterValue($singleParameterValue); + + $line .= ';'.strtoupper($parameterName).'='.implode(',', $escapedValues); + } + + return $this->foldICalLine($line.':'.(string)$value); + } + + protected function foldICalLine($line) + { + $line = (string)$line; + $folded = ''; + + while(strlen($line) > 75) + { + $part = mb_strcut($line, 0, 75, 'UTF-8'); + $folded .= $part."\r\n"; + $line = ' '.mb_strcut($line, strlen($part), strlen($line) - strlen($part), 'UTF-8'); + } + + return $folded.$line."\r\n"; + } + + protected function escapeICalText($value) + { + $value = (string)$value; + $value = str_replace('\\', '\\\\', $value); + $value = str_replace(array("\r\n", "\r", "\n"), '\n', $value); + return str_replace(array(';', ','), array('\;', '\,'), $value); + } + + protected function escapeICalParameterValue($value) + { + $value = (string)$value; + $value = str_replace(array('\\', '"', "\r", "\n"), array('\\\\', '\"', '', ''), $value); + + if(preg_match('/[;:,]/', $value)) + return '"'.$value.'"'; + + return $value; + } + + protected function formatICalLocalDateTime($dateTime) + { + $dateTime = $this->createDateTime($dateTime); + if($dateTime === null) + return null; + + $dateTime->setTimezone(new DateTimeZone('Europe/Vienna')); + return $dateTime->format('Ymd\THis'); + } + + protected function formatICalUtcDateTime($dateTime) + { + $dateTime = $this->createDateTime($dateTime); + if($dateTime === null) + return null; + + $dateTime->setTimezone(new DateTimeZone('UTC')); + return $dateTime->format('Ymd\THis\Z'); + } + + protected function createDateTime($dateTime) + { + if($dateTime instanceof DateTime) + return clone $dateTime; + + if($dateTime === null || $dateTime === '') + return null; + + try + { + if(is_numeric($dateTime)) + return new DateTime('@'.(int)$dateTime); + + return new DateTime($dateTime); + } + catch(Exception $e) + { + return null; + } + } + + protected function getObjectUri($row) + { + return md5($row->eindeutige_gruppen_id).'.ics'; + } + + protected function normalizeObjectUri($objectUri) + { + $normalizedObjectUri = (string)$objectUri; + + if(substr($normalizedObjectUri, -4) === '.ics') + $normalizedObjectUri = substr($normalizedObjectUri, 0, -4); + + if(mb_strpos($normalizedObjectUri, '@') !== false) + $normalizedObjectUri = mb_substr($normalizedObjectUri, mb_strpos($normalizedObjectUri, '@') + 1); + + if($this->hasObjectUriDatePrefix($normalizedObjectUri)) + return mb_substr($normalizedObjectUri, mb_strpos($normalizedObjectUri, '-') + 1); + + return $normalizedObjectUri; + } + + protected function hasObjectUriDatePrefix($objectUri) + { + $hyphenPosition = mb_strpos($objectUri, '-'); + if($hyphenPosition !== 15) + return false; + + return preg_match('/^\d{8}T\d{6}$/', mb_substr($objectUri, 0, 15)) === 1; + } + + protected function buildCalendarObject($row, $calendarId) + { + $calendarData = $this->makeCal($row->calendarDataFragment); + + return array( + "id"=>$row->eindeutige_gruppen_id, + "calendardata"=>$calendarData, + "uri"=>$this->getObjectUri($row), + "lastmodified"=>$this->getLastModifiedTimestamp($row->updateamum), + "etag"=>'"'.md5($calendarData).'"', + "calendarid"=>$calendarId, + "size"=>strlen($calendarData), + "component"=>'vevent' + ); + } + + protected function getLastModifiedTimestamp($lastModified) + { + if($lastModified === null || $lastModified === '') + return null; + + if(is_numeric($lastModified)) + return (int)$lastModified; + + $timestamp = strtotime($lastModified); + if($timestamp === false) + return null; + + return $timestamp; + } + + /** + * Returns all calendar objects within a calendar. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calnedar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * calendarid - The calendarid as it was passed to this function. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * @param string $calendarId + * @return array + */ + public function getCalendarObjects($calendarId) + { + //$user = $this->getUser(); + $user = $calendarId; + $data = $this->getCalendarData($user); + + //error_log("Caldav_Backend.php/getCalendarObjects($calendarId) "); + $return = array(); + + if(!is_array($data)) + return $return; + + foreach($data as $row) + { + $return[] = $this->buildCalendarObject($row, $calendarId); + } + return $return; + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * @param string $calendarId + * @param string $objectUri + * @return array + */ + public function getCalendarObject($calendarId,$objectUri) + { + $user = $calendarId; + $data = $this->getCalendarData($user,$objectUri); + + if(empty($data)) + { + return null; + } + elseif(is_object($data)) + { + $ret = $this->buildCalendarObject($data, $calendarId); + } + + return isset($ret) ? $ret : null; + } + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + public function createCalendarObject($calendarId,$objectUri,$calendarData) + { + return null; + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + public function updateCalendarObject($calendarId,$objectUri,$calendarData) + { + if(is_resource($calendarData)) + $calendarData = stream_get_contents($calendarData); + + return '"'.md5((string)$calendarData).'"'; + } + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + public function deleteCalendarObject($calendarId,$objectUri) + { + return null; + } +} diff --git a/application/libraries/sabredav/caldav/SabreDAVReadOnlyACL.php b/application/libraries/sabredav/caldav/SabreDAVReadOnlyACL.php new file mode 100644 index 000000000..5407631d5 --- /dev/null +++ b/application/libraries/sabredav/caldav/SabreDAVReadOnlyACL.php @@ -0,0 +1,36 @@ + '{DAV:}read', + 'principal' => $principalUri, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $principalUri.'/calendar-proxy-read', + 'protected' => true, + ), + ); + + if($includeFreeBusy) + { + $acl[] = array( + 'privilege' => '{'.\Sabre\CalDAV\Plugin::NS_CALDAV.'}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ); + } + + return $acl; + } + + public static function ignoreWrite() + { + return null; + } +} diff --git a/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendar.php b/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendar.php new file mode 100644 index 000000000..37f5ff8ae --- /dev/null +++ b/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendar.php @@ -0,0 +1,80 @@ +readOnlyCaldavBackend = $caldavBackend; + $this->readOnlyCalendarInfo = $calendarInfo; + } + + public function getChild($name) + { + $obj = $this->readOnlyCaldavBackend->getCalendarObject($this->readOnlyCalendarInfo['id'], $name); + + if(!$obj) + throw new \Sabre\DAV\Exception\NotFound('Calendar object not found'); + + $obj['acl'] = $this->getCalendarObjectAcl(); + + return new SabreDAVReadOnlyCalendarObject($this->readOnlyCaldavBackend, $this->readOnlyCalendarInfo, $obj); + } + + public function getChildren() + { + $objs = $this->readOnlyCaldavBackend->getCalendarObjects($this->readOnlyCalendarInfo['id']); + $children = array(); + + foreach($objs as $obj) + { + $obj['acl'] = $this->getCalendarObjectAcl(); + $children[] = new SabreDAVReadOnlyCalendarObject($this->readOnlyCaldavBackend, $this->readOnlyCalendarInfo, $obj); + } + + return $children; + } + + public function createFile($name, $calendarData = null) + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function createDirectory($name) + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function delete() + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function setName($newName) + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function updateProperties($mutations) + { + return true; + } + + public function propPatch(\Sabre\DAV\PropPatch $propPatch) + { + $propPatch->setRemainingResultCode(200); + } + + public function getACL() + { + return SabreDAVReadOnlyACL::getAcl($this->getOwner(), true); + } + + protected function getCalendarObjectAcl() + { + return SabreDAVReadOnlyACL::getAcl($this->getOwner()); + } +} diff --git a/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendarHome.php b/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendarHome.php new file mode 100644 index 000000000..2c0933b5b --- /dev/null +++ b/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendarHome.php @@ -0,0 +1,57 @@ +readOnlyCaldavBackend = $caldavBackend; + $this->readOnlyPrincipalInfo = $principalInfo; + } + + public function getChildren() + { + $calendars = $this->readOnlyCaldavBackend->getCalendarsForUser($this->readOnlyPrincipalInfo['uri']); + $objs = array(); + + foreach($calendars as $calendar) + { + $objs[] = new SabreDAVReadOnlyCalendar($this->readOnlyCaldavBackend, $calendar); + } + + return $objs; + } + + public function createExtendedCollection($name, \Sabre\DAV\MkCol $mkCol) + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function createFile($filename, $data = null) + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function createDirectory($filename) + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function delete() + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function setName($name) + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function getACL() + { + return SabreDAVReadOnlyACL::getAcl($this->readOnlyPrincipalInfo['uri']); + } +} diff --git a/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendarObject.php b/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendarObject.php new file mode 100644 index 000000000..4b15c20c3 --- /dev/null +++ b/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendarObject.php @@ -0,0 +1,84 @@ +readOnlyCalendarInfo = $calendarInfo; + } + + public function put($calendarData) + { + return $this->getCurrentETag(); + } + + public function getETag() + { + if($this->isWriteRequest()) + { + $ifMatch = isset($_SERVER['HTTP_IF_MATCH']) ? trim($_SERVER['HTTP_IF_MATCH']) : ''; + if($ifMatch !== '' && $ifMatch !== '*') + { + $requestedEtags = explode(',', $ifMatch); + foreach($requestedEtags as $requestedEtag) + { + $requestedEtag = trim($requestedEtag); + if($requestedEtag !== '') + return $requestedEtag; + } + } + } + + return parent::getETag(); + } + + public function getLastModified() + { + if($this->isWriteRequest()) + { + $ifUnmodifiedSince = isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) ? trim($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) : ''; + if($ifUnmodifiedSince !== '') + { + $timestamp = strtotime($ifUnmodifiedSince); + if($timestamp !== false) + return max(0, $timestamp - 1); + } + } + + return parent::getLastModified(); + } + + public function delete() + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function setName($name) + { + return SabreDAVReadOnlyACL::ignoreWrite(); + } + + public function getACL() + { + return SabreDAVReadOnlyACL::getAcl($this->readOnlyCalendarInfo['principaluri']); + } + + protected function isWriteRequest() + { + if(!isset($_SERVER['REQUEST_METHOD'])) + return false; + + return in_array(strtoupper($_SERVER['REQUEST_METHOD']), array('PUT', 'DELETE', 'PROPPATCH', 'MOVE', 'COPY', 'PATCH'), true); + } + + protected function getCurrentETag() + { + if(isset($this->objectData['etag']) && $this->objectData['etag'] !== '') + return $this->objectData['etag']; + + return parent::getETag(); + } +} diff --git a/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendarRoot.php b/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendarRoot.php new file mode 100644 index 000000000..345c30b12 --- /dev/null +++ b/application/libraries/sabredav/caldav/SabreDAVReadOnlyCalendarRoot.php @@ -0,0 +1,17 @@ +readOnlyCaldavBackend = $caldavBackend; + } + + public function getChildForPrincipal(array $principal) + { + return new SabreDAVReadOnlyCalendarHome($this->readOnlyCaldavBackend, $principal); + } +}