/** * 'beforeMethod' event handles. This event handles intercepts GET requests ending * with ?export * * @param string $method * @param string $uri * @return bool */ public function beforeMethod($method, $uri) { if ($method != 'GET') { return; } if ($this->server->httpRequest->getQueryString() != 'export') { return; } // splitting uri list($uri) = explode('?', $uri, 2); $node = $this->server->tree->getNodeForPath($uri); if (!$node instanceof Calendar) { return; } // Checking ACL, if available. if ($aclPlugin = $this->server->getPlugin('acl')) { $aclPlugin->checkPrivileges($uri, '{DAV:}read'); } $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); $this->server->httpResponse->sendStatus(200); $nodes = $this->server->getPropertiesForPath($uri, array('{' . Plugin::NS_CALDAV . '}calendar-data'), 1); $this->server->httpResponse->sendBody($this->generateICS($nodes)); // Returning false to break the event chain return false; }
/** * Intercepts GET requests on addressbook urls ending with ?photo. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool|void */ function httpGet(RequestInterface $request, ResponseInterface $response) { $queryParams = $request->getQueryParameters(); // TODO: in addition to photo we should also add logo some point in time if (!array_key_exists('photo', $queryParams)) { return true; } $path = $request->getPath(); $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof Card) { return true; } $this->server->transactionType = 'carddav-image-export'; // Checking ACL, if available. if ($aclPlugin = $this->server->getPlugin('acl')) { /** @var \Sabre\DAVACL\Plugin $aclPlugin */ $aclPlugin->checkPrivileges($path, '{DAV:}read'); } if ($result = $this->getPhoto($node)) { $response->setHeader('Content-Type', $result['Content-Type']); $response->setStatus(200); $response->setBody($result['body']); // Returning false to break the event chain return false; } return true; }
protected function getUser() { $user = null; $authPlugin = $this->server->getPlugin('auth'); if ($authPlugin !== null) { $user = $authPlugin->getCurrentUser(); } return $user; }
function setUp() { $nodes = [new DAV\Mock\Collection('testdir', ['file1.txt' => 'contents'])]; $this->server = new DAV\Server($nodes); $this->server->addPlugin(new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock())); // Login $this->server->getPlugin('auth')->beforeMethod(new \Sabre\HTTP\Request(), new \Sabre\HTTP\Response()); $aclPlugin = new Plugin(); $this->server->addPlugin($aclPlugin); }
function setUp() { $nodes = [new DAV\SimpleCollection('testdir')]; $this->server = new DAV\Server($nodes); $this->plugin = new Plugin(); $this->plugin->setDefaultAcl([]); $this->server->addPlugin(new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock())); // Login $this->server->getPlugin('auth')->beforeMethod(new \Sabre\HTTP\Request(), new \Sabre\HTTP\Response()); $this->server->addPlugin($this->plugin); }
/** * PropFind * * @param PropFind $propFind * @param BaseINode $node * @return void */ function propFind(PropFind $propFind, BaseINode $node) { $caldavPlugin = $this->server->getPlugin('caldav'); if ($node instanceof DAVACL\IPrincipal) { $principalUrl = $node->getPrincipalUrl(); // notification-URL property $propFind->handle('{' . self::NS_CALENDARSERVER . '}notification-URL', function () use($principalUrl, $caldavPlugin) { $notificationPath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl) . '/notifications/'; return new DAV\Xml\Property\Href($notificationPath); }); } if ($node instanceof INode) { $propFind->handle('{' . self::NS_CALENDARSERVER . '}notificationtype', [$node, 'getNotificationType']); } }
/** * This method is used to generate HTML output for the * DAV\Browser\Plugin. * * @param INode $node * @param string $output * @param string $path * @return bool|null */ function htmlActionsPanel(INode $node, &$output, $path) { if (!$node instanceof ISharedNode) { return; } $aclPlugin = $this->server->getPlugin('acl'); if ($aclPlugin) { if (!$aclPlugin->checkPrivileges($path, '{DAV:}share', \Sabre\DAVACL\Plugin::R_PARENT, false)) { // Sharing is not permitted, we will not draw this interface. return; } } $output .= '<tr><td colspan="2"><form method="post" action=""> <h3>Share this resource</h3> <input type="hidden" name="sabreAction" value="share" /> <label>Share with (uri):</label> <input type="text" name="href" placeholder="mailto:user@example.org"/><br /> <label>Access</label> <select name="access"> <option value="readwrite">Read-write</option> <option value="read">Read-only</option> <option value="no-access">Revoke access</option> </select><br /> <input type="submit" value="share" /> </form> </td></tr>'; }
function testInit() { $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); $plugin = new Plugin(new Backend\Mock(), 'realm'); $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('auth')); }
/** * This method is responsible for parsing the request and generating the * response for the CALDAV:free-busy-query REPORT. * * @param \DOMNode $dom * @return void */ protected function freeBusyQueryReport(\DOMNode $dom) { $start = null; $end = null; foreach ($dom->firstChild->childNodes as $childNode) { $clark = DAV\XMLUtil::toClarkNotation($childNode); if ($clark == '{' . self::NS_CALDAV . '}time-range') { $start = $childNode->getAttribute('start'); $end = $childNode->getAttribute('end'); break; } } if ($start) { $start = VObject\DateTimeParser::parseDateTime($start); } if ($end) { $end = VObject\DateTimeParser::parseDateTime($end); } $uri = $this->server->getRequestUri(); if (!$start && !$end) { throw new DAV\Exception\BadRequest('The freebusy report must have a time-range filter'); } $acl = $this->server->getPlugin('acl'); if ($acl) { $acl->checkPrivileges($uri, '{' . self::NS_CALDAV . '}read-free-busy'); } $calendar = $this->server->tree->getNodeForPath($uri); if (!$calendar instanceof ICalendar) { throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars'); } $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; // Figuring out the default timezone for the calendar, for floating // times. $calendarProps = $this->server->getProperties($uri, [$tzProp]); if (isset($calendarProps[$tzProp])) { $vtimezoneObj = VObject\Reader::read($calendarProps[$tzProp]); $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); } else { $calendarTimeZone = new DateTimeZone('UTC'); } // Doing a calendar-query first, to make sure we get the most // performance. $urls = $calendar->calendarQuery(['name' => 'VCALENDAR', 'comp-filters' => [['name' => 'VEVENT', 'comp-filters' => [], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => ['start' => $start, 'end' => $end]]], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => null]); $objects = array_map(function ($url) use($calendar) { $obj = $calendar->getChild($url)->get(); return $obj; }, $urls); $generator = new VObject\FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $generator->setTimeZone($calendarTimeZone); $result = $generator->getResult(); $result = $result->serialize(); $this->server->httpResponse->setStatus(200); $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); $this->server->httpResponse->setHeader('Content-Length', strlen($result)); $this->server->httpResponse->setBody($result); }
/** * 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 * * The following request status codes may be returned: * * 2.0;description * * 3.7;description * * @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) { $caldavNS = '{' . Plugin::NS_CALDAV . '}'; $aclPlugin = $this->server->getPlugin('acl'); if (substr($email, 0, 7) === 'mailto:') { $email = substr($email, 7); } $result = $aclPlugin->principalSearch(['{http://sabredav.org/ns}email-address' => $email], ['{DAV:}principal-URL', $caldavNS . 'calendar-home-set', '{http://sabredav.org/ns}email-address']); if (!count($result)) { return ['request-status' => '3.7;Could not find principal', 'href' => 'mailto:' . $email]; } if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) { return ['request-status' => '3.7;No calendar-home-set property found', 'href' => 'mailto:' . $email]; } $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref(); // Grabbing the calendar list $objects = []; foreach ($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) { if (!$node instanceof ICalendar) { continue; } $sct = $caldavNS . 'schedule-calendar-transp'; $ctz = $caldavNS . 'calendar-timezone'; $props = $node->getProperties([$sct, $ctz]); if (isset($props[$sct]) && $props[$sct]->getValue() == ScheduleCalendarTransp::TRANSPARENT) { // If a calendar is marked as 'transparent', it means we must // ignore it for free-busy purposes. continue; } $aclPlugin->checkPrivileges($homeSet . $node->getName(), $caldavNS . 'read-free-busy'); if (isset($props[$ctz])) { $vtimezoneObj = VObject\Reader::read($props[$ctz]); $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); } else { $calendarTimeZone = new DateTimeZone('UTC'); } // Getting the list of object uris within the time-range $urls = $node->calendarQuery(['name' => 'VCALENDAR', 'comp-filters' => [['name' => 'VEVENT', 'comp-filters' => [], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => ['start' => $start, 'end' => $end]]], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => null]); $calObjects = array_map(function ($url) use($node) { $obj = $node->getChild($url)->get(); return $obj; }, $urls); $objects = array_merge($objects, $calObjects); } $vcalendar = new VObject\Component\VCalendar(); $vcalendar->METHOD = 'REPLY'; $generator = new VObject\FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $generator->setBaseObject($vcalendar); $generator->setTimeZone($calendarTimeZone); $result = $generator->getResult(); $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email; $vcalendar->VFREEBUSY->UID = (string) $request->VFREEBUSY->UID; $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER; return ['calendar-data' => $result, 'request-status' => '2.0;Success', 'href' => 'mailto:' . $email]; }
function testInit() { $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); $plugin = new Plugin(new Backend\Mock()); $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('auth')); $this->assertInternalType('array', $plugin->getPluginInfo()); }
/** * We intercept this to handle POST requests on calendars. * * @param RequestInterface $request * @param ResponseInterface $response * @return null|bool */ function httpPost(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); // Only handling xml $contentType = $request->getHeader('Content-Type'); if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false) { return; } // Making sure the node exists try { $node = $this->server->tree->getNodeForPath($path); } catch (NotFound $e) { return; } // CSRF protection $this->protectAgainstCSRF(); $requestBody = $request->getBodyAsString(); // If this request handler could not deal with this POST request, it // will return 'null' and other plugins get a chance to handle the // request. // // However, we already requested the full body. This is a problem, // because a body can only be read once. This is why we preemptively // re-populated the request body with the existing data. $request->setBody($requestBody); $dom = XMLUtil::loadDOMDocument($requestBody); $documentType = XMLUtil::toClarkNotation($dom->firstChild); switch ($documentType) { // Dealing with the 'share' document, which modified invitees on a // calendar. case '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}share': // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableAddressBook) { return; } $this->server->transactionType = 'post-calendar-share'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $mutations = $this->parseShareRequest($dom); $node->updateShares($mutations[0], $mutations[1]); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; } }
function getServer() { $tree = array(new MockPropertyNode('node1', array('{http://sabredav.org/ns}simple' => 'foo', '{http://sabredav.org/ns}href' => new DAV\Property\Href('node2'), '{DAV:}displayname' => 'Node 1')), new MockPropertyNode('node2', array('{http://sabredav.org/ns}simple' => 'simple', '{http://sabredav.org/ns}hreflist' => new DAV\Property\HrefList(array('node1', 'node3')), '{DAV:}displayname' => 'Node 2')), new MockPropertyNode('node3', array('{http://sabredav.org/ns}simple' => 'simple', '{DAV:}displayname' => 'Node 3'))); $fakeServer = new DAV\Server($tree); $fakeServer->debugExceptions = true; $fakeServer->httpResponse = new HTTP\ResponseMock(); $plugin = new Plugin(); $plugin->allowAccessToNodesWithoutACL = true; $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); return $fakeServer; }
function getServer() { $tree = [new DAV\Mock\PropertiesCollection('node1', [], ['{http://sabredav.org/ns}simple' => 'foo', '{http://sabredav.org/ns}href' => new DAV\Xml\Property\Href('node2'), '{DAV:}displayname' => 'Node 1']), new DAV\Mock\PropertiesCollection('node2', [], ['{http://sabredav.org/ns}simple' => 'simple', '{http://sabredav.org/ns}hreflist' => new DAV\Xml\Property\Href(['node1', 'node3']), '{DAV:}displayname' => 'Node 2']), new DAV\Mock\PropertiesCollection('node3', [], ['{http://sabredav.org/ns}simple' => 'simple', '{DAV:}displayname' => 'Node 3'])]; $fakeServer = new DAV\Server($tree); $fakeServer->sapi = new HTTP\SapiMock(); $fakeServer->debugExceptions = true; $fakeServer->httpResponse = new HTTP\ResponseMock(); $plugin = new Plugin(); $plugin->allowAccessToNodesWithoutACL = true; $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); return $fakeServer; }
function getServer() { $backend = new PrincipalBackend\Mock(); $dir = new DAV\SimpleCollection('root'); $principals = new PrincipalCollection($backend); $dir->addChild($principals); $fakeServer = new DAV\Server(new DAV\ObjectTree($dir)); $fakeServer->httpResponse = new HTTP\ResponseMock(); $plugin = new Plugin($backend, 'realm'); $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); return $fakeServer; }
/** * We intercept this to handle POST requests on a dav resource. * * @param RequestInterface $request * @param ResponseInterface $response * @return null|false */ function httpPost(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); // Only handling xml $contentType = $request->getHeader('Content-Type'); if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false) { return; } // Making sure the node exists try { $node = $this->server->tree->getNodeForPath($path); } catch (NotFound $e) { return; } $requestBody = $request->getBodyAsString(); // If this request handler could not deal with this POST request, it // will return 'null' and other plugins get a chance to handle the // request. // // However, we already requested the full body. This is a problem, // because a body can only be read once. This is why we preemptively // re-populated the request body with the existing data. $request->setBody($requestBody); $message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType); switch ($documentType) { // Dealing with the 'share' document, which modified invitees on a // calendar. case '{' . self::NS_OWNCLOUD . '}share': // We can only deal with IShareableCalendar objects if (!$node instanceof IShareable) { return; } $this->server->transactionType = 'post-oc-resource-share'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { /** @var \Sabre\DAVACL\Plugin $acl */ $acl->checkPrivileges($path, '{DAV:}write'); } $node->updateShares($message->set, $message->remove); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; } }
protected function _principalSearchReport(\DOMDocument $dom) { $requestedProperties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($dom->firstChild)); $searchTokens = $dom->firstChild->getElementsByTagName('search-token'); $searchProperties = array(); if ($searchTokens->length > 0) { $searchProperties['{http://calendarserver.org/ns/}search-token'] = $searchTokens->item(0)->nodeValue; } $result = $this->server->getPlugin('acl')->principalSearch($searchProperties, $requestedProperties); $prefer = $this->server->getHTTPPRefer(); $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal'])); }
function getServer() { $backend = new PrincipalBackend\Mock(); $dir = new DAV\SimpleCollection('root'); $principals = new PrincipalCollection($backend); $dir->addChild($principals); $fakeServer = new DAV\Server($dir); $fakeServer->sapi = new HTTP\SapiMock(); $fakeServer->httpResponse = new HTTP\ResponseMock(); $plugin = new Plugin(); $plugin->allowUnauthenticatedAccess = false; $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); return $fakeServer; }
function getServer() { $tree = [new DAV\Mock\PropertiesCollection('node1', [], ['{http://sabredav.org/ns}simple' => 'foo', '{http://sabredav.org/ns}href' => new DAV\Xml\Property\Href('node2'), '{DAV:}displayname' => 'Node 1']), new DAV\Mock\PropertiesCollection('node2', [], ['{http://sabredav.org/ns}simple' => 'simple', '{http://sabredav.org/ns}hreflist' => new DAV\Xml\Property\Href(['node1', 'node3']), '{DAV:}displayname' => 'Node 2']), new DAV\Mock\PropertiesCollection('node3', [], ['{http://sabredav.org/ns}simple' => 'simple', '{DAV:}displayname' => 'Node 3'])]; $fakeServer = new DAV\Server($tree); $fakeServer->sapi = new HTTP\SapiMock(); $fakeServer->debugExceptions = true; $fakeServer->httpResponse = new HTTP\ResponseMock(); $plugin = new Plugin(); $plugin->allowUnauthenticatedAccess = false; // Anyone can do anything $plugin->setDefaultACL([['principal' => '{DAV:}all', 'privilege' => '{DAV:}all']]); $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); return $fakeServer; }
function getServer() { $backend = new PrincipalBackend\Mock(); $dir = new DAV\SimpleCollection('root'); $principals = new PrincipalCollection($backend); $dir->addChild($principals); $fakeServer = new DAV\Server($dir); $fakeServer->sapi = new HTTP\SapiMock(); $fakeServer->httpResponse = new HTTP\ResponseMock(); $fakeServer->debugExceptions = true; $plugin = new MockPlugin($backend, 'realm'); $plugin->allowAccessToNodesWithoutACL = true; $this->assertTrue($plugin instanceof Plugin); $fakeServer->addPlugin($plugin); $this->assertEquals($plugin, $fakeServer->getPlugin('acl')); return $fakeServer; }
/** * 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 * * The following request status codes may be returned: * * 2.0;description * * 3.7;description * * @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) { $caldavNS = '{' . Plugin::NS_CALDAV . '}'; $aclPlugin = $this->server->getPlugin('acl'); if (substr($email, 0, 7) === 'mailto:') { $email = substr($email, 7); } $result = $aclPlugin->principalSearch(array('{http://sabredav.org/ns}email-address' => $email), array('{DAV:}principal-URL', $caldavNS . 'calendar-home-set', '{http://sabredav.org/ns}email-address')); if (!count($result)) { return array('request-status' => '3.7;Could not find principal', 'href' => 'mailto:' . $email); } if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) { return array('request-status' => '3.7;No calendar-home-set property found', 'href' => 'mailto:' . $email); } $homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref(); // Grabbing the calendar list $objects = array(); foreach ($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) { if (!$node instanceof ICalendar) { continue; } $aclPlugin->checkPrivileges($homeSet . $node->getName(), $caldavNS . 'read-free-busy'); // Getting the list of object uris within the time-range $urls = $node->calendarQuery(array('name' => 'VCALENDAR', 'comp-filters' => array(array('name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array('start' => $start, 'end' => $end))), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null)); $calObjects = array_map(function ($url) use($node) { $obj = $node->getChild($url)->get(); return $obj; }, $urls); $objects = array_merge($objects, $calObjects); } $vcalendar = VObject\Component::create('VCALENDAR'); $vcalendar->VERSION = '2.0'; $vcalendar->METHOD = 'REPLY'; $vcalendar->CALSCALE = 'GREGORIAN'; $vcalendar->PRODID = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN'; $generator = new VObject\FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $generator->setBaseObject($vcalendar); $result = $generator->getResult(); $vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email; $vcalendar->VFREEBUSY->UID = (string) $request->VFREEBUSY->UID; $vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER; return array('calendar-data' => $result, 'request-status' => '2.0;Success', 'href' => 'mailto:' . $email); }
/** * This method is responsible for parsing the request and generating the * response for the CALDAV:free-busy-query REPORT. * * @param Xml\Request\FreeBusyQueryReport $report * @return void */ protected function freeBusyQueryReport(Xml\Request\FreeBusyQueryReport $report) { $uri = $this->server->getRequestUri(); $acl = $this->server->getPlugin('acl'); if ($acl) { $acl->checkPrivileges($uri, '{' . self::NS_CALDAV . '}read-free-busy'); } $calendar = $this->server->tree->getNodeForPath($uri); if (!$calendar instanceof ICalendar) { throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars'); } $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone'; // Figuring out the default timezone for the calendar, for floating // times. $calendarProps = $this->server->getProperties($uri, [$tzProp]); if (isset($calendarProps[$tzProp])) { $vtimezoneObj = VObject\Reader::read($calendarProps[$tzProp]); $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone(); // Destroy circular references so PHP will garbage collect the object. $vtimezoneObj->destroy(); } else { $calendarTimeZone = new DateTimeZone('UTC'); } // Doing a calendar-query first, to make sure we get the most // performance. $urls = $calendar->calendarQuery(['name' => 'VCALENDAR', 'comp-filters' => [['name' => 'VEVENT', 'comp-filters' => [], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => ['start' => $report->start, 'end' => $report->end]]], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => null]); $objects = array_map(function ($url) use($calendar) { $obj = $calendar->getChild($url)->get(); return $obj; }, $urls); $generator = new VObject\FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($report->start, $report->end); $generator->setTimeZone($calendarTimeZone); $result = $generator->getResult(); $result = $result->serialize(); $this->server->httpResponse->setStatus(200); $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); $this->server->httpResponse->setHeader('Content-Length', strlen($result)); $this->server->httpResponse->setBody($result); }
function testACLIntegrationNotBlocked() { $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array('uri' => 'UUID-123467', 'principaluri' => 'admin', 'id' => 1); $tree = array(new Calendar($cbackend, $props), new DAVACL\PrincipalCollection($pbackend)); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->sapi = new HTTP\SapiMock(); $s->addPlugin($p); $s->addPlugin(new Plugin()); $s->addPlugin(new DAVACL\Plugin()); $s->addPlugin(new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(), 'SabreDAV')); // Forcing login $s->getPlugin('acl')->adminPrincipals = array('principals/admin'); $h = HTTP\Sapi::createFromServerArray(['REQUEST_URI' => '/UUID-123467?export', 'REQUEST_METHOD' => 'GET']); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals(200, $s->httpResponse->status, 'Invalid status received. Response body: ' . $s->httpResponse->body); $this->assertEquals(array('X-Sabre-Version' => DAV\Version::VERSION, 'Content-Type' => 'text/calendar'), $s->httpResponse->getHeaders()); $obj = VObject\Reader::read($s->httpResponse->body); $this->assertEquals(5, count($obj->children())); $this->assertEquals(1, count($obj->VERSION)); $this->assertEquals(1, count($obj->CALSCALE)); $this->assertEquals(1, count($obj->PRODID)); $this->assertEquals(1, count($obj->VTIMEZONE)); $this->assertEquals(1, count($obj->VEVENT)); }
function testACLIntegrationNotBlocked() { if (!SABRE_HASSQLITE) { $this->markTestSkipped('SQLite driver is not available'); } $cbackend = TestUtil::getBackend(); $pbackend = new DAVACL\PrincipalBackend\Mock(); $props = array('uri' => 'UUID-123467', 'principaluri' => 'admin', 'id' => 1); $tree = array(new Calendar($cbackend, $props), new DAVACL\PrincipalCollection($pbackend)); $p = new ICSExportPlugin(); $s = new DAV\Server($tree); $s->addPlugin($p); $s->addPlugin(new Plugin()); $s->addPlugin(new DAVACL\Plugin()); $s->addPlugin(new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock(), 'SabreDAV')); // Forcing login $s->getPlugin('acl')->adminPrincipals = array('principals/admin'); $h = new HTTP\Request(array('QUERY_STRING' => 'export', 'REQUEST_URI' => '/UUID-123467', 'REQUEST_METHOD' => 'GET')); $s->httpRequest = $h; $s->httpResponse = new HTTP\ResponseMock(); $s->exec(); $this->assertEquals('HTTP/1.1 200 OK', $s->httpResponse->status, 'Invalid status received. Response body: ' . $s->httpResponse->body); $this->assertEquals(array('Content-Type' => 'text/calendar'), $s->httpResponse->headers); $obj = VObject\Reader::read($s->httpResponse->body); $this->assertEquals(5, count($obj->children())); $this->assertEquals(1, count($obj->VERSION)); $this->assertEquals(1, count($obj->CALSCALE)); $this->assertEquals(1, count($obj->PRODID)); $this->assertEquals(1, count($obj->VTIMEZONE)); $this->assertEquals(1, count($obj->VEVENT)); }
/** * Sets up the plugin * * This method is automatically called by the server class. * * @param DAV\Server $server * @return void */ function initialize(DAV\Server $server) { if ($this->allowUnauthenticatedAccess) { $authPlugin = $server->getPlugin('auth'); if (!$authPlugin) { throw new \Exception('The Auth plugin must be loaded before the ACL plugin if you want to allow unauthenticated access.'); } $authPlugin->autoRequireLogin = false; } $this->server = $server; $server->on('propFind', [$this, 'propFind'], 20); $server->on('beforeMethod', [$this, 'beforeMethod'], 20); $server->on('beforeBind', [$this, 'beforeBind'], 20); $server->on('beforeUnbind', [$this, 'beforeUnbind'], 20); $server->on('propPatch', [$this, 'propPatch']); $server->on('beforeUnlock', [$this, 'beforeUnlock'], 20); $server->on('report', [$this, 'report']); $server->on('method:ACL', [$this, 'httpAcl']); $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']); $server->on('getPrincipalByUri', function ($principal, &$uri) { $uri = $this->getPrincipalByUri($principal); // Break event chain if ($uri) { return false; } }); array_push($server->protectedProperties, '{DAV:}alternate-URI-set', '{DAV:}principal-URL', '{DAV:}group-membership', '{DAV:}principal-collection-set', '{DAV:}current-user-principal', '{DAV:}supported-privilege-set', '{DAV:}current-user-privilege-set', '{DAV:}acl', '{DAV:}acl-restrictions', '{DAV:}inherited-acl-set', '{DAV:}owner', '{DAV:}group'); // Automatically mapping nodes implementing IPrincipal to the // {DAV:}principal resourcetype. $server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal'; // Mapping the group-member-set property to the HrefList property // class. $server->xml->elementMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Xml\\Property\\Href'; $server->xml->elementMap['{DAV:}acl'] = 'Sabre\\DAVACL\\Xml\\Property\\Acl'; $server->xml->elementMap['{DAV:}acl-principal-prop-set'] = 'Sabre\\DAVACL\\Xml\\Request\\AclPrincipalPropSetReport'; $server->xml->elementMap['{DAV:}expand-property'] = 'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport'; $server->xml->elementMap['{DAV:}principal-property-search'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport'; $server->xml->elementMap['{DAV:}principal-search-property-set'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport'; $server->xml->elementMap['{DAV:}principal-match'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalMatchReport'; }