function testHTMLActionsPanel() { $output = ''; $r = $this->server->emit('onHTMLActionsPanel', [$this->server->tree->getNodeForPath('calendars/user1'), &$output]); $this->assertFalse($r); $this->assertTrue(!!strpos($output, 'Display name')); }
function testCurrentUserPrincipal() { $fakeServer = new DAV\Server(); $plugin = new DAV\Auth\Plugin(new DAV\Auth\Backend\Mock()); $fakeServer->addPlugin($plugin); $plugin = new Plugin(); $plugin->setDefaultACL([['principal' => '{DAV:}all', 'privilege' => '{DAV:}all']]); $fakeServer->addPlugin($plugin); $requestedProperties = ['{DAV:}current-user-principal']; $result = $fakeServer->getPropertiesForPath('', $requestedProperties); $result = $result[0]; $this->assertEquals(1, count($result[200])); $this->assertArrayHasKey('{DAV:}current-user-principal', $result[200]); $this->assertInstanceOf('Sabre\\DAVACL\\Xml\\Property\\Principal', $result[200]['{DAV:}current-user-principal']); $this->assertEquals(Xml\Property\Principal::UNAUTHENTICATED, $result[200]['{DAV:}current-user-principal']->getType()); // This will force the login $fakeServer->emit('beforeMethod', [$fakeServer->httpRequest, $fakeServer->httpResponse]); $result = $fakeServer->getPropertiesForPath('', $requestedProperties); $result = $result[0]; $this->assertEquals(1, count($result[200])); $this->assertArrayHasKey('{DAV:}current-user-principal', $result[200]); $this->assertInstanceOf('Sabre\\DAVACL\\Xml\\Property\\Principal', $result[200]['{DAV:}current-user-principal']); $this->assertEquals(Xml\Property\Principal::HREF, $result[200]['{DAV:}current-user-principal']->getType()); $this->assertEquals('principals/admin/', $result[200]['{DAV:}current-user-principal']->getHref()); }
function testBeforeGetPropertiesNoListing() { $this->plugin->hideNodesFromListings = true; $propFind = new DAV\PropFind('testdir', ['{DAV:}displayname', '{DAV:}getcontentlength', '{DAV:}bar', '{DAV:}owner']); $r = $this->server->emit('propFind', [$propFind, new DAV\SimpleCollection('testdir')]); $this->assertFalse($r); }
/** * This method is responsible for delivering the ITip message. * * @param ITip\Message $itipMessage * @return void */ function deliver(ITip\Message $iTipMessage) { $this->server->emit('schedule', [$iTipMessage]); if (!$iTipMessage->scheduleStatus) { $iTipMessage->scheduleStatus = '5.2;There was no system capable of delivering the scheduling message'; } }
function schedule(Message $message) { $plugin = new IMip\MockPlugin('*****@*****.**'); $server = new Server(); $server->addPlugin($plugin); $server->emit('schedule', [$message]); return $plugin->getSentEmails(); }
/** * @depends testInit */ function testGetCurrentUserPrincipal() { $fakeServer = new DAV\Server(new DAV\SimpleCollection('bla')); $plugin = new Plugin(new Backend\Mock(), 'realm'); $fakeServer->addPlugin($plugin); $fakeServer->emit('beforeMethod', [new HTTP\Request(), new HTTP\Response()]); $this->assertEquals('admin', $plugin->getCurrentUser()); }
/** * HTTP REPORT method implementation * * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) * It's used in a lot of extensions, so it made sense to implement it into the core. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpReport(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $result = $this->server->xml->parse($request->getBody(), $request->getUrl(), $rootElementName); if ($this->server->emit('report', [$rootElementName, $result, $path])) { // If emit returned true, it means the report was not supported throw new Exception\ReportNotSupported(); } // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; }
/** * This method is responsible for delivering the ITip message. * * @param ITip\Message $itipMessage * @return void */ function deliver(ITip\Message $iTipMessage) { $this->server->emit('schedule', [$iTipMessage]); if (!$iTipMessage->scheduleStatus) { $iTipMessage->scheduleStatus = '5.2;There was no system capable of delivering the scheduling message'; } // In case the change was considered 'insignificant', we are going to // remove any error statuses, if any. See ticket #525. list($baseCode) = explode('.', $iTipMessage->scheduleStatus); if (!$iTipMessage->significantChange && in_array($baseCode, ['3', '5'])) { $iTipMessage->scheduleStatus = null; } }
/** * HTTP REPORT method implementation * * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) * It's used in a lot of extensions, so it made sense to implement it into the core. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function httpReport(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); $body = $request->getBodyAsString(); $dom = XMLUtil::loadDOMDocument($body); $reportName = XMLUtil::toClarkNotation($dom->firstChild); if ($this->server->emit('report', [$reportName, $dom, $path])) { // If emit returned true, it means the report was not supported throw new Exception\ReportNotSupported(); } // Sending back false will interupt the event chain and tell the server // we've handled this method. return false; }
/** * Updates the list of sharees on a shared resource. * * The sharees array is a list of people that are to be added modified * or removed in the list of shares. * * @param string $path * @param Sharee[] $sharees * @return void */ function shareResource($path, array $sharees) { $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof ISharedNode) { throw new Forbidden('Sharing is not allowed on this node'); } // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}share'); } foreach ($sharees as $sharee) { // We're going to attempt to get a local principal uri for a share // href by emitting the getPrincipalByUri event. $principal = null; $this->server->emit('getPrincipalByUri', [$sharee->href, &$principal]); $sharee->principal = $principal; } $node->updateInvites($sharees); }
/** * 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 * @param bool $modified Should be set to true, if this event handler * changed &$data. * @param RequestInterface $request The http request. * @param ResponseInterface $response The http response. * @param bool $isNew Is the item a new one, or an update. * @return void */ protected function validateICalendar(&$data, $path, &$modified, RequestInterface $request, ResponseInterface $response, $isNew) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } $before = md5($data); // Converting the data to unicode, if needed. $data = DAV\StringUtil::ensureUTF8($data); if ($before !== md5($data)) { $modified = true; } try { // If the data starts with a [, we can reasonably assume we're dealing // with a jCal object. if (substr($data, 0, 1) === '[') { $vobj = VObject\Reader::readJson($data); // Converting $data back to iCalendar, as that's what we // technically support everywhere. $data = $vobj->serialize(); $modified = true; } else { $vobj = VObject\Reader::read($data); } } catch (VObject\ParseException $e) { throw new DAV\Exception\UnsupportedMediaType('This resource only supports 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.'); } $sCCS = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; // Get the Supported Components for the target calendar list($parentPath) = Uri\split($path); $calendarProperties = $this->server->getProperties($parentPath, [$sCCS]); if (isset($calendarProperties[$sCCS])) { $supportedComponents = $calendarProperties[$sCCS]->getValue(); } else { $supportedComponents = ['VJOURNAL', 'VTODO', 'VEVENT']; } $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 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'); } // We use an extra variable to allow event handles to tell us wether // the object was modified or not. // // This helps us determine if we need to re-serialize the object. $subModified = false; $this->server->emit('calendarObjectChange', [$request, $response, $vobj, $parentPath, &$subModified, $isNew]); if ($subModified) { // An event handler told us that it modified the object. $data = $vobj->serialize(); // Using md5 to figure out if there was an *actual* change. if (!$modified && $before !== md5($data)) { $modified = true; } } }
function testBeforeUnbind() { $this->assertTrue($this->server->emit('beforeUnbind', ['testdir'])); }
/** * 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 * @param bool $modified Should be set to true, if this event handler * changed &$data. * @param RequestInterface $request The http request. * @param ResponseInterface $response The http response. * @param bool $isNew Is the item a new one, or an update. * @return void */ protected function validateICalendar(&$data, $path, &$modified, RequestInterface $request, ResponseInterface $response, $isNew) { // If it's a stream, we convert it to a string first. if (is_resource($data)) { $data = stream_get_contents($data); } $before = $data; try { // If the data starts with a [, we can reasonably assume we're dealing // with a jCal object. if (substr($data, 0, 1) === '[') { $vobj = VObject\Reader::readJson($data); // Converting $data back to iCalendar, as that's what we // technically support everywhere. $data = $vobj->serialize(); $modified = true; } else { $vobj = VObject\Reader::read($data); } } catch (VObject\ParseException $e) { throw new DAV\Exception\UnsupportedMediaType('This resource only supports 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.'); } $sCCS = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; // Get the Supported Components for the target calendar list($parentPath) = Uri\split($path); $calendarProperties = $this->server->getProperties($parentPath, [$sCCS]); if (isset($calendarProperties[$sCCS])) { $supportedComponents = $calendarProperties[$sCCS]->getValue(); } else { $supportedComponents = ['VJOURNAL', 'VTODO', 'VEVENT']; } $foundType = null; foreach ($vobj->getComponents() as $component) { switch ($component->name) { case 'VTIMEZONE': continue 2; case 'VEVENT': case 'VTODO': case 'VJOURNAL': $foundType = $component->name; break; } } if (!$foundType || !in_array($foundType, $supportedComponents)) { throw new Exception\InvalidComponentType('iCalendar objects must at least have a component of type ' . implode(', ', $supportedComponents)); } $options = VObject\Node::PROFILE_CALDAV; $prefer = $this->server->getHTTPPrefer(); if ($prefer['handling'] !== 'strict') { $options |= VObject\Node::REPAIR; } $messages = $vobj->validate($options); $highestLevel = 0; $warningMessage = null; // $messages contains a list of problems with the vcard, along with // their severity. foreach ($messages as $message) { if ($message['level'] > $highestLevel) { // Recording the highest reported error level. $highestLevel = $message['level']; $warningMessage = $message['message']; } switch ($message['level']) { case 1: // Level 1 means that there was a problem, but it was repaired. $modified = true; break; case 2: // Level 2 means a warning, but not critical break; case 3: // Level 3 means a critical error throw new DAV\Exception\UnsupportedMediaType('Validation error in iCalendar: ' . $message['message']); } } if ($warningMessage) { $response->setHeader('X-Sabre-Ew-Gross', 'iCalendar validation warning: ' . $warningMessage); } // We use an extra variable to allow event handles to tell us wether // the object was modified or not. // // This helps us determine if we need to re-serialize the object. $subModified = false; $this->server->emit('calendarObjectChange', [$request, $response, $vobj, $parentPath, &$subModified, $isNew]); if ($modified || $subModified) { // An event handler told us that it modified the object. $data = $vobj->serialize(); // Using md5 to figure out if there was an *actual* change. if (!$modified && strcmp($data, $before) !== 0) { $modified = true; } } // Destroy circular references so PHP will garbage collect the object. $vobj->destroy(); }