diff options
Diffstat (limited to '3rdparty/Sabre/DAV/Browser/Plugin.php')
-rwxr-xr-x[-rw-r--r--] | 3rdparty/Sabre/DAV/Browser/Plugin.php | 488 |
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')); } |