/** * 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 */ public function unlockNode($uri, LockInfo $lockInfo) { if (!$this->server->broadcastEvent('beforeUnlock', array($uri, $lockInfo))) { return; } if ($this->locksBackend) { return $this->locksBackend->unlock($uri, $lockInfo); } }
function testBrowserPostAction() { $r = $this->server->broadcastEvent('onBrowserPostAction', array('calendars/user1', 'mkcalendar', array('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'); } }
/** * 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 string $uri * @return void */ protected function httpPatch($uri) { // Get the node. Will throw a 404 if not found $node = $this->server->tree->getNodeForPath($uri); if (!$node instanceof IFile) { throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.'); } $range = $this->getHTTPUpdateRange(); if (!$range) { throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers'); } $contentType = strtolower($this->server->httpRequest->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'); // Load the begin and end data $start = $range[0] ? $range[0] : 0; $end = $range[1] ? $range[1] : $len - 1; // Check consistency if ($end < $start) { throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); } if ($end - $start + 1 != $len) { throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[0] . ') and end (' . $range[1] . ') offsets'); } // Checking If-None-Match and related headers. if (!$this->server->checkPreconditions()) { return; } if (!$this->server->broadcastEvent('beforeWriteContent', array($uri, $node, null))) { return; } $body = $this->server->httpRequest->getBody(); $etag = $node->putRange($body, $start - 1); $this->server->broadcastEvent('afterWriteContent', array($uri, $node)); $this->server->httpResponse->setHeader('Content-Length', '0'); if ($etag) { $this->server->httpResponse->setHeader('ETag', $etag); } $this->server->httpResponse->sendStatus(204); return false; }
/** * Generates the html directory index for a given url * * @param string $path * @return string */ public function generateDirectoryIndex($path) { $version = ''; if (DAV\Server::$exposeVersion) { $version = DAV\Version::VERSION . "-" . DAV\Version::STABILITY; } $html = "<html>\n<head>\n <title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . $version . "</title>\n <style type=\"text/css\">\n body { Font-family: arial}\n h1 { font-size: 150% }\n </style>\n "; if ($this->enableAssets) { $html .= '<link rel="shortcut icon" href="' . $this->getAssetUrl('favicon.ico') . '" type="image/vnd.microsoft.icon" />'; } $html .= "</head>\n<body>\n <h1>Index for " . $this->escapeHTML($path) . "/</h1>\n <table>\n <tr><th width=\"24\"></th><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>\n <tr><td colspan=\"5\"><hr /></td></tr>"; $files = $this->server->getPropertiesForPath($path, array('{DAV:}displayname', '{DAV:}resourcetype', '{DAV:}getcontenttype', '{DAV:}getcontentlength', '{DAV:}getlastmodified'), 1); $parent = $this->server->tree->getNodeForPath($path); if ($path) { list($parentUri) = DAV\URLUtil::splitPath($path); $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); $icon = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="Parent" /></a>' : ''; $html .= "<tr>\n <td>{$icon}</td>\n <td><a href=\"{$fullPath}\">..</a></td>\n <td>[parent]</td>\n <td></td>\n <td></td>\n </tr>"; } foreach ($files as $file) { // This is the current directory, we can skip it if (rtrim($file['href'], '/') == $path) { continue; } list(, $name) = DAV\URLUtil::splitPath($file['href']); $type = null; if (isset($file[200]['{DAV:}resourcetype'])) { $type = $file[200]['{DAV:}resourcetype']->getValue(); // resourcetype can have multiple values if (!is_array($type)) { $type = array($type); } foreach ($type as $k => $v) { // Some name mapping is preferred switch ($v) { case '{DAV:}collection': $type[$k] = 'Collection'; break; case '{DAV:}principal': $type[$k] = 'Principal'; break; case '{urn:ietf:params:xml:ns:carddav}addressbook': $type[$k] = 'Addressbook'; break; case '{urn:ietf:params:xml:ns:caldav}calendar': $type[$k] = 'Calendar'; break; case '{urn:ietf:params:xml:ns:caldav}schedule-inbox': $type[$k] = 'Schedule Inbox'; break; case '{urn:ietf:params:xml:ns:caldav}schedule-outbox': $type[$k] = 'Schedule Outbox'; break; case '{http://calendarserver.org/ns/}calendar-proxy-read': $type[$k] = 'Proxy-Read'; break; case '{http://calendarserver.org/ns/}calendar-proxy-write': $type[$k] = 'Proxy-Write'; break; } } $type = implode(', ', $type); } // If no resourcetype was found, we attempt to use // the contenttype property if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { $type = $file[200]['{DAV:}getcontenttype']; } if (!$type) { $type = 'Unknown'; } $size = isset($file[200]['{DAV:}getcontentlength']) ? (int) $file[200]['{DAV:}getcontentlength'] : ''; $lastmodified = isset($file[200]['{DAV:}getlastmodified']) ? $file[200]['{DAV:}getlastmodified']->getTime()->format(\DateTime::ATOM) : ''; $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; $displayName = $this->escapeHTML($displayName); $type = $this->escapeHTML($type); $icon = ''; if ($this->enableAssets) { $node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name); foreach (array_reverse($this->iconMap) as $class => $iconName) { if ($node instanceof $class) { $icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24" /></a>'; break; } } } $html .= "<tr>\n <td>{$icon}</td>\n <td><a href=\"{$fullPath}\">{$displayName}</a></td>\n <td>{$type}</td>\n <td>{$size}</td>\n <td>{$lastmodified}</td>\n </tr>"; } $html .= "<tr><td colspan=\"5\"><hr /></td></tr>"; $output = ''; if ($this->enablePost) { $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); } $html .= $output; $html .= "</table>\n <address>Generated by SabreDAV " . $version . " (c)2007-2014 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>\n </body>\n </html>"; return $html; }
/** * 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 string $uri * @return void */ protected function httpPatch($uri) { // Get the node. Will throw a 404 if not found $node = $this->server->tree->getNodeForPath($uri); if (!$node instanceof IFile && !$node instanceof IPatchSupport) { throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.'); } $range = $this->getHTTPUpdateRange(); if (!$range) { throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers'); } $contentType = strtolower($this->server->httpRequest->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; } // Checking If-None-Match and related headers. if (!$this->server->checkPreconditions()) { return; } if (!$this->server->broadcastEvent('beforeWriteContent', array($uri, $node, null))) { return; } $body = $this->server->httpRequest->getBody(); if ($node instanceof IPatchSupport) { $etag = $node->patch($body, $range[0], isset($range[1]) ? $range[1] : null); } else { // The old interface switch ($range[0]) { case self::RANGE_APPEND: throw new DAV\Exception\NotImplemented('This node does not support the append syntax. Please upgrade it to IPatchSupport'); case self::RANGE_START: $etag = $node->putRange($body, $range[1]); break; case self::RANGE_END: throw new DAV\Exception\NotImplemented('This node does not support the end-range syntax. Please upgrade it to IPatchSupport'); break; } } $this->server->broadcastEvent('afterWriteContent', array($uri, $node)); $this->server->httpResponse->setHeader('Content-Length', '0'); if ($etag) { $this->server->httpResponse->setHeader('ETag', $etag); } $this->server->httpResponse->sendStatus(204); return false; }