summaryrefslogtreecommitdiffstats
path: root/3rdparty/Sabre/DAV/Browser/Plugin.php
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/Sabre/DAV/Browser/Plugin.php')
-rwxr-xr-x[-rw-r--r--]3rdparty/Sabre/DAV/Browser/Plugin.php488
1 files changed, 346 insertions, 142 deletions
diff --git a/3rdparty/Sabre/DAV/Browser/Plugin.php b/3rdparty/Sabre/DAV/Browser/Plugin.php
index cd5617babb1..09bbdd2ae02 100644..100755
--- a/3rdparty/Sabre/DAV/Browser/Plugin.php
+++ b/3rdparty/Sabre/DAV/Browser/Plugin.php
@@ -6,77 +6,126 @@
* This plugin provides a html representation, so that a WebDAV server may be accessed
* using a browser.
*
- * The class intercepts GET requests to collection resources and generates a simple
- * html index.
- *
+ * The class intercepts GET requests to collection resources and generates a simple
+ * html index.
+ *
* @package Sabre
* @subpackage DAV
- * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @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_Browser_Plugin extends Sabre_DAV_ServerPlugin {
/**
- * reference to server class
- *
- * @var Sabre_DAV_Server
+ * List of default icons for nodes.
+ *
+ * This is an array with class / interface names as keys, and asset names
+ * as values.
+ *
+ * The evaluation order is reversed. The last item in the list gets
+ * precendence.
+ *
+ * @var array
+ */
+ public $iconMap = array(
+ 'Sabre_DAV_IFile' => 'icons/file',
+ 'Sabre_DAV_ICollection' => 'icons/collection',
+ 'Sabre_DAVACL_IPrincipal' => 'icons/principal',
+ 'Sabre_CalDAV_ICalendar' => 'icons/calendar',
+ 'Sabre_CardDAV_IAddressBook' => 'icons/addressbook',
+ 'Sabre_CardDAV_ICard' => 'icons/card',
+ );
+
+ /**
+ * The file extension used for all icons
+ *
+ * @var string
+ */
+ public $iconExtension = '.png';
+
+ /**
+ * reference to server class
+ *
+ * @var Sabre_DAV_Server
*/
protected $server;
/**
- * enableEditing
- *
- * @var bool
+ * enablePost turns on the 'actions' panel, which allows people to create
+ * folders and upload files straight from a browser.
+ *
+ * @var bool
*/
protected $enablePost = true;
/**
+ * By default the browser plugin will generate a favicon and other images.
+ * To turn this off, set this property to false.
+ *
+ * @var bool
+ */
+ protected $enableAssets = true;
+
+ /**
* Creates the object.
*
* By default it will allow file creation and uploads.
* Specify the first argument as false to disable this
- *
- * @param bool $enablePost
- * @return void
+ *
+ * @param bool $enablePost
+ * @param bool $enableAssets
*/
- public function __construct($enablePost=true) {
+ public function __construct($enablePost=true, $enableAssets = true) {
- $this->enablePost = $enablePost;
+ $this->enablePost = $enablePost;
+ $this->enableAssets = $enableAssets;
}
/**
- * Initializes the plugin and subscribes to events
- *
- * @param Sabre_DAV_Server $server
+ * Initializes the plugin and subscribes to events
+ *
+ * @param Sabre_DAV_Server $server
* @return void
*/
public function initialize(Sabre_DAV_Server $server) {
$this->server = $server;
$this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor'));
+ $this->server->subscribeEvent('onHTMLActionsPanel', array($this, 'htmlActionsPanel'),200);
if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler'));
}
/**
- * This method intercepts GET requests to collections and returns the html
- *
- * @param string $method
- * @return bool
+ * This method intercepts GET requests to collections and returns the html
+ *
+ * @param string $method
+ * @param string $uri
+ * @return bool
*/
public function httpGetInterceptor($method, $uri) {
- if ($method!='GET') return true;
+ if ($method !== 'GET') return true;
+
+ // We're not using straight-up $_GET, because we want everything to be
+ // unit testable.
+ $getVars = array();
+ parse_str($this->server->httpRequest->getQueryString(), $getVars);
+
+ if (isset($getVars['sabreAction']) && $getVars['sabreAction'] === 'asset' && isset($getVars['assetName'])) {
+ $this->serveAsset($getVars['assetName']);
+ return false;
+ }
- try {
+ try {
$node = $this->server->tree->getNodeForPath($uri);
- } catch (Sabre_DAV_Exception_FileNotFound $e) {
- // We're simply stopping when the file isn't found to not interfere
+ } catch (Sabre_DAV_Exception_NotFound $e) {
+ // We're simply stopping when the file isn't found to not interfere
// with other plugins.
return;
}
- if ($node instanceof Sabre_DAV_IFile)
+ if ($node instanceof Sabre_DAV_IFile)
return;
$this->server->httpResponse->sendStatus(200);
@@ -87,57 +136,71 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
);
return false;
-
+
}
/**
- * Handles POST requests for tree operations
- *
- * This method is not yet used.
- *
- * @param string $method
+ * Handles POST requests for tree operations.
+ *
+ * @param string $method
+ * @param string $uri
* @return bool
*/
public function httpPOSTHandler($method, $uri) {
- if ($method!='POST') return true;
- if (isset($_POST['sabreAction'])) switch($_POST['sabreAction']) {
+ if ($method!='POST') return;
+ $contentType = $this->server->httpRequest->getHeader('Content-Type');
+ list($contentType) = explode(';', $contentType);
+ if ($contentType !== 'application/x-www-form-urlencoded' &&
+ $contentType !== 'multipart/form-data') {
+ return;
+ }
+ $postVars = $this->server->httpRequest->getPostVars();
- case 'mkcol' :
- if (isset($_POST['name']) && trim($_POST['name'])) {
- // Using basename() because we won't allow slashes
- list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($_POST['name']));
- $this->server->createDirectory($uri . '/' . $folderName);
- }
- break;
- case 'put' :
- if ($_FILES) $file = current($_FILES);
- else break;
- $newName = trim($file['name']);
- list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name']));
- if (isset($_POST['name']) && trim($_POST['name']))
- $newName = trim($_POST['name']);
-
- // Making sure we only have a 'basename' component
- list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName);
-
-
- if (is_uploaded_file($file['tmp_name'])) {
- $parent = $this->server->tree->getNodeForPath(trim($uri,'/'));
- $parent->createFile($newName,fopen($file['tmp_name'],'r'));
- }
+ if (!isset($postVars['sabreAction']))
+ return;
+
+ if ($this->server->broadcastEvent('onBrowserPostAction', array($uri, $postVars['sabreAction'], $postVars))) {
+
+ switch($postVars['sabreAction']) {
+
+ case 'mkcol' :
+ if (isset($postVars['name']) && trim($postVars['name'])) {
+ // Using basename() because we won't allow slashes
+ list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($postVars['name']));
+ $this->server->createDirectory($uri . '/' . $folderName);
+ }
+ break;
+ case 'put' :
+ if ($_FILES) $file = current($_FILES);
+ else break;
+
+ list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name']));
+ if (isset($postVars['name']) && trim($postVars['name']))
+ $newName = trim($postVars['name']);
+
+ // Making sure we only have a 'basename' component
+ list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName);
+
+ if (is_uploaded_file($file['tmp_name'])) {
+ $this->server->createFile($uri . '/' . $newName, fopen($file['tmp_name'],'r'));
+ }
+ break;
+
+ }
}
$this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri());
+ $this->server->httpResponse->sendStatus(302);
return false;
}
/**
- * Escapes a string for html.
- *
- * @param string $value
- * @return void
+ * Escapes a string for html.
+ *
+ * @param string $value
+ * @return string
*/
public function escapeHTML($value) {
@@ -146,118 +209,199 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
}
/**
- * Generates the html directory index for a given url
+ * Generates the html directory index for a given url
*
- * @param string $path
- * @return string
+ * @param string $path
+ * @return string
*/
public function generateDirectoryIndex($path) {
+ $version = '';
+ if (Sabre_DAV_Server::$exposeVersion) {
+ $version = Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY;
+ }
+
$html = "<html>
<head>
- <title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . Sabre_DAV_Version::VERSION . "</title>
- <style type=\"text/css\"> body { Font-family: arial}</style>
-</head>
+ <title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . $version . "</title>
+ <style type=\"text/css\">
+ body { Font-family: arial}
+ h1 { font-size: 150% }
+ </style>
+ ";
+
+ if ($this->enableAssets) {
+ $html.='<link rel="shortcut icon" href="'.$this->getAssetUrl('favicon.ico').'" type="image/vnd.microsoft.icon" />';
+ }
+
+ $html .= "</head>
<body>
<h1>Index for " . $this->escapeHTML($path) . "/</h1>
<table>
- <tr><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>
- <tr><td colspan=\"4\"><hr /></td></tr>";
-
- $files = $this->server->getPropertiesForPath($path,array(
- '{DAV:}displayname',
- '{DAV:}resourcetype',
- '{DAV:}getcontenttype',
- '{DAV:}getcontentlength',
- '{DAV:}getlastmodified',
- ),1);
+ <tr><th width=\"24\"></th><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>
+ <tr><td colspan=\"5\"><hr /></td></tr>";
- $parent = $this->server->tree->getNodeForPath($path);
+ $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) = Sabre_DAV_URLUtil::splitPath($path);
- $fullPath = Sabre_DAV_URLUtil::encodePath($this->server->getBaseUri() . $parentUri);
+ if ($path) {
- $html.= "<tr>
-<td><a href=\"{$fullPath}\">..</a></td>
-<td>[parent]</td>
-<td></td>
-<td></td>
-</tr>";
+ list($parentUri) = Sabre_DAV_URLUtil::splitPath($path);
+ $fullPath = Sabre_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>
+ <td>$icon</td>
+ <td><a href=\"{$fullPath}\">..</a></td>
+ <td>[parent]</td>
+ <td></td>
+ <td></td>
+ </tr>";
- foreach($files as $k=>$file) {
+ }
- // This is the current directory, we can skip it
- if (rtrim($file['href'],'/')==$path) continue;
+ foreach($files as $file) {
+
+ // This is the current directory, we can skip it
+ if (rtrim($file['href'],'/')==$path) continue;
+
+ list(, $name) = Sabre_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;
+ }
- list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']);
+ }
+ $type = implode(', ', $type);
+ }
- $type = null;
+ // 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):'';
- if (isset($file[200]['{DAV:}resourcetype'])) {
- $type = $file[200]['{DAV:}resourcetype']->getValue();
+ $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/'));
- // resourcetype can have multiple values
- if (!is_array($type)) $type = array($type);
+ $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name;
- foreach($type as $k=>$v) {
+ $displayName = $this->escapeHTML($displayName);
+ $type = $this->escapeHTML($type);
- // 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';
+ $icon = '';
+
+ if ($this->enableAssets) {
+ $node = $parent->getChild($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;
+ }
+
+
}
}
- $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'];
+ $html.= "<tr>
+ <td>$icon</td>
+ <td><a href=\"{$fullPath}\">{$displayName}</a></td>
+ <td>{$type}</td>
+ <td>{$size}</td>
+ <td>{$lastmodified}</td>
+ </tr>";
+
}
- 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):'';
+ $html.= "<tr><td colspan=\"5\"><hr /></td></tr>";
+
+ $output = '';
- $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/'));
+ if ($this->enablePost) {
+ $this->server->broadcastEvent('onHTMLActionsPanel',array($parent, &$output));
+ }
- $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name;
+ $html.=$output;
- $name = $this->escapeHTML($name);
- $displayName = $this->escapeHTML($displayName);
- $type = $this->escapeHTML($type);
+ $html.= "</table>
+ <address>Generated by SabreDAV " . $version . " (c)2007-2012 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>
+ </body>
+ </html>";
- $html.= "<tr>
-<td><a href=\"{$fullPath}\">{$displayName}</a></td>
-<td>{$type}</td>
-<td>{$size}</td>
-<td>{$lastmodified}</td>
-</tr>";
+ return $html;
}
- $html.= "<tr><td colspan=\"4\"><hr /></td></tr>";
+ /**
+ * This method is used to generate the 'actions panel' output for
+ * collections.
+ *
+ * This specifically generates the interfaces for creating new files, and
+ * creating new directories.
+ *
+ * @param Sabre_DAV_INode $node
+ * @param mixed $output
+ * @return void
+ */
+ public function htmlActionsPanel(Sabre_DAV_INode $node, &$output) {
+
+ if (!$node instanceof Sabre_DAV_ICollection)
+ return;
+
+ // We also know fairly certain that if an object is a non-extended
+ // SimpleCollection, we won't need to show the panel either.
+ if (get_class($node)==='Sabre_DAV_SimpleCollection')
+ return;
- if ($this->enablePost && $parent instanceof Sabre_DAV_ICollection) {
- $html.= '<tr><td><form method="post" action="">
+ $output.= '<tr><td colspan="2"><form method="post" action="">
<h3>Create new folder</h3>
<input type="hidden" name="sabreAction" value="mkcol" />
Name: <input type="text" name="name" /><br />
@@ -270,15 +414,75 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
File: <input type="file" name="file" /><br />
<input type="submit" value="upload" />
</form>
- </td></tr>';
- }
+ </td></tr>';
+
+ }
- $html.= "</table>
- <address>Generated by SabreDAV " . Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY . " (c)2007-2011 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>
-</body>
-</html>";
+ /**
+ * This method takes a path/name of an asset and turns it into url
+ * suiteable for http access.
+ *
+ * @param string $assetName
+ * @return string
+ */
+ protected function getAssetUrl($assetName) {
- return $html;
+ return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName);
+
+ }
+
+ /**
+ * This method returns a local pathname to an asset.
+ *
+ * @param string $assetName
+ * @return string
+ */
+ protected function getLocalAssetPath($assetName) {
+
+ // Making sure people aren't trying to escape from the base path.
+ $assetSplit = explode('/', $assetName);
+ if (in_array('..',$assetSplit)) {
+ throw new Sabre_DAV_Exception('Incorrect asset path');
+ }
+ $path = __DIR__ . '/assets/' . $assetName;
+ return $path;
+
+ }
+
+ /**
+ * This method reads an asset from disk and generates a full http response.
+ *
+ * @param string $assetName
+ * @return void
+ */
+ protected function serveAsset($assetName) {
+
+ $assetPath = $this->getLocalAssetPath($assetName);
+ if (!file_exists($assetPath)) {
+ throw new Sabre_DAV_Exception_NotFound('Could not find an asset with this name');
+ }
+ // Rudimentary mime type detection
+ switch(strtolower(substr($assetPath,strpos($assetPath,'.')+1))) {
+
+ case 'ico' :
+ $mime = 'image/vnd.microsoft.icon';
+ break;
+
+ case 'png' :
+ $mime = 'image/png';
+ break;
+
+ default:
+ $mime = 'application/octet-stream';
+ break;
+
+ }
+
+ $this->server->httpResponse->setHeader('Content-Type', $mime);
+ $this->server->httpResponse->setHeader('Content-Length', filesize($assetPath));
+ $this->server->httpResponse->setHeader('Cache-Control', 'public, max-age=1209600');
+ $this->server->httpResponse->sendStatus(200);
+ $this->server->httpResponse->sendBody(fopen($assetPath,'r'));
}