/** * Unlocks a uri * * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified * * @param string $uri * @param LockInfo $lockInfo * @return bool */ function unlockNode($uri, LockInfo $lockInfo) { if (!$this->server->emit('beforeUnlock', [$uri, $lockInfo])) { return; } return $this->locksBackend->unlock($uri, $lockInfo); }
/** * Patch an uri * * The WebDAV patch request can be used to modify only a part of an * existing resource. If the resource does not exist yet and the first * offset is not 0, the request fails * * @param RequestInterface $request * @param ResponseInterface $response * @return void */ function httpPatch(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); // Get the node. Will throw a 404 if not found $node = $this->server->tree->getNodeForPath($path); if (!$node instanceof IPatchSupport) { throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.'); } $range = $this->getHTTPUpdateRange($request); if (!$range) { throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers'); } $contentType = strtolower($request->getHeader('Content-Type')); if ($contentType != 'application/x-sabredav-partialupdate') { throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"'); } $len = $this->server->httpRequest->getHeader('Content-Length'); if (!$len) { throw new DAV\Exception\LengthRequired('A Content-Length header is required'); } switch ($range[0]) { case self::RANGE_START: // Calculate the end-range if it doesn't exist. if (!$range[2]) { $range[2] = $range[1] + $len - 1; } else { if ($range[2] < $range[1]) { throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[2] . ') is lower than the start offset (' . $range[1] . ')'); } if ($range[2] - $range[1] + 1 != $len) { throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[1] . ') and end (' . $range[2] . ') offsets'); } } break; } if (!$this->server->emit('beforeWriteContent', [$path, $node, null])) { return; } $body = $this->server->httpRequest->getBody(); $etag = $node->patch($body, $range[0], isset($range[1]) ? $range[1] : null); $this->server->emit('afterWriteContent', [$path, $node]); $response->setHeader('Content-Length', '0'); if ($etag) { $response->setHeader('ETag', $etag); } $response->setStatus(204); // Breaks the event chain return false; }
function testBrowserPostAction() { $r = $this->server->emit('onBrowserPostAction', ['calendars/user1', 'mkcalendar', ['name' => 'NEWCALENDAR', '{DAV:}displayname' => 'foo']]); $this->assertFalse($r); $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); $this->assertEquals(3, count($calendars)); $newCalendar = null; foreach ($calendars as $calendar) { if ($calendar['uri'] === 'NEWCALENDAR') { $newCalendar = $calendar; break; } } if (!$newCalendar) { $this->fail('Could not find newly created calendar'); } }
/** * Returns a tree of supported privileges for a resource. * * The returned array structure should be in this form: * * [ * [ * 'privilege' => '{DAV:}read', * 'abstract' => false, * 'aggregates' => [] * ] * ] * * Privileges can be nested using "aggregrates". Doing so means that * if you assign someone the aggregrating privilege, all the * sub-privileges will automatically be granted. * * Marking a privilege as abstract means that the privilege cannot be * directly assigned, but must be assigned via the parent privilege. * * So a more complex version might look like this: * * [ * [ * 'privilege' => '{DAV:}read', * 'abstract' => false, * 'aggregates' => [ * [ * 'privilege' => '{DAV:}read-acl', * 'abstract' => false, * 'aggregates' => [], * ] * ] * ] * ] * * @param string|INode $node * @return array */ function getSupportedPrivilegeSet($node) { if (is_string($node)) { $node = $this->server->tree->getNodeForPath($node); } $supportedPrivileges = null; if ($node instanceof IACL) { $supportedPrivileges = $node->getSupportedPrivilegeSet(); } if (is_null($supportedPrivileges)) { // Default $supportedPrivileges = ['{DAV:}read' => ['abstract' => false, 'aggregates' => ['{DAV:}read-acl' => ['abstract' => false, 'aggregates' => []], '{DAV:}read-current-user-privilege-set' => ['abstract' => false, 'aggregates' => []]]], '{DAV:}write' => ['abstract' => false, 'aggregates' => ['{DAV:}write-properties' => ['abstract' => false, 'aggregates' => []], '{DAV:}write-content' => ['abstract' => false, 'aggregates' => []], '{DAV:}unlock' => ['abstract' => false, 'aggregates' => []]]]]; if ($node instanceof \Sabre\DAV\ICollection) { $supportedPrivileges['{DAV:}write']['aggregates']['{DAV:}bind'] = ['abstract' => false, 'aggregates' => []]; $supportedPrivileges['{DAV:}write']['aggregates']['{DAV:}unbind'] = ['abstract' => false, 'aggregates' => []]; } if ($node instanceof \Sabre\DAVACL\IACL) { $supportedPrivileges['{DAV:}write']['aggregates']['{DAV:}write-acl'] = ['abstract' => false, 'aggregates' => []]; } } $this->server->emit('getSupportedPrivilegeSet', [$node, &$supportedPrivileges]); return $supportedPrivileges; }
/** * Generates the html directory index for a given url * * @param string $path * @return string */ function generateDirectoryIndex($path) { $html = $this->generateHeader($path ? $path : '/', $path); $node = $this->server->tree->getNodeForPath($path); if ($node instanceof DAV\ICollection) { $html .= "<section><h1>Nodes</h1>\n"; $html .= "<table class=\"nodeTable\">"; $subNodes = $this->server->getPropertiesForChildren($path, ['{DAV:}displayname', '{DAV:}resourcetype', '{DAV:}getcontenttype', '{DAV:}getcontentlength', '{DAV:}getlastmodified']); foreach ($subNodes as $subPath => $subProps) { $subNode = $this->server->tree->getNodeForPath($subPath); $fullPath = $this->server->getBaseUri() . URLUtil::encodePath($subPath); list(, $displayPath) = URLUtil::splitPath($subPath); $subNodes[$subPath]['subNode'] = $subNode; $subNodes[$subPath]['fullPath'] = $fullPath; $subNodes[$subPath]['displayPath'] = $displayPath; } uasort($subNodes, [$this, 'compareNodes']); foreach ($subNodes as $subProps) { $type = ['string' => 'Unknown', 'icon' => 'cog']; if (isset($subProps['{DAV:}resourcetype'])) { $type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']); } $html .= '<tr>'; $html .= '<td class="nameColumn"><a href="' . $this->escapeHTML($subProps['fullPath']) . '"><span class="oi" data-glyph="' . $this->escapeHTML($type['icon']) . '"></span> ' . $this->escapeHTML($subProps['displayPath']) . '</a></td>'; $html .= '<td class="typeColumn">' . $this->escapeHTML($type['string']) . '</td>'; $html .= '<td>'; if (isset($subProps['{DAV:}getcontentlength'])) { $html .= $this->escapeHTML($subProps['{DAV:}getcontentlength'] . ' bytes'); } $html .= '</td><td>'; if (isset($subProps['{DAV:}getlastmodified'])) { $lastMod = $subProps['{DAV:}getlastmodified']->getTime(); $html .= $this->escapeHTML($lastMod->format('F j, Y, g:i a')); } $html .= '</td>'; $buttonActions = ''; if ($subNode instanceof DAV\IFile) { $buttonActions = '<a href="' . $this->escapeHTML($subProps['fullPath']) . '?sabreAction=info"><span class="oi" data-glyph="info"></span></a>'; } $this->server->emit('browserButtonActions', [$subProps['fullPath'], $subProps['subNode'], &$buttonActions]); $html .= '<td>' . $buttonActions . '</td>'; $html .= '</tr>'; } $html .= '</table>'; } $html .= "</section>"; $html .= "<section><h1>Properties</h1>"; $html .= "<table class=\"propTable\">"; // Allprops request $propFind = new PropFindAll($path); $properties = $this->server->getPropertiesByNode($propFind, $node); $properties = $propFind->getResultForMultiStatus()[200]; foreach ($properties as $propName => $propValue) { if (!in_array($propName, $this->uninterestingProperties)) { $html .= $this->drawPropertyRow($propName, $propValue); } } $html .= "</table>"; $html .= "</section>"; /* Start of generating actions */ $output = ''; if ($this->enablePost) { $this->server->emit('onHTMLActionsPanel', [$node, &$output]); } if ($output) { $html .= "<section><h1>Actions</h1>"; $html .= "<div class=\"actions\">\n"; $html .= $output; $html .= "</div>\n"; $html .= "</section>\n"; } $html .= $this->generateFooter(); $this->server->httpResponse->setHeader('Content-Security-Policy', "img-src 'self'; style-src 'self';"); return $html; }
/** * Generates the html directory index for a given url * * @param string $path * @return string */ function generateDirectoryIndex($path) { $version = ''; if (DAV\Server::$exposeVersion) { $version = DAV\Version::VERSION; } $vars = ['path' => $this->escapeHTML($path), 'favicon' => $this->escapeHTML($this->getAssetUrl('favicon.ico')), 'style' => $this->escapeHTML($this->getAssetUrl('sabredav.css')), 'iconstyle' => $this->escapeHTML($this->getAssetUrl('openiconic/open-iconic.css')), 'logo' => $this->escapeHTML($this->getAssetUrl('sabredav.png')), 'baseUrl' => $this->server->getBaseUri()]; $html = <<<HTML <!DOCTYPE html> <html> <head> <title>{$vars['path']}/ - sabre/dav {$version}</title> <link rel="shortcut icon" href="{$vars['favicon']}" type="image/vnd.microsoft.icon" /> <link rel="stylesheet" href="{$vars['style']}" type="text/css" /> <link rel="stylesheet" href="{$vars['iconstyle']}" type="text/css" /> </head> <body> <header> <div class="logo"> <a href="{$vars['baseUrl']}"><img src="{$vars['logo']}" alt="sabre/dav" /> {$vars['path']}/</a> </div> </header> <nav> HTML; // If the path is empty, there's no parent. if ($path) { list($parentUri) = URLUtil::splitPath($path); $fullPath = URLUtil::encodePath($this->server->getBaseUri() . $parentUri); $html .= '<a href="' . $fullPath . '" class="btn">⇤ Go to parent</a>'; } else { $html .= '<span class="btn disabled">⇤ Go to parent</span>'; } $html .= "</nav>"; $node = $this->server->tree->getNodeForPath($path); if ($node instanceof DAV\ICollection) { $html .= "<section><h1>Nodes</h1>\n"; $html .= "<table class=\"nodeTable\">"; $subNodes = $this->server->getPropertiesForChildren($path, ['{DAV:}displayname', '{DAV:}resourcetype', '{DAV:}getcontenttype', '{DAV:}getcontentlength', '{DAV:}getlastmodified']); foreach ($subNodes as $subPath => $subProps) { $subNode = $this->server->tree->getNodeForPath($subPath); $fullPath = URLUtil::encodePath($this->server->getBaseUri() . $subPath); list(, $displayPath) = URLUtil::splitPath($subPath); $subNodes[$subPath]['subNode'] = $subNode; $subNodes[$subPath]['fullPath'] = $fullPath; $subNodes[$subPath]['displayPath'] = $displayPath; } uasort($subNodes, [$this, 'compareNodes']); foreach ($subNodes as $subProps) { $type = ['string' => 'Unknown', 'icon' => 'cog']; if (isset($subProps['{DAV:}resourcetype'])) { $type = $this->mapResourceType($subProps['{DAV:}resourcetype']->getValue(), $subProps['subNode']); } $html .= '<tr>'; $html .= '<td class="nameColumn"><a href="' . $this->escapeHTML($subProps['fullPath']) . '"><span class="oi" data-glyph="' . $type['icon'] . '"></span> ' . $this->escapeHTML($subProps['displayPath']) . '</a></td>'; $html .= '<td class="typeColumn">' . $type['string'] . '</td>'; $html .= '<td>'; if (isset($subProps['{DAV:}getcontentlength'])) { $html .= $subProps['{DAV:}getcontentlength'] . ' bytes'; } $html .= '</td><td>'; if (isset($subProps['{DAV:}getlastmodified'])) { $lastMod = $subProps['{DAV:}getlastmodified']->getTime(); $html .= $lastMod->format('F j, Y, g:i a'); } $html .= '</td></tr>'; } $html .= '</table>'; } $html .= "</section>"; $html .= "<section><h1>Properties</h1>"; $html .= "<table class=\"propTable\">"; // Allprops request $propFind = new PropFindAll($path); $properties = $this->server->getPropertiesByNode($propFind, $node); $properties = $propFind->getResultForMultiStatus()[200]; foreach ($properties as $propName => $propValue) { $html .= $this->drawPropertyRow($propName, $propValue); } $html .= "</table>"; $html .= "</section>"; /* Start of generating actions */ $output = ''; if ($this->enablePost) { $this->server->emit('onHTMLActionsPanel', [$node, &$output]); } if ($output) { $html .= "<section><h1>Actions</h1>"; $html .= "<div class=\"actions\">\n"; $html .= $output; $html .= "</div>\n"; $html .= "</section>\n"; } $html .= "\n <footer>Generated by SabreDAV " . $version . " (c)2007-2014 <a href=\"http://sabre.io/\">http://sabre.io/</a></footer>\n </body>\n </html>"; $this->server->httpResponse->setHeader('Content-Security-Policy', "img-src 'self'; style-src 'self';"); return $html; }