* Fix issues with brackets in directory names

* Update dependencies
This commit is contained in:
bohwaz 2022-11-15 13:10:52 +01:00
parent 49b4ec839c
commit 97cbdd15c9
4 changed files with 82 additions and 14 deletions

View file

@ -43,6 +43,7 @@ class ErrorManager
*/ */
const PRODUCTION = 1; const PRODUCTION = 1;
const DEVELOPMENT = 2; const DEVELOPMENT = 2;
const CLI_DEVELOPMENT = 4;
/** /**
* Term colors * Term colors
@ -269,11 +270,22 @@ class ErrorManager
$html_report = null; $html_report = null;
if (self::$enabled != self::PRODUCTION || self::$email_errors) { if (self::$enabled & self::DEVELOPMENT || self::$email_errors) {
$html_report = self::htmlReport($report); $html_report = self::htmlReport($report);
} }
if (PHP_SAPI == 'cli' || 0 === strpos($_SERVER['HTTP_USER_AGENT'] ?? '', 'curl/')) $is_curl = 0 === strpos($_SERVER['HTTP_USER_AGENT'] ?? '', 'curl/');
$is_cli = PHP_SAPI == 'cli';
if (!$is_cli) {
http_response_code(500);
}
if ($is_curl && !headers_sent()) {
header('Content-Type: text/plain; charset=utf-8', true);
}
if (($is_cli || $is_curl) && (self::$enabled & self::DEVELOPMENT || self::$enabled & self::CLI_DEVELOPMENT))
{ {
foreach ($report->errors as $e) foreach ($report->errors as $e)
{ {
@ -315,7 +327,11 @@ class ErrorManager
} }
} }
} }
else if (self::$enabled == self::PRODUCTION) else if (($is_cli || $is_curl) && self::$enabled & self::PRODUCTION) {
self::termPrint(' /!\\ An internal server error occurred ', self::RED);
self::termPrint(' Error reference was: ' . $report->context->id, self::YELLOW);
}
else if (self::$enabled & self::PRODUCTION)
{ {
self::htmlProduction($report); self::htmlProduction($report);
} }
@ -520,7 +536,7 @@ class ErrorManager
'date' => date(DATE_ATOM), 'date' => date(DATE_ATOM),
'os' => PHP_OS, 'os' => PHP_OS,
'language' => 'PHP ' . PHP_VERSION, 'language' => 'PHP ' . PHP_VERSION,
'environment' => self::$enabled == self::DEVELOPMENT ? 'development' : 'production:' . self::$enabled, 'environment' => self::$enabled & self::DEVELOPMENT ? 'development' : 'production:' . self::$enabled,
'php_sapi' => PHP_SAPI, 'php_sapi' => PHP_SAPI,
'remote_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null, 'remote_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null,
'http_method' => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : null, 'http_method' => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : null,
@ -718,6 +734,8 @@ class ErrorManager
/** /**
* Enable error manager * Enable error manager
* @param integer $type Type of error management (ErrorManager::PRODUCTION or ErrorManager::DEVELOPMENT) * @param integer $type Type of error management (ErrorManager::PRODUCTION or ErrorManager::DEVELOPMENT)
* You can also use ErrorManager::PRODUCTION | ErrorManager::CLI_DEVELOPMENT to get error messages in CLI but still hide errors
* on web front-end.
* @return void * @return void
*/ */
static public function enable($type = self::DEVELOPMENT) static public function enable($type = self::DEVELOPMENT)
@ -734,9 +752,9 @@ class ErrorManager
ini_set('log_errors', false); ini_set('log_errors', false);
ini_set('html_errors', false); ini_set('html_errors', false);
ini_set('zend.exception_ignore_args', false); // We want to get the args in exceptions (since PHP 7.4) ini_set('zend.exception_ignore_args', false); // We want to get the args in exceptions (since PHP 7.4)
error_reporting($type == self::DEVELOPMENT ? -1 : E_ALL & ~E_DEPRECATED & ~E_STRICT); error_reporting($type & self::DEVELOPMENT ? -1 : E_ALL & ~E_DEPRECATED & ~E_STRICT);
if ($type == self::DEVELOPMENT && PHP_SAPI != 'cli') if ($type & self::DEVELOPMENT && PHP_SAPI != 'cli')
{ {
self::setHtmlHeader('<!DOCTYPE html><meta charset="utf-8" /><style type="text/css"> self::setHtmlHeader('<!DOCTYPE html><meta charset="utf-8" /><style type="text/css">
body { font-family: sans-serif; } * { margin: 0; padding: 0; } body { font-family: sans-serif; } * { margin: 0; padding: 0; }
@ -760,7 +778,7 @@ class ErrorManager
set_error_handler([__CLASS__, 'errorHandler']); set_error_handler([__CLASS__, 'errorHandler']);
if ($type == self::DEVELOPMENT) if ($type & self::DEVELOPMENT)
{ {
self::startTimer('_global'); self::startTimer('_global');
} }

View file

@ -63,6 +63,7 @@ abstract class NextCloud
self::PROP_NC_DDC, self::PROP_NC_DDC,
]; ];
protected string $prefix = '';
protected string $root_url; protected string $root_url;
protected Server $server; protected Server $server;
protected AbstractStorage $storage; protected AbstractStorage $storage;
@ -326,6 +327,8 @@ abstract class NextCloud
{ {
$this->requireAuth(); $this->requireAuth();
$base_uri = null;
// Find out which route we are using and replace URI // Find out which route we are using and replace URI
foreach (self::ROUTES as $route => $method) { foreach (self::ROUTES as $route => $method) {
if ($method != 'webdav') { if ($method != 'webdav') {
@ -338,6 +341,10 @@ abstract class NextCloud
} }
} }
if (!$base_uri) {
throw new Exception('Invalid WebDAV URL', 404);
}
// Android app is using "/remote.php/dav/files/user//" as root // Android app is using "/remote.php/dav/files/user//" as root
// so let's alias that as well // so let's alias that as well
// ownCloud Android is requesting just /dav/files/ // ownCloud Android is requesting just /dav/files/
@ -345,6 +352,8 @@ abstract class NextCloud
$base_uri = $match[0]; $base_uri = $match[0];
} }
$this->server->prefix = $this->prefix;
$this->server->setBaseURI($base_uri); $this->server->setBaseURI($base_uri);
$this->server->route($uri); $this->server->route($uri);
} }

