summaryrefslogtreecommitdiffstats
path: root/3rdparty/Sabre/DAV/Server.php
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/Sabre/DAV/Server.php')
-rwxr-xr-x[-rw-r--r--]3rdparty/Sabre/DAV/Server.php506
1 files changed, 282 insertions, 224 deletions
diff --git a/3rdparty/Sabre/DAV/Server.php b/3rdparty/Sabre/DAV/Server.php
index 3d76d4f1918..4284c127b6e 100644..100755
--- a/3rdparty/Sabre/DAV/Server.php
+++ b/3rdparty/Sabre/DAV/Server.php
@@ -2,11 +2,11 @@
/**
* Main DAV server class
- *
+ *
* @package Sabre
* @subpackage DAV
- * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
- * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
*/
class Sabre_DAV_Server {
@@ -26,9 +26,6 @@ class Sabre_DAV_Server {
*/
const NODE_DIRECTORY = 2;
- const PROP_SET = 1;
- const PROP_REMOVE = 2;
-
/**
* XML namespace for all SabreDAV related elements
*/
@@ -36,42 +33,42 @@ class Sabre_DAV_Server {
/**
* The tree object
- *
- * @var Sabre_DAV_Tree
+ *
+ * @var Sabre_DAV_Tree
*/
public $tree;
/**
- * The base uri
- *
- * @var string
+ * The base uri
+ *
+ * @var string
*/
- protected $baseUri = null;
+ protected $baseUri = null;
/**
- * httpResponse
- *
- * @var Sabre_HTTP_Response
+ * httpResponse
+ *
+ * @var Sabre_HTTP_Response
*/
public $httpResponse;
/**
* httpRequest
- *
- * @var Sabre_HTTP_Request
+ *
+ * @var Sabre_HTTP_Request
*/
public $httpRequest;
/**
- * The list of plugins
- *
- * @var array
+ * The list of plugins
+ *
+ * @var array
*/
protected $plugins = array();
/**
- * This array contains a list of callbacks we should call when certain events are triggered
- *
+ * This array contains a list of callbacks we should call when certain events are triggered
+ *
* @var array
*/
protected $eventSubscriptions = array();
@@ -81,7 +78,7 @@ class Sabre_DAV_Server {
*
* If you are defining your own custom namespace, add it here to reduce
* bandwidth and improve legibility of xml bodies.
- *
+ *
* @var array
*/
public $xmlNamespaces = array(
@@ -90,9 +87,9 @@ class Sabre_DAV_Server {
);
/**
- * The propertymap can be used to map properties from
+ * The propertymap can be used to map properties from
* requests to property classes.
- *
+ *
* @var array
*/
public $propertyMap = array(
@@ -125,23 +122,32 @@ class Sabre_DAV_Server {
* This is a flag that allow or not showing file, line and code
* of the exception in the returned XML
*
- * @var bool
+ * @var bool
*/
public $debugExceptions = false;
/**
- * This property allows you to automatically add the 'resourcetype' value
+ * This property allows you to automatically add the 'resourcetype' value
* based on a node's classname or interface.
*
- * The preset ensures that {DAV:}collection is automaticlly added for nodes
+ * The preset ensures that {DAV:}collection is automaticlly added for nodes
* implementing Sabre_DAV_ICollection.
- *
+ *
* @var array
*/
public $resourceTypeMapping = array(
'Sabre_DAV_ICollection' => '{DAV:}collection',
);
+ /**
+ * If this setting is turned off, SabreDAV's version number will be hidden
+ * from various places.
+ *
+ * Some people feel this is a good security measure.
+ *
+ * @var bool
+ */
+ static public $exposeVersion = true;
/**
* Sets up the server
@@ -150,14 +156,13 @@ class Sabre_DAV_Server {
* use it as the directory tree. If a Sabre_DAV_INode is passed, it
* will create a Sabre_DAV_ObjectTree and use the node as the root.
*
- * If nothing is passed, a Sabre_DAV_SimpleCollection is created in
+ * If nothing is passed, a Sabre_DAV_SimpleCollection is created in
* a Sabre_DAV_ObjectTree.
*
* If an array is passed, we automatically create a root node, and use
- * the nodes in the array as top-level children.
- *
- * @param Sabre_DAV_Tree $tree The tree object
- * @return void
+ * the nodes in the array as top-level children.
+ *
+ * @param Sabre_DAV_Tree|Sabre_DAV_INode|null $treeOrNode The tree object
*/
public function __construct($treeOrNode = null) {
@@ -190,7 +195,7 @@ class Sabre_DAV_Server {
}
/**
- * Starts the DAV Server
+ * Starts the DAV Server
*
* @return void
*/
@@ -218,7 +223,9 @@ class Sabre_DAV_Server {
$error->appendChild($DOM->createElement('s:stacktrace',$e->getTraceAsString()));
}
- $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION));
+ if (self::$exposeVersion) {
+ $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION));
+ }
if($e instanceof Sabre_DAV_Exception) {
@@ -233,7 +240,7 @@ class Sabre_DAV_Server {
}
$headers['Content-Type'] = 'application/xml; charset=utf-8';
-
+
$this->httpResponse->sendStatus($httpCode);
$this->httpResponse->setHeaders($headers);
$this->httpResponse->sendBody($DOM->saveXML());
@@ -244,24 +251,24 @@ class Sabre_DAV_Server {
/**
* Sets the base server uri
- *
+ *
* @param string $uri
* @return void
*/
public function setBaseUri($uri) {
// If the baseUri does not end with a slash, we must add it
- if ($uri[strlen($uri)-1]!=='/')
+ if ($uri[strlen($uri)-1]!=='/')
$uri.='/';
- $this->baseUri = $uri;
+ $this->baseUri = $uri;
}
/**
* Returns the base responding uri
- *
- * @return string
+ *
+ * @return string
*/
public function getBaseUri() {
@@ -272,11 +279,11 @@ class Sabre_DAV_Server {
/**
* This method attempts to detect the base uri.
- * Only the PATH_INFO variable is considered.
- *
- * If this variable is not set, the root (/) is assumed.
+ * Only the PATH_INFO variable is considered.
*
- * @return void
+ * If this variable is not set, the root (/) is assumed.
+ *
+ * @return string
*/
public function guessBaseUri() {
@@ -303,21 +310,21 @@ class Sabre_DAV_Server {
return rtrim($baseUri,'/') . '/';
}
- throw new Sabre_DAV_Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.');
+ throw new Sabre_DAV_Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.');
- }
+ }
- // The last fallback is that we're just going to assume the server root.
+ // The last fallback is that we're just going to assume the server root.
return '/';
}
/**
* Adds a plugin to the server
- *
+ *
* For more information, console the documentation of Sabre_DAV_ServerPlugin
*
- * @param Sabre_DAV_ServerPlugin $plugin
+ * @param Sabre_DAV_ServerPlugin $plugin
* @return void
*/
public function addPlugin(Sabre_DAV_ServerPlugin $plugin) {
@@ -333,11 +340,11 @@ class Sabre_DAV_Server {
* This function returns null if the plugin was not found.
*
* @param string $name
- * @return Sabre_DAV_ServerPlugin
+ * @return Sabre_DAV_ServerPlugin
*/
public function getPlugin($name) {
- if (isset($this->plugins[$name]))
+ if (isset($this->plugins[$name]))
return $this->plugins[$name];
// This is a fallback and deprecated.
@@ -350,9 +357,9 @@ class Sabre_DAV_Server {
}
/**
- * Returns all plugins
- *
- * @return array
+ * Returns all plugins
+ *
+ * @return array
*/
public function getPlugins() {
@@ -361,7 +368,6 @@ class Sabre_DAV_Server {
}
-
/**
* Subscribe to an event.
*
@@ -371,9 +377,9 @@ class Sabre_DAV_Server {
*
* This is for example used to make sure that the authentication plugin
* is triggered before anything else. If it's not needed to change this
- * number, it is recommended to ommit.
- *
- * @param string $event
+ * number, it is recommended to ommit.
+ *
+ * @param string $event
* @param callback $callback
* @param int $priority
* @return void
@@ -398,7 +404,7 @@ class Sabre_DAV_Server {
*
* @param string $eventName
* @param array $arguments
- * @return bool
+ * @return bool
*/
public function broadcastEvent($eventName,$arguments = array()) {
@@ -418,7 +424,7 @@ class Sabre_DAV_Server {
}
/**
- * Handles a http request, and execute a method based on its name
+ * Handles a http request, and execute a method based on its name
*
* @param string $method
* @param string $uri
@@ -426,7 +432,7 @@ class Sabre_DAV_Server {
*/
public function invokeMethod($method, $uri) {
- $method = strtoupper($method);
+ $method = strtoupper($method);
if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return;
@@ -453,7 +459,7 @@ class Sabre_DAV_Server {
if ($this->broadcastEvent('unknownMethod',array($method, $uri))) {
// Unsupported method
- throw new Sabre_DAV_Exception_NotImplemented();
+ throw new Sabre_DAV_Exception_NotImplemented('There was no handler found for this "' . $method . '" method');
}
}
@@ -461,9 +467,9 @@ class Sabre_DAV_Server {
}
// {{{ HTTP Method implementations
-
+
/**
- * HTTP OPTIONS
+ * HTTP OPTIONS
*
* @param string $uri
* @return void
@@ -476,11 +482,13 @@ class Sabre_DAV_Server {
$features = array('1','3', 'extended-mkcol');
foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
-
+
$this->httpResponse->setHeader('DAV',implode(', ',$features));
$this->httpResponse->setHeader('MS-Author-Via','DAV');
$this->httpResponse->setHeader('Accept-Ranges','bytes');
- $this->httpResponse->setHeader('X-Sabre-Version',Sabre_DAV_Version::VERSION);
+ if (self::$exposeVersion) {
+ $this->httpResponse->setHeader('X-Sabre-Version',Sabre_DAV_Version::VERSION);
+ }
$this->httpResponse->setHeader('Content-Length',0);
$this->httpResponse->sendStatus(200);
@@ -492,13 +500,13 @@ class Sabre_DAV_Server {
* This method simply fetches the contents of a uri, like normal
*
* @param string $uri
- * @return void
+ * @return bool
*/
protected function httpGet($uri) {
$node = $this->tree->getNodeForPath($uri,0);
- if (!$this->checkPreconditions(true)) return false;
+ if (!$this->checkPreconditions(true)) return false;
if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects');
$body = $node->get();
@@ -535,7 +543,7 @@ class Sabre_DAV_Server {
} else {
$nodeSize = null;
}
-
+
$this->httpResponse->setHeaders($httpHeaders);
$range = $this->getHTTPRange();
@@ -545,12 +553,12 @@ class Sabre_DAV_Server {
// If ifRange is set, and range is specified, we first need to check
// the precondition.
if ($nodeSize && $range && $ifRange) {
-
+
// if IfRange is parsable as a date we'll treat it as a DateTime
// otherwise, we must treat it as an etag.
try {
$ifRangeDate = new DateTime($ifRange);
-
+
// It's a date. We must check if the entity is modified since
// the specified date.
if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true;
@@ -560,8 +568,8 @@ class Sabre_DAV_Server {
}
} catch (Exception $e) {
-
- // It's an entity. We can do a simple comparison.
+
+ // It's an entity. We can do a simple comparison.
if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true;
elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true;
}
@@ -575,7 +583,7 @@ class Sabre_DAV_Server {
$start = $range[0];
$end = $range[1]?$range[1]:$nodeSize-1;
- if($start >= $nodeSize)
+ if($start >= $nodeSize)
throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')');
if($end < $start) throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
@@ -625,7 +633,7 @@ class Sabre_DAV_Server {
$node = $this->tree->getNodeForPath($uri);
/* This information is only collection for File objects.
- * Ideally we want to throw 405 Method Not Allowed for every
+ * Ideally we want to throw 405 Method Not Allowed for every
* non-file, but MS Office does not like this
*/
if ($node instanceof Sabre_DAV_IFile) {
@@ -640,7 +648,7 @@ class Sabre_DAV_Server {
}
/**
- * HTTP Delete
+ * HTTP Delete
*
* The HTTP delete method, deletes a given uri
*
@@ -651,6 +659,7 @@ class Sabre_DAV_Server {
if (!$this->broadcastEvent('beforeUnbind',array($uri))) return;
$this->tree->delete($uri);
+ $this->broadcastEvent('afterUnbind',array($uri));
$this->httpResponse->sendStatus(204);
$this->httpResponse->setHeader('Content-Length','0');
@@ -659,13 +668,13 @@ class Sabre_DAV_Server {
/**
- * WebDAV PROPFIND
+ * WebDAV PROPFIND
*
* This WebDAV method requests information about an uri resource, or a list of resources
* If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value
* If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory)
*
- * The request body contains an XML data structure that has a list of properties the client understands
+ * The request body contains an XML data structure that has a list of properties the client understands
* The response body is also an xml document, containing information about every uri resource and the requested properties
*
* It has to return a HTTP 207 Multi-status status code
@@ -679,7 +688,7 @@ class Sabre_DAV_Server {
$requestedProperties = $this->parsePropfindRequest($this->httpRequest->getBody(true));
$depth = $this->getHTTPDepth(1);
- // The only two options for the depth of a propfind is 0 or 1
+ // The only two options for the depth of a propfind is 0 or 1
if ($depth!=0) $depth = 1;
$newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth);
@@ -688,8 +697,8 @@ class Sabre_DAV_Server {
$this->httpResponse->sendStatus(207);
$this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
- // Normally this header is only needed for OPTIONS responses, however..
- // iCal seems to also depend on these being set for PROPFIND. Since
+ // Normally this header is only needed for OPTIONS responses, however..
+ // iCal seems to also depend on these being set for PROPFIND. Since
// this is not harmful, we'll add it.
$features = array('1','3', 'extended-mkcol');
foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
@@ -712,7 +721,7 @@ class Sabre_DAV_Server {
protected function httpPropPatch($uri) {
$newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true));
-
+
$result = $this->updateProperties($uri, $newProperties);
$this->httpResponse->sendStatus(207);
@@ -725,14 +734,14 @@ class Sabre_DAV_Server {
}
/**
- * HTTP PUT method
- *
+ * HTTP PUT method
+ *
* This HTTP method updates a file, or creates a new one.
*
- * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 200 Ok
+ * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 204 No Content
*
* @param string $uri
- * @return void
+ * @return bool
*/
protected function httpPut($uri) {
@@ -768,13 +777,13 @@ class Sabre_DAV_Server {
// Intercepting the Finder problem
if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
-
+
/**
- Many webservers will not cooperate well with Finder PUT requests,
+ Many webservers will not cooperate well with Finder PUT requests,
because it uses 'Chunked' transfer encoding for the request body.
- The symptom of this problem is that Finder sends files to the
- server, but they arrive as 0-lenght files in PHP.
+ The symptom of this problem is that Finder sends files to the
+ server, but they arrive as 0-length files in PHP.
If we don't do anything, the user might think they are uploading
files successfully, but they end up empty on the server. Instead,
@@ -808,29 +817,36 @@ class Sabre_DAV_Server {
}
- if ($this->tree->nodeExists($uri)) {
+ if ($this->tree->nodeExists($uri)) {
$node = $this->tree->getNodeForPath($uri);
-
+
// Checking If-None-Match and related headers.
if (!$this->checkPreconditions()) return;
-
+
// If the node is a collection, we'll deny it
if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_Conflict('PUT is not allowed on non-files.');
- if (!$this->broadcastEvent('beforeWriteContent',array($this->getRequestUri()))) return false;
+ if (!$this->broadcastEvent('beforeWriteContent',array($uri, $node, &$body))) return false;
+
+ $etag = $node->put($body);
+
+ $this->broadcastEvent('afterWriteContent',array($uri, $node));
- $node->put($body);
$this->httpResponse->setHeader('Content-Length','0');
+ if ($etag) $this->httpResponse->setHeader('ETag',$etag);
$this->httpResponse->sendStatus(204);
} else {
+ $etag = null;
// If we got here, the resource didn't exist yet.
- if (!$this->createFile($this->getRequestUri(),$body)) {
+ if (!$this->createFile($this->getRequestUri(),$body,$etag)) {
// For one reason or another the file was not created.
return;
}
+
$this->httpResponse->setHeader('Content-Length','0');
+ if ($etag) $this->httpResponse->setHeader('ETag', $etag);
$this->httpResponse->sendStatus(201);
}
@@ -855,7 +871,7 @@ class Sabre_DAV_Server {
$contentType = $this->httpRequest->getHeader('Content-Type');
if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) {
- // We must throw 415 for unsupport mkcol bodies
+ // We must throw 415 for unsupported mkcol bodies
throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
}
@@ -863,7 +879,7 @@ class Sabre_DAV_Server {
$dom = Sabre_DAV_XMLUtil::loadDOMDocument($requestBody);
if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') {
- // We must throw 415 for unsupport mkcol bodies
+ // We must throw 415 for unsupported mkcol bodies
throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.');
}
@@ -875,7 +891,7 @@ class Sabre_DAV_Server {
$properties = array_merge($properties, Sabre_DAV_XMLUtil::parseProperties($childNode, $this->propertyMap));
}
- if (!isset($properties['{DAV:}resourcetype']))
+ if (!isset($properties['{DAV:}resourcetype']))
throw new Sabre_DAV_Exception_BadRequest('The mkcol request must include a {DAV:}resourcetype property');
$resourceType = $properties['{DAV:}resourcetype']->getValue();
@@ -918,19 +934,21 @@ class Sabre_DAV_Server {
$moveInfo = $this->getCopyAndMoveInfo();
// If the destination is part of the source tree, we must fail
- if ($moveInfo['destination']==$uri)
+ if ($moveInfo['destination']==$uri)
throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.');
if ($moveInfo['destinationExists']) {
if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false;
$this->tree->delete($moveInfo['destination']);
+ $this->broadcastEvent('afterUnbind',array($moveInfo['destination']));
}
if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false;
if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false;
$this->tree->move($uri,$moveInfo['destination']);
+ $this->broadcastEvent('afterUnbind',array($uri));
$this->broadcastEvent('afterBind',array($moveInfo['destination']));
// If a resource was overwritten we should send a 204, otherwise a 201
@@ -946,13 +964,13 @@ class Sabre_DAV_Server {
* A lot of the actual request processing is done in getCopyMoveInfo
*
* @param string $uri
- * @return void
+ * @return bool
*/
protected function httpCopy($uri) {
$copyInfo = $this->getCopyAndMoveInfo();
// If the destination is part of the source tree, we must fail
- if ($copyInfo['destination']==$uri)
+ if ($copyInfo['destination']==$uri)
throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.');
if ($copyInfo['destinationExists']) {
@@ -998,13 +1016,13 @@ class Sabre_DAV_Server {
}
// }}}
- // {{{ HTTP/WebDAV protocol helpers
+ // {{{ HTTP/WebDAV protocol helpers
/**
- * Returns an array with all the supported HTTP methods for a specific uri.
+ * Returns an array with all the supported HTTP methods for a specific uri.
*
- * @param string $uri
- * @return array
+ * @param string $uri
+ * @return array
*/
public function getAllowedMethods($uri) {
@@ -1023,13 +1041,13 @@ class Sabre_DAV_Server {
// The MKCOL is only allowed on an unmapped uri
try {
- $node = $this->tree->getNodeForPath($uri);
- } catch (Sabre_DAV_Exception_FileNotFound $e) {
+ $this->tree->getNodeForPath($uri);
+ } catch (Sabre_DAV_Exception_NotFound $e) {
$methods[] = 'MKCOL';
}
// We're also checking if any of the plugins register any new methods
- foreach($this->plugins as $plugin) $methods = array_merge($methods,$plugin->getHTTPMethods($uri));
+ foreach($this->plugins as $plugin) $methods = array_merge($methods, $plugin->getHTTPMethods($uri));
array_unique($methods);
return $methods;
@@ -1037,8 +1055,8 @@ class Sabre_DAV_Server {
}
/**
- * Gets the uri for the request, keeping the base uri into consideration
- *
+ * Gets the uri for the request, keeping the base uri into consideration
+ *
* @return string
*/
public function getRequestUri() {
@@ -1048,9 +1066,9 @@ class Sabre_DAV_Server {
}
/**
- * Calculates the uri for a request, making sure that the base uri is stripped out
- *
- * @param string $uri
+ * Calculates the uri for a request, making sure that the base uri is stripped out
+ *
+ * @param string $uri
* @throws Sabre_DAV_Exception_Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri
* @return string
*/
@@ -1068,9 +1086,9 @@ class Sabre_DAV_Server {
return trim(Sabre_DAV_URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/');
- // A special case, if the baseUri was accessed without a trailing
- // slash, we'll accept it as well.
- } elseif ($uri.'/' === $this->getBaseUri()) {
+ // A special case, if the baseUri was accessed without a trailing
+ // slash, we'll accept it as well.
+ } elseif ($uri.'/' === $this->getBaseUri()) {
return '';
@@ -1086,10 +1104,10 @@ class Sabre_DAV_Server {
* Returns the HTTP depth header
*
* This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre_DAV_Server::DEPTH_INFINITY object
- * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existant
- *
- * @param mixed $default
- * @return int
+ * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent
+ *
+ * @param mixed $default
+ * @return int
*/
public function getHTTPDepth($default = self::DEPTH_INFINITY) {
@@ -1100,7 +1118,7 @@ class Sabre_DAV_Server {
if ($depth == 'infinity') return self::DEPTH_INFINITY;
-
+
// If its an unknown value. we'll grab the default
if (!ctype_digit($depth)) return $default;
@@ -1118,14 +1136,14 @@ class Sabre_DAV_Server {
* The second number is the offset of the last byte in the range.
*
* If the second offset is null, it should be treated as the offset of the last byte of the entity
- * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity
+ * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity
*
- * return $mixed
+ * @return array|null
*/
public function getHTTPRange() {
$range = $this->httpRequest->getHeader('range');
- if (is_null($range)) return null;
+ if (is_null($range)) return null;
// Matching "Range: bytes=1234-5678: both numbers are optional
@@ -1143,15 +1161,15 @@ class Sabre_DAV_Server {
/**
* Returns information about Copy and Move requests
- *
- * This function is created to help getting information about the source and the destination for the
- * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions
- *
+ *
+ * This function is created to help getting information about the source and the destination for the
+ * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions
+ *
* The returned value is an array with the following keys:
* * destination - Destination path
- * * destinationExists - Wether or not the destination is an existing url (and should therefore be overwritten)
+ * * destinationExists - Whether or not the destination is an existing url (and should therefore be overwritten)
*
- * @return array
+ * @return array
*/
public function getCopyAndMoveInfo() {
@@ -1170,7 +1188,7 @@ class Sabre_DAV_Server {
try {
$destinationParent = $this->tree->getNodeForPath($destinationDir);
if (!($destinationParent instanceof Sabre_DAV_ICollection)) throw new Sabre_DAV_Exception_UnsupportedMediaType('The destination node is not a collection');
- } catch (Sabre_DAV_Exception_FileNotFound $e) {
+ } catch (Sabre_DAV_Exception_NotFound $e) {
// If the destination parent node is not found, we throw a 409
throw new Sabre_DAV_Exception_Conflict('The destination node is not found');
@@ -1179,12 +1197,12 @@ class Sabre_DAV_Server {
try {
$destinationNode = $this->tree->getNodeForPath($destination);
-
+
// If this succeeded, it means the destination already exists
// we'll need to throw precondition failed in case overwrite is false
if (!$overwrite) throw new Sabre_DAV_Exception_PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite');
- } catch (Sabre_DAV_Exception_FileNotFound $e) {
+ } catch (Sabre_DAV_Exception_NotFound $e) {
// Destination didn't exist, we're all good
$destinationNode = false;
@@ -1220,22 +1238,50 @@ class Sabre_DAV_Server {
}
/**
+ * A kid-friendly way to fetch properties for a node's children.
+ *
+ * The returned array will be indexed by the path of the of child node.
+ * Only properties that are actually found will be returned.
+ *
+ * The parent node will not be returned.
+ *
+ * @param string $path
+ * @param array $propertyNames
+ * @return array
+ */
+ public function getPropertiesForChildren($path, $propertyNames) {
+
+ $result = array();
+ foreach($this->getPropertiesForPath($path,$propertyNames,1) as $k=>$row) {
+
+ // Skipping the parent path
+ if ($k === 0) continue;
+
+ $result[$row['href']] = $row[200];
+
+ }
+ return $result;
+
+ }
+
+ /**
* Returns a list of HTTP headers for a particular resource
*
- * The generated http headers are based on properties provided by the
+ * The generated http headers are based on properties provided by the
* resource. The method basically provides a simple mapping between
* DAV property and HTTP header.
*
* The headers are intended to be used for HEAD and GET requests.
- *
+ *
* @param string $path
+ * @return array
*/
public function getHTTPHeaders($path) {
$propertyMap = array(
'{DAV:}getcontenttype' => 'Content-Type',
'{DAV:}getcontentlength' => 'Content-Length',
- '{DAV:}getlastmodified' => 'Last-Modified',
+ '{DAV:}getlastmodified' => 'Last-Modified',
'{DAV:}getetag' => 'ETag',
);
@@ -1245,40 +1291,40 @@ class Sabre_DAV_Server {
foreach($propertyMap as $property=>$header) {
if (!isset($properties[$property])) continue;
- if (is_scalar($properties[$property])) {
+ if (is_scalar($properties[$property])) {
$headers[$header] = $properties[$property];
- // GetLastModified gets special cased
+ // GetLastModified gets special cased
} elseif ($properties[$property] instanceof Sabre_DAV_Property_GetLastModified) {
- $headers[$header] = $properties[$property]->getTime()->format(DateTime::RFC1123);
+ $headers[$header] = Sabre_HTTP_Util::toHTTPDate($properties[$property]->getTime());
}
}
return $headers;
-
+
}
/**
* Returns a list of properties for a given path
- *
+ *
* The path that should be supplied should have the baseUrl stripped out
* The list of properties should be supplied in Clark notation. If the list is empty
* 'allprops' is assumed.
*
* If a depth of 1 is requested child elements will also be returned.
*
- * @param string $path
+ * @param string $path
* @param array $propertyNames
- * @param int $depth
+ * @param int $depth
* @return array
*/
- public function getPropertiesForPath($path,$propertyNames = array(),$depth = 0) {
+ public function getPropertiesForPath($path, $propertyNames = array(), $depth = 0) {
if ($depth!=0) $depth = 1;
$returnPropertyList = array();
-
+
$parentNode = $this->tree->getNodeForPath($path);
$nodes = array(
$path => $parentNode
@@ -1286,11 +1332,11 @@ class Sabre_DAV_Server {
if ($depth==1 && $parentNode instanceof Sabre_DAV_ICollection) {
foreach($this->tree->getChildren($path) as $childNode)
$nodes[$path . '/' . $childNode->getName()] = $childNode;
- }
-
+ }
+
// If the propertyNames array is empty, it means all properties are requested.
// We shouldn't actually return everything we know though, and only return a
- // sensible list.
+ // sensible list.
$allProperties = count($propertyNames)==0;
foreach($nodes as $myPath=>$node) {
@@ -1315,8 +1361,8 @@ class Sabre_DAV_Server {
);
}
- // If the resourceType was not part of the list, we manually add it
- // and mark it for removal. We need to know the resourcetype in order
+ // If the resourceType was not part of the list, we manually add it
+ // and mark it for removal. We need to know the resourcetype in order
// to make certain decisions about the entry.
// WebDAV dictates we should add a / and the end of href's for collections
$removeRT = false;
@@ -1326,32 +1372,39 @@ class Sabre_DAV_Server {
}
$result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties));
- // If this method explicitly returned false, we must ignore this
- // node as it is inacessible.
+ // If this method explicitly returned false, we must ignore this
+ // node as it is inaccessible.
if ($result===false) continue;
if (count($currentPropertyNames) > 0) {
- if ($node instanceof Sabre_DAV_IProperties)
+ if ($node instanceof Sabre_DAV_IProperties)
$newProperties['200'] = $newProperties[200] + $node->getProperties($currentPropertyNames);
}
foreach($currentPropertyNames as $prop) {
-
+
if (isset($newProperties[200][$prop])) continue;
switch($prop) {
case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); break;
- case '{DAV:}getcontentlength' : if ($node instanceof Sabre_DAV_IFile) $newProperties[200][$prop] = (int)$node->getSize(); break;
- case '{DAV:}quota-used-bytes' :
+ case '{DAV:}getcontentlength' :
+ if ($node instanceof Sabre_DAV_IFile) {
+ $size = $node->getSize();
+ if (!is_null($size)) {
+ $newProperties[200][$prop] = (int)$node->getSize();
+ }
+ }
+ break;
+ case '{DAV:}quota-used-bytes' :
if ($node instanceof Sabre_DAV_IQuota) {
$quotaInfo = $node->getQuotaInfo();
$newProperties[200][$prop] = $quotaInfo[0];
}
break;
- case '{DAV:}quota-available-bytes' :
+ case '{DAV:}quota-available-bytes' :
if ($node instanceof Sabre_DAV_IQuota) {
$quotaInfo = $node->getQuotaInfo();
$newProperties[200][$prop] = $quotaInfo[1];
@@ -1364,7 +1417,7 @@ class Sabre_DAV_Server {
foreach($this->plugins as $plugin) {
$reports = array_merge($reports, $plugin->getSupportedReportSet($myPath));
}
- $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet($reports);
+ $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet($reports);
break;
case '{DAV:}resourcetype' :
$newProperties[200]['{DAV:}resourcetype'] = new Sabre_DAV_Property_ResourceType();
@@ -1379,14 +1432,14 @@ class Sabre_DAV_Server {
if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null;
}
-
+
$this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties));
- $newProperties['href'] = trim($myPath,'/');
+ $newProperties['href'] = trim($myPath,'/');
// Its is a WebDAV recommendation to add a trailing slash to collectionnames.
// Apple's iCal also requires a trailing slash for principals (rfc 3744).
- // Therefore we add a trailing / for any non-file. This might need adjustments
+ // Therefore we add a trailing / for any non-file. This might need adjustments
// if we find there are other edge cases.
if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype']) && count($newProperties[200]['{DAV:}resourcetype']->getValue())>0) $newProperties['href'] .='/';
@@ -1397,7 +1450,7 @@ class Sabre_DAV_Server {
$returnPropertyList[] = $newProperties;
}
-
+
return $returnPropertyList;
}
@@ -1406,27 +1459,31 @@ class Sabre_DAV_Server {
* This method is invoked by sub-systems creating a new file.
*
* Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin).
- * It was important to get this done through a centralized function,
+ * It was important to get this done through a centralized function,
* allowing plugins to intercept this using the beforeCreateFile event.
*
* This method will return true if the file was actually created
- *
- * @param string $uri
- * @param resource $data
- * @return bool
+ *
+ * @param string $uri
+ * @param resource $data
+ * @param string $etag
+ * @return bool
*/
- public function createFile($uri,$data) {
+ public function createFile($uri,$data, &$etag = null) {
list($dir,$name) = Sabre_DAV_URLUtil::splitPath($uri);
if (!$this->broadcastEvent('beforeBind',array($uri))) return false;
- if (!$this->broadcastEvent('beforeCreateFile',array($uri,$data))) return false;
$parent = $this->tree->getNodeForPath($dir);
- $parent->createFile($name,$data);
+
+ if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false;
+
+ $etag = $parent->createFile($name,$data);
$this->tree->markDirty($dir);
$this->broadcastEvent('afterBind',array($uri));
+ $this->broadcastEvent('afterCreateFile',array($uri, $parent));
return true;
}
@@ -1434,7 +1491,7 @@ class Sabre_DAV_Server {
/**
* This method is invoked by sub-systems creating a new directory.
*
- * @param string $uri
+ * @param string $uri
* @return void
*/
public function createDirectory($uri) {
@@ -1447,14 +1504,14 @@ class Sabre_DAV_Server {
* Use this method to create a new collection
*
* The {DAV:}resourcetype is specified using the resourceType array.
- * At the very least it must contain {DAV:}collection.
+ * At the very least it must contain {DAV:}collection.
*
* The properties array can contain a list of additional properties.
- *
- * @param string $uri The new uri
- * @param array $resourceType The resourceType(s)
+ *
+ * @param string $uri The new uri
+ * @param array $resourceType The resourceType(s)
* @param array $properties A list of properties
- * @return void
+ * @return array|null
*/
public function createCollection($uri, array $resourceType, array $properties) {
@@ -1471,7 +1528,7 @@ class Sabre_DAV_Server {
$parent = $this->tree->getNodeForPath($parentUri);
- } catch (Sabre_DAV_Exception_FileNotFound $e) {
+ } catch (Sabre_DAV_Exception_NotFound $e) {
throw new Sabre_DAV_Exception_Conflict('Parent node does not exist');
@@ -1491,14 +1548,14 @@ class Sabre_DAV_Server {
// If we got here.. it means there's already a node on that url, and we need to throw a 405
throw new Sabre_DAV_Exception_MethodNotAllowed('The resource you tried to create already exists');
- } catch (Sabre_DAV_Exception_FileNotFound $e) {
+ } catch (Sabre_DAV_Exception_NotFound $e) {
// This is correct
}
-
+
if (!$this->broadcastEvent('beforeBind',array($uri))) return;
- // There are 2 modes of operation. The standard collection
+ // There are 2 modes of operation. The standard collection
// creates the directory, and then updates properties
// the extended collection can create it directly.
if ($parent instanceof Sabre_DAV_IExtendedCollection) {
@@ -1513,7 +1570,7 @@ class Sabre_DAV_Server {
}
$parent->createDirectory($newName);
- $rollBack = false;
+ $rollBack = false;
$exception = null;
$errorResult = null;
@@ -1544,7 +1601,7 @@ class Sabre_DAV_Server {
return $errorResult;
}
-
+
}
$this->tree->markDirty($parentUri);
$this->broadcastEvent('afterBind',array($uri));
@@ -1557,29 +1614,29 @@ class Sabre_DAV_Server {
* The properties array must be a list of properties. Array-keys are
* property names in clarknotation, array-values are it's values.
* If a property must be deleted, the value should be null.
- *
- * Note that this request should either completely succeed, or
+ *
+ * Note that this request should either completely succeed, or
* completely fail.
*
* The response is an array with statuscodes for keys, which in turn
* contain arrays with propertynames. This response can be used
* to generate a multistatus body.
- *
- * @param string $uri
- * @param array $properties
- * @return array
+ *
+ * @param string $uri
+ * @param array $properties
+ * @return array
*/
public function updateProperties($uri, array $properties) {
// we'll start by grabbing the node, this will throw the appropriate
- // exceptions if it doesn't.
+ // exceptions if it doesn't.
$node = $this->tree->getNodeForPath($uri);
-
+
$result = array(
200 => array(),
403 => array(),
424 => array(),
- );
+ );
$remainingProperties = $properties;
$hasError = false;
@@ -1684,14 +1741,15 @@ class Sabre_DAV_Server {
* the appropriate HTTP response headers are already set.
*
* Normally this method will throw 412 Precondition Failed for failures
- * related to If-None-Match, If-Match and If-Unmodified Since. It will
+ * related to If-None-Match, If-Match and If-Unmodified Since. It will
* set the status to 304 Not Modified for If-Modified_since.
*
- * If the $handleAsGET argument is set to true, it will also return 304
+ * If the $handleAsGET argument is set to true, it will also return 304
* Not Modified for failure of the If-None-Match precondition. This is the
* desired behaviour for HTTP GET and HTTP HEAD requests.
*
- * @return bool
+ * @param bool $handleAsGET
+ * @return bool
*/
public function checkPreconditions($handleAsGET = false) {
@@ -1708,7 +1766,7 @@ class Sabre_DAV_Server {
// request succeed if a resource exists at that url.
try {
$node = $this->tree->getNodeForPath($uri);
- } catch (Sabre_DAV_Exception_FileNotFound $e) {
+ } catch (Sabre_DAV_Exception_NotFound $e) {
throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match');
}
@@ -1722,7 +1780,7 @@ class Sabre_DAV_Server {
// Stripping any extra spaces
$ifMatchItem = trim($ifMatchItem,' ');
-
+
$etag = $node->getETag();
if ($etag===$ifMatchItem) {
$haveMatch = true;
@@ -1744,7 +1802,7 @@ class Sabre_DAV_Server {
if (!$node) {
try {
$node = $this->tree->getNodeForPath($uri);
- } catch (Sabre_DAV_Exception_FileNotFound $e) {
+ } catch (Sabre_DAV_Exception_NotFound $e) {
$nodeExists = false;
}
}
@@ -1758,10 +1816,10 @@ class Sabre_DAV_Server {
$etag = $node->getETag();
foreach($ifNoneMatch as $ifNoneMatchItem) {
-
+
// Stripping any extra spaces
$ifNoneMatchItem = trim($ifNoneMatchItem,' ');
-
+
if ($etag===$ifNoneMatchItem) $haveMatch = true;
}
@@ -1781,7 +1839,7 @@ class Sabre_DAV_Server {
}
if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) {
-
+
// The If-Modified-Since header contains a date. We
// will only return the entity if it has been changed since
// that date. If it hasn't been changed, we return a 304
@@ -1799,23 +1857,24 @@ class Sabre_DAV_Server {
$lastMod = new DateTime('@' . $lastMod);
if ($lastMod <= $date) {
$this->httpResponse->sendStatus(304);
+ $this->httpResponse->setHeader('Last-Modified', Sabre_HTTP_Util::toHTTPDate($lastMod));
return false;
- }
+ }
}
}
}
if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) {
-
+
// The If-Unmodified-Since will allow allow the request if the
// entity has not changed since the specified date.
$date = Sabre_HTTP_Util::parseHTTPDate($ifUnmodifiedSince);
-
+
// We must only check the date if it's valid
if ($date) {
if (is_null($node)) {
$node = $this->tree->getNodeForPath($uri);
- }
+ }
$lastMod = $node->getLastModified();
if ($lastMod) {
$lastMod = new DateTime('@' . $lastMod);
@@ -1830,16 +1889,15 @@ class Sabre_DAV_Server {
}
- // }}}
- // {{{ XML Readers & Writers
-
-
+ // }}}
+ // {{{ XML Readers & Writers
+
+
/**
- * Generates a WebDAV propfind response body based on a list of nodes
- *
+ * Generates a WebDAV propfind response body based on a list of nodes
+ *
* @param array $fileProperties The list with nodes
- * @param array $requestedProperties The properties that should be returned
- * @return string
+ * @return string
*/
public function generateMultiStatus(array $fileProperties) {
@@ -1859,7 +1917,7 @@ class Sabre_DAV_Server {
$href = $entry['href'];
unset($entry['href']);
-
+
$response = new Sabre_DAV_Property_Response($href,$entry);
$response->serialize($this,$multiStatus);
@@ -1878,7 +1936,7 @@ class Sabre_DAV_Server {
* The keys in the returned array contain the property name (e.g.: {DAV:}displayname,
* and the value contains the property value. If a property is to be removed the value
* will be null.
- *
+ *
* @param string $body xml body
* @return array list of properties in need of updating or deletion
*/
@@ -1886,17 +1944,17 @@ class Sabre_DAV_Server {
//We'll need to change the DAV namespace declaration to something else in order to make it parsable
$dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
-
+
$newProperties = array();
foreach($dom->firstChild->childNodes as $child) {
- if ($child->nodeType !== XML_ELEMENT_NODE) continue;
+ if ($child->nodeType !== XML_ELEMENT_NODE) continue;
$operation = Sabre_DAV_XMLUtil::toClarkNotation($child);
if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue;
-
+
$innerProperties = Sabre_DAV_XMLUtil::parseProperties($child, $this->propertyMap);
foreach($innerProperties as $propertyName=>$propertyValue) {
@@ -1920,9 +1978,9 @@ class Sabre_DAV_Server {
*
* This will either be a list of properties, or an empty array; in which case
* an {DAV:}allprop was requested.
- *
- * @param string $body
- * @return array
+ *
+ * @param string $body
+ * @return array
*/
public function parsePropFindRequest($body) {
@@ -1931,7 +1989,7 @@ class Sabre_DAV_Server {
$dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
$elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0);
- return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem));
+ return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem));
}