View file

@ -92,6 +92,11 @@ class Server
*/ */
public string $original_uri; public string $original_uri;
/**
* Prefix, if you want to force some clients inside a path, eg. '/documents/'
*/
public string $prefix = '';
protected AbstractStorage $storage; protected AbstractStorage $storage;
public function setStorage(AbstractStorage $storage) public function setStorage(AbstractStorage $storage)
@ -109,6 +114,15 @@ class Server
$this->base_uri = rtrim($uri, '/') . '/'; $this->base_uri = rtrim($uri, '/') . '/';
} }
protected function _prefix(string $uri): string
{
if (!$this->prefix) {
return $uri;
}
return rtrim(rtrim($this->prefix, '/') . '/' . ltrim($uri, '/'), '/');
}
protected function html_directory(string $uri, iterable $list): ?string protected function html_directory(string $uri, iterable $list): ?string
{ {
// Not a file: let's serve a directory listing if you are browsing with a web browser // Not a file: let's serve a directory listing if you are browsing with a web browser
@ -187,6 +201,8 @@ class Server
throw new Exception('We can only delete to infinity', 400); throw new Exception('We can only delete to infinity', 400);
} }
$uri = $this->_prefix($uri);
$this->checkLock($uri); $this->checkLock($uri);
$this->storage->delete($uri); $this->storage->delete($uri);
@ -233,6 +249,8 @@ class Server
$hash = bin2hex(base64_decode($_SERVER['HTTP_CONTENT_MD5'])); $hash = bin2hex(base64_decode($_SERVER['HTTP_CONTENT_MD5']));
} }
$uri = $this->_prefix($uri);
$this->checkLock($uri); $this->checkLock($uri);
if (!empty($_SERVER['HTTP_IF_MATCH'])) { if (!empty($_SERVER['HTTP_IF_MATCH'])) {
@ -271,6 +289,8 @@ class Server
public function http_head(string $uri, array &$props = []): ?string public function http_head(string $uri, array &$props = []): ?string
{ {
$uri = $this->_prefix($uri);
$requested_props = self::BASIC_PROPERTIES; $requested_props = self::BASIC_PROPERTIES;
$requested_props[] = 'DAV::getetag'; $requested_props[] = 'DAV::getetag';
@ -325,6 +345,8 @@ class Server
$props = []; $props = [];
$this->http_head($uri, $props); $this->http_head($uri, $props);
$uri = $this->_prefix($uri);
$is_collection = !empty($props['DAV::resourcetype']) && $props['DAV::resourcetype'] == 'collection'; $is_collection = !empty($props['DAV::resourcetype']) && $props['DAV::resourcetype'] == 'collection';
$out = ''; $out = '';
@ -523,6 +545,8 @@ class Server
protected function _http_copymove(string $uri, string $method): ?string protected function _http_copymove(string $uri, string $method): ?string
{ {
$uri = $this->_prefix($uri);
$destination = $_SERVER['HTTP_DESTINATION'] ?? null; $destination = $_SERVER['HTTP_DESTINATION'] ?? null;
$depth = $_SERVER['HTTP_DEPTH'] ?? 1; $depth = $_SERVER['HTTP_DEPTH'] ?? 1;
@ -581,6 +605,7 @@ class Server
throw new Exception('Unsupported body for MKCOL', 415); throw new Exception('Unsupported body for MKCOL', 415);
} }
$uri = $this->_prefix($uri);
$this->storage->mkcol($uri); $this->storage->mkcol($uri);
http_response_code(201); http_response_code(201);
@ -656,6 +681,7 @@ class Server
// We only support depth of 0 and 1 // We only support depth of 0 and 1
$depth = isset($_SERVER['HTTP_DEPTH']) && empty($_SERVER['HTTP_DEPTH']) ? 0 : 1; $depth = isset($_SERVER['HTTP_DEPTH']) && empty($_SERVER['HTTP_DEPTH']) ? 0 : 1;
$uri = $this->_prefix($uri);
$body = file_get_contents('php://input'); $body = file_get_contents('php://input');
if (false !== strpos($body, '<!DOCTYPE ')) { if (false !== strpos($body, '<!DOCTYPE ')) {
@ -756,7 +782,12 @@ class Server
foreach ($items as $uri => $item) { foreach ($items as $uri => $item) {
$e = '<d:response>'; $e = '<d:response>';
$path = '/' . str_replace('%2F', '/', rawurlencode(trim($this->base_uri . $uri, '/'))); if ($this->prefix) {
$uri = substr($uri, strlen($this->prefix));
}
$uri = trim(rtrim($this->base_uri, '/') . '/' . ltrim($uri, '/'), '/');
$path = '/' . str_replace('%2F', '/', rawurlencode($uri));
if (($item['DAV::resourcetype'] ?? null) == 'collection') { if (($item['DAV::resourcetype'] ?? null) == 'collection') {
$path .= '/'; $path .= '/';
@ -929,6 +960,7 @@ class Server
public function http_proppatch(string $uri): ?string public function http_proppatch(string $uri): ?string
{ {
$uri = $this->_prefix($uri);
$this->checkLock($uri); $this->checkLock($uri);
$body = file_get_contents('php://input'); $body = file_get_contents('php://input');
@ -948,6 +980,7 @@ class Server
public function http_lock(string $uri): ?string public function http_lock(string $uri): ?string
{ {
$uri = $this->_prefix($uri);
// We don't use this currently, but maybe later? // We don't use this currently, but maybe later?
//$depth = !empty($this->_SERVER['HTTP_DEPTH']) ? 1 : 0; //$depth = !empty($this->_SERVER['HTTP_DEPTH']) ? 1 : 0;
//$timeout = isset($_SERVER['HTTP_TIMEOUT']) ? explode(',', $_SERVER['HTTP_TIMEOUT']) : []; //$timeout = isset($_SERVER['HTTP_TIMEOUT']) ? explode(',', $_SERVER['HTTP_TIMEOUT']) : [];
@ -1039,6 +1072,7 @@ class Server
public function http_unlock(string $uri): ?string public function http_unlock(string $uri): ?string
{ {
$uri = $this->_prefix($uri);
$token = $this->getLockToken(); $token = $this->getLockToken();
if (!$token) { if (!$token) {
@ -1169,6 +1203,7 @@ class Server
} }
$uri = substr($uri, strlen($this->base_uri)); $uri = substr($uri, strlen($this->base_uri));
$uri = $this->_prefix($uri);
return $uri; return $uri;
} }

View file

@ -51,11 +51,11 @@ class Storage extends AbstractStorage
public function list(string $uri, ?array $properties): iterable public function list(string $uri, ?array $properties): iterable
{ {
$dirs = glob($this->users->current()->path . $uri . '/*', \GLOB_ONLYDIR); $dirs = self::glob($this->users->current()->path . $uri, '/*', \GLOB_ONLYDIR);
$dirs = array_map('basename', $dirs); $dirs = array_map('basename', $dirs);
natcasesort($dirs); natcasesort($dirs);
$files = glob($this->users->current()->path . $uri . '/*'); $files = self::glob($this->users->current()->path . $uri, '/*');
$files = array_map('basename', $files); $files = array_map('basename', $files);
$files = array_diff($files, $dirs); $files = array_diff($files, $dirs);
natcasesort($files); natcasesort($files);
@ -291,7 +291,7 @@ class Storage extends AbstractStorage
} }
if (is_dir($target)) { if (is_dir($target)) {
foreach (glob($target . '/*') as $file) { foreach (self::glob($target, '/*') as $file) {
$this->delete(substr($file, strlen($this->users->current()->path))); $this->delete(substr($file, strlen($this->users->current()->path)));
} }
@ -421,12 +421,18 @@ class Storage extends AbstractStorage
return; return;
} }
static protected function glob(string $path, string $pattern = '', int $flags = 0): array
{
$path = preg_replace('/[\*\?\[\]]/', '\\\\$0', $path);
return glob($path . $pattern, $flags);
}
static public function getDirectorySize(string $path): int static public function getDirectorySize(string $path): int
{ {
$total = 0; $total = 0;
$path = rtrim($path, '/'); $path = rtrim($path, '/');
foreach (glob($path . '/*', GLOB_NOSORT) as $f) { foreach (self::glob($path, '/*', GLOB_NOSORT) as $f) {
if (is_dir($f)) { if (is_dir($f)) {
$total += self::getDirectorySize($f); $total += self::getDirectorySize($f);
} }
@ -440,7 +446,7 @@ class Storage extends AbstractStorage
static public function deleteDirectory(string $path): void static public function deleteDirectory(string $path): void
{ {
foreach (glob($path . '/*', GLOB_NOSORT) as $f) { foreach (self::glob($path, '/*', GLOB_NOSORT) as $f) {
if (is_dir($f)) { if (is_dir($f)) {
self::deleteDirectory($f); self::deleteDirectory($f);
@rmdir($f); @rmdir($f);
@ -458,7 +464,7 @@ class Storage extends AbstractStorage
$last = 0; $last = 0;
$path = rtrim($path, '/'); $path = rtrim($path, '/');
foreach (glob($path . '/*', GLOB_NOSORT) as $f) { foreach (self::glob($path, '/*', GLOB_NOSORT) as $f) {
if (is_dir($f)) { if (is_dir($f)) {
$m = self::getDirectoryMTime($f); $m = self::getDirectoryMTime($f);