Update dependencies

This commit is contained in:
bohwaz 2023-01-28 03:11:31 +01:00
parent 255e3a56d0
commit b9e765be83
5 changed files with 226 additions and 71 deletions

View file

@ -247,11 +247,8 @@ class ErrorManager
} }
} }
$report = self::makeReport($e); extract(self::buildExceptionReport($e, false));
$log = sprintf('=========== Error ref. %s ===========', $report->context->id) unset($e);
. PHP_EOL . PHP_EOL . (string) $e . PHP_EOL . PHP_EOL
. '<errorReport>' . PHP_EOL . json_encode($report, \JSON_PRETTY_PRINT)
. PHP_EOL . '</errorReport>' . PHP_EOL;
// Log exception to file // Log exception to file
if (ini_get('log_errors')) if (ini_get('log_errors'))
@ -259,21 +256,12 @@ class ErrorManager
error_log($log); error_log($log);
} }
$exception_title = $e->getMessage();
unset($e);
// Disable any output if it was buffering // Disable any output if it was buffering
if (ob_get_level()) if (ob_get_level())
{ {
ob_end_clean(); ob_end_clean();
} }
$html_report = null;
if (self::$enabled & self::DEVELOPMENT || self::$email_errors) {
$html_report = self::htmlReport($report);
}
$is_curl = 0 === strpos($_SERVER['HTTP_USER_AGENT'] ?? '', 'curl/'); $is_curl = 0 === strpos($_SERVER['HTTP_USER_AGENT'] ?? '', 'curl/');
$is_cli = PHP_SAPI == 'cli'; $is_cli = PHP_SAPI == 'cli';
@ -348,7 +336,7 @@ class ErrorManager
// Log exception to email // Log exception to email
if (self::$email_errors) { if (self::$email_errors) {
self::sendEmail($exception_title, $report, $log, $html_report); self::sendEmail($title, $report, $log, $html_report);
} }
// Send report to URL // Send report to URL
@ -362,6 +350,21 @@ class ErrorManager
} }
} }
static public function reportExceptionSilent(\Throwable $e): void
{
extract(self::buildExceptionReport($e));
// Log exception to file
if (ini_get('log_errors'))
{
error_log($log);
}
if (self::$email_errors) {
self::sendEmail($title, $report, $log, $html_report);
}
}
static protected function sendEmail(string $title, \stdClass $report, string $log, string $html): void static protected function sendEmail(string $title, \stdClass $report, string $log, string $html): void
{ {
// From: sender // From: sender
@ -419,6 +422,25 @@ class ErrorManager
return $file; return $file;
} }
static public function buildExceptionReport(\Throwable $e, bool $force_html = false): array
{
$report = self::makeReport($e);
$log = sprintf('=========== Error ref. %s ===========', $report->context->id)
. PHP_EOL . PHP_EOL . (string) $e . PHP_EOL . PHP_EOL
. '<errorReport>' . PHP_EOL . json_encode($report, \JSON_PRETTY_PRINT)
. PHP_EOL . '</errorReport>' . PHP_EOL;
$html_report = null;
if ($force_html || self::$enabled & self::DEVELOPMENT || self::$email_errors) {
$html_report = self::htmlReport($report);
}
$title = $e->getMessage();
return compact('report', 'log', 'html_report', 'title');
}
/** /**
* Generates a report from an exception * Generates a report from an exception
*/ */
@ -743,6 +765,7 @@ class ErrorManager
if (self::$enabled) if (self::$enabled)
return true; return true;
self::$context['request_started'] = $_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true);
self::$enabled = $type; self::$enabled = $type;
@ -800,8 +823,6 @@ class ErrorManager
self::$context[$a] = $_SERVER[$b]; self::$context[$a] = $_SERVER[$b];
} }
} }
self::$context['request_started'] = microtime(true);
} }
/** /**

View file

@ -175,6 +175,47 @@ abstract class NextCloud
abstract public function assembleChunks(string $login, string $name, string $target, ?int $mtime): array; abstract public function assembleChunks(string $login, string $name, string $target, ?int $mtime): array;
abstract public function listChunks(string $login, string $name): array;
/**
* Thumbnail API
* @param string $uri URI path to the file we want a thumbnail for
* @param int $width
* @param int $height
* @param bool $crop TRUE if a cropped image is desired
* @param bool $preview TRUE if the thumbnail is for preview (in NextCloud Android, see below)
*/
public function serveThumbnail(string $uri, int $width, int $height, bool $crop = false, bool $preview = false): void
{
if (!preg_match('/\.(?:jpe?g|gif|png|webp)$/', $uri)) {
http_response_code(404);
return;
}
// We don't support thumbnails, but you are free to generate cropped thumbnails and send them to the HTTP client
if (!$preview) {
http_response_code(404);
return;
}
// On Android, the app is annoying and asks to download the image
// every time ("no resized image available") if no preview is available.
// So to avoid that you can just redirect to the file if it's not too large
// But you are free to extend this method and resize the image on the fly instead.
else {
$size = $this->server->getStorage()->properties($uri, ['DAV::getcontentlength'], 0);
$size = count($size) ? current($size) : null;
if ($size > 1024*1024 || !$size) {
http_response_code(404);
return;
}
$url = '/remote.php/dav/files/' . $uri;
$this->server->log('=> Preview: redirect to %s', $url);
header('Location: ' . $url);
}
}
// END OF ABSTRACT METHODS // END OF ABSTRACT METHODS
/** /**
@ -187,6 +228,9 @@ abstract class NextCloud
'remote.php/webdav/uploads/' => 'chunked', 'remote.php/webdav/uploads/' => 'chunked',
'remote.php/dav/uploads/' => 'chunked', 'remote.php/dav/uploads/' => 'chunked',
// There's just 3 or 4 different endpoints for avatars, this is ridiculous
'remote.php/dav/avatars/' => 'avatar',
// Main routes // Main routes
'remote.php/webdav/' => 'webdav', // desktop client 'remote.php/webdav/' => 'webdav', // desktop client
'remote.php/dav' => 'webdav', // android client 'remote.php/dav' => 'webdav', // android client
@ -214,10 +258,20 @@ abstract class NextCloud
'index.php/avatar' => 'avatar', 'index.php/avatar' => 'avatar',
'ocs/v2.php/apps/dav/api/v1/direct' => 'direct_url', 'ocs/v2.php/apps/dav/api/v1/direct' => 'direct_url',
'remote.php/direct/' => 'direct', 'remote.php/direct/' => 'direct',
'avatars/' => 'avatar',
]; ];
const AUTH_REDIRECT_URL = 'nc://login/server:%s&user:%s&password:%s'; const AUTH_REDIRECT_URL = 'nc://login/server:%s&user:%s&password:%s';
// *Every* different NextCloud/ownCloud app uses a different root path
// this is ridiculous.
// NC Desktop: /remote.php/dav/files/user// (note the double slash at the end)
// NC iOS: /remote.php/dav/files/user (note the missing end slash)
// ownCloud Android: /remote.php/dav/files/ (note the missing user part)
// -> only at first, then it queries the correct path, WTF
// See also https://github.com/nextcloud/server/issues/25867
const WEBDAV_BASE_REGEXP = '~^.*remote\.php/(?:webdav/|dav/files/(?:(?:(?!/).)+(?:/+|$)|/*$))~';
public function setRootURL(string $url) public function setRootURL(string $url)
{ {
$this->root_url = $url; $this->root_url = $url;
@ -241,6 +295,8 @@ abstract class NextCloud
$uri = $_SERVER['REQUEST_URI'] ?? '/'; $uri = $_SERVER['REQUEST_URI'] ?? '/';
} }
$uri = parse_url($uri, PHP_URL_PATH);
$uri = ltrim($uri, '/'); $uri = ltrim($uri, '/');
$uri = rawurldecode($uri); $uri = rawurldecode($uri);
@ -252,8 +308,6 @@ abstract class NextCloud
$route = current($route); $route = current($route);
header('Access-Control-Allow-Origin: *', true);
$method = $_SERVER['REQUEST_METHOD'] ?? null; $method = $_SERVER['REQUEST_METHOD'] ?? null;
$this->server->log('NC <= %s %s => routed to: %s', $method, $uri, $route); $this->server->log('NC <= %s %s => routed to: %s', $method, $uri, $route);
@ -293,6 +347,8 @@ abstract class NextCloud
$this->server->log("NC => Body:\n%s", $json); $this->server->log("NC => Body:\n%s", $json);
} }
$this->server->log('NC Sent response: %d', http_response_code());
return true; return true;
} }
@ -329,6 +385,17 @@ abstract class NextCloud
{ {
$this->requireAuth(); $this->requireAuth();
$method = $_SERVER['REQUEST_METHOD'] ?? '';
// ownCloud-Android is using a different preview API
// remote.php/dav/files/user/name.jpg?x=224&y=224&c=&preview=1
if (!empty($_GET['preview'])) {
$x = (int) $_GET['x'] ?? 0;
$y = (int) $_GET['y'] ?? 0;
$this->serveThumbnail($uri, $x, $y, $x == $y);
return;
}
$base_uri = null; $base_uri = null;
// Find out which route we are using and replace URI // Find out which route we are using and replace URI
@ -347,11 +414,10 @@ abstract class NextCloud
throw new Exception('Invalid WebDAV URL', 404); throw new Exception('Invalid WebDAV URL', 404);
} }
// Android app is using "/remote.php/dav/files/user//" as root $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
// so let's alias that as well
// ownCloud Android is requesting just /dav/files/ if (preg_match(self::WEBDAV_BASE_REGEXP, $uri, $match)) {
if (preg_match('!^' . preg_quote($base_uri, '!') . 'files/(?:[^/]+/+)?!', $uri, $match)) { $base_uri = rtrim($match[0], '/') . '/';
$base_uri = $match[0];
} }
$this->server->prefix = $this->prefix; $this->server->prefix = $this->prefix;
@ -362,14 +428,23 @@ abstract class NextCloud
public function nc_status(): array public function nc_status(): array
{ {
if (stristr($_SERVER['HTTP_USER_AGENT'], 'owncloud')) {
$name = 'ownCloud';
$version = '10.11.0';
}
else {
$name = 'NextCloud';
$version = '24.0.4';
}
return [ return [
'installed' => true, 'installed' => true,
'maintenance' => false, 'maintenance' => false,
'needsDbUpgrade' => false, 'needsDbUpgrade' => false,
'version' => '24.0.4.1', 'version' => $version,
'versionstring' => '24.0.4', 'versionstring' => $version,
'edition' => '', 'edition' => '',
'productname' => 'NextCloud', 'productname' => $name,
'extendedSupport' => false, 'extendedSupport' => false,
]; ];
} }
@ -483,10 +558,10 @@ abstract class NextCloud
$user = $this->getUserName() ?? 'null'; $user = $this->getUserName() ?? 'null';
return $this->nc_ocs([ return $this->nc_ocs([
'id' => $user, 'id' => sha1($user),
'enabled' => true, 'enabled' => true,
'email' => null, 'email' => null,
'storageLocation' => '/secret/whoknows/' . $user, 'storageLocation' => '/secret/whoknows/' . sha1($user),
'role' => '', 'role' => '',
'display-name' => $user, 'display-name' => $user,
'quota' => [ 'quota' => [
@ -509,7 +584,7 @@ abstract class NextCloud
return $this->nc_ocs([]); return $this->nc_ocs([]);
} }
protected function nc_avatar(): ?array protected function nc_avatar(): void
{ {
throw new Exception('Not implemented', 404); throw new Exception('Not implemented', 404);
} }
@ -647,29 +722,10 @@ abstract class NextCloud
$height = $_GET['y'] ?? null; $height = $_GET['y'] ?? null;
$crop = !($_GET['a'] ?? null); $crop = !($_GET['a'] ?? null);
if (!preg_match('/\.(?:jpe?g|gif|png|webp)$/', $uri)) { $url = str_replace('%2F', '/', rawurldecode($_GET['file'] ?? ''));
http_response_code(404);
return;
}
// On Android, the app is annoying and asks to download the image
// every time ("no resized image available").
// So to avoid that we will just redirect to the file if it is not too big.
// But you are free to extend this method and resize the image on the fly
$url = str_replace('%2F', '/', rawurlencode(rawurldecode($_GET['file'] ?? '')));
$url = ltrim($url, '/'); $url = ltrim($url, '/');
$size = current($this->storage->properties($url, ['DAV::getcontentlenth'], 0)); $this->serveThumbnail($url, (int) $width, (int) $height, $crop, true);
// 1 MB is a large image
if ($size > 1024*1024) {
http_response_code(404);
return;
}
$url = '/remote.php/dav/files/' . $url;
$this->server->log('=> Preview: redirect to %s', $url);
header('Location: ' . $url);
} }
protected function nc_thumbnail(string $uri): void protected function nc_thumbnail(string $uri): void
@ -680,8 +736,7 @@ abstract class NextCloud
list($width, $height, $uri) = array_pad(explode('/', $uri, 3), 3, null); list($width, $height, $uri) = array_pad(explode('/', $uri, 3), 3, null);
// We don't support this feature, but you are free to generate cropped thumbnails here $this->serveThumbnail($uri, (int)$width, (int)$height, $width == $height);
http_response_code(404);
} }
/** /**
@ -736,7 +791,7 @@ abstract class NextCloud
} }
elseif ($method == 'MOVE') { elseif ($method == 'MOVE') {
$dest = $_SERVER['HTTP_DESTINATION']; $dest = $_SERVER['HTTP_DESTINATION'];
$dest = preg_replace('!^.*/remote.php/(?:web)?dav/(?:files/)?[^/]*/!', '', $dest); $dest = preg_replace(self::WEBDAV_BASE_REGEXP, '', $dest);
$dest = trim(rawurldecode($dest), '/'); $dest = trim(rawurldecode($dest), '/');
if (false !== strpos($dest, '..') || false !== strpos($dest, '//')) { if (false !== strpos($dest, '..') || false !== strpos($dest, '//')) {
@ -757,6 +812,8 @@ abstract class NextCloud
header(sprintf('OC-ETag: "%s"', $return['etag'])); header(sprintf('OC-ETag: "%s"', $return['etag']));
} }
$this->server->log("=> Chunks assembled to: %s", $dest);
if (!empty($return['created'])) { if (!empty($return['created'])) {
http_response_code(201); http_response_code(201);
} }
@ -766,6 +823,26 @@ abstract class NextCloud
} }
elseif ($method == 'DELETE' && !$part) { elseif ($method == 'DELETE' && !$part) {
$this->deleteChunks($login, $dir); $this->deleteChunks($login, $dir);
$this->server->log("=> Deleted chunks");
}
elseif ($method == 'PROPFIND') {
header('HTTP/1.1 207 Multi-Status', true);
$out = '<?xml version="1.0" encoding="utf-8"?>' . PHP_EOL;
$out .= '<d:multistatus xmlns:d="DAV:">' . PHP_EOL;
foreach ($this->listChunks($login, $dir) as $chunk) {
$out .= '<d:response>' . PHP_EOL;
$chunk = '/' . $uri . '/' . $chunk;
$out .= sprintf('<d:href>%s</d:href>', htmlspecialchars($chunk, ENT_XML1)) . PHP_EOL;
$out .= '<d:propstat><d:prop><d:getcontenttype>application/octet-stream</d:getcontenttype><d:resoucetype/></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat>' . PHP_EOL;
$out .= '</d:response>' . PHP_EOL;
}
$out .= '</d:multistatus>';
echo $out;
$this->server->log("=> Body:\n%s", $out);
} }
else { else {
throw new Exception('Invalid method for chunked upload', 400); throw new Exception('Invalid method for chunked upload', 400);

View file

@ -82,6 +82,13 @@ class Server
const SHARED_LOCK = 'shared'; const SHARED_LOCK = 'shared';
const EXCLUSIVE_LOCK = 'exclusive'; const EXCLUSIVE_LOCK = 'exclusive';
/**
* Enable on-the-fly gzip compression
* This can use a large amount of resources
* @var boolean
*/
protected bool $enable_gzip = true;
/** /**
* Base server URI (eg. "/index.php/webdav/") * Base server URI (eg. "/index.php/webdav/")
*/ */
@ -114,6 +121,19 @@ class Server
$this->base_uri = rtrim($uri, '/') . '/'; $this->base_uri = rtrim($uri, '/') . '/';
} }
/**
* Extend max_execution_time so that upload/download of files don't expire if connection is slow
*/
protected function extendExecutionTime(): void
{
if (false === strpos(@ini_get('disable_functions'), 'set_time_limit')) {
@set_time_limit(3600);
}
@ini_set('max_execution_time', '3600');
@ini_set('max_input_time', '3600');
}
protected function _prefix(string $uri): string protected function _prefix(string $uri): string
{ {
if (!$this->prefix) { if (!$this->prefix) {
@ -269,6 +289,8 @@ class Server
header('X-OC-MTime: accepted'); header('X-OC-MTime: accepted');
} }
$this->extendExecutionTime();
$created = $this->storage->put($uri, fopen('php://input', 'r'), $hash, $mtime); $created = $this->storage->put($uri, fopen('php://input', 'r'), $hash, $mtime);
$prop = $this->storage->properties($uri, ['DAV::getetag'], 0); $prop = $this->storage->properties($uri, ['DAV::getetag'], 0);
@ -383,6 +405,8 @@ class Server
throw new \RuntimeException('Invalid file array returned by ::get()'); throw new \RuntimeException('Invalid file array returned by ::get()');
} }
$this->extendExecutionTime();
$length = $start = $end = null; $length = $start = $end = null;
$gzip = false; $gzip = false;
@ -402,17 +426,20 @@ class Server
$this->log('HTTP Range requested: %s-%s', $start, $end); $this->log('HTTP Range requested: %s-%s', $start, $end);
} }
elseif (isset($_SERVER['HTTP_ACCEPT_ENCODING']) elseif ($this->enable_gzip
&& isset($_SERVER['HTTP_ACCEPT_ENCODING'])
&& false !== strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && false !== strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')
&& isset($props['DAV::getcontentlength'])
// Don't compress if size is larger than 8 MiB
&& $props['DAV::getcontentlength'] < 8*1024*1024
// Don't compress already compressed content // Don't compress already compressed content
&& !preg_match('/\.(?:mp4|m4a|zip|docx|xlsx|ods|odt|odp|7z|gz|bz2|rar|webm|ogg|mp3|ogm|flac|ogv|mkv|avi)$/i', $uri)) { && !preg_match('/\.(?:cbz|cbr|cb7|mp4|m4a|zip|docx|xlsx|pptx|ods|odt|odp|7z|gz|bz2|lzma|lz|xz|apk|dmg|jar|rar|webm|ogg|mp3|ogm|flac|ogv|mkv|avi)$/i', $uri)) {
$gzip = true; $gzip = true;
header('Content-Encoding: gzip', true); header('Content-Encoding: gzip', true);
} }
// Try to avoid common issues with output buffering and stuff // Try to avoid common issues with output buffering and stuff
if (function_exists('apache_setenv')) if (function_exists('apache_setenv')) {
{
@apache_setenv('no-gzip', 1); @apache_setenv('no-gzip', 1);
} }
@ -495,9 +522,9 @@ class Server
if ($gzip) { if ($gzip) {
$this->log('Using gzip output compression'); $this->log('Using gzip output compression');
$gzip = deflate_init(ZLIB_ENCODING_GZIP, ['level' => 9]); $gzip = deflate_init(ZLIB_ENCODING_GZIP);
$fp = fopen('php://memory', 'wb'); $fp = fopen('php://temp', 'wb');
while (!feof($file['resource'])) { while (!feof($file['resource'])) {
fwrite($fp, deflate_add($gzip, fread($file['resource'], 8192), ZLIB_NO_FLUSH)); fwrite($fp, deflate_add($gzip, fread($file['resource'], 8192), ZLIB_NO_FLUSH));
@ -517,14 +544,16 @@ class Server
header('Content-Length: ' . $length, true); header('Content-Length: ' . $length, true);
} }
$block_size = 8192*4;
while (!feof($file['resource']) && ($end === null || $end > 0)) { while (!feof($file['resource']) && ($end === null || $end > 0)) {
$l = $end !== null ? min(8192, $end) : 8192; $l = $end !== null ? min($block_size, $end) : $block_size;
echo fread($file['resource'], $l); echo fread($file['resource'], $l);
flush(); flush();
if (null !== $end) { if (null !== $end) {
$end -= 8192; $end -= $block_size;
} }
} }

View file

@ -26,11 +26,17 @@ class WOPI
const PROP_TOKEN = self::NS . ':token'; const PROP_TOKEN = self::NS . ':token';
const PROP_TOKEN_TTL = self::NS . ':token-ttl'; const PROP_TOKEN_TTL = self::NS . ':token-ttl';
const PROP_READ_ONLY = self::NS . ':ReadOnly'; const PROP_READ_ONLY = self::NS . ':ReadOnly';
const PROP_USER_NAME = self::NS . ':FriendlyUserName'; const PROP_USER_NAME = self::NS . ':UserFriendlyName';
const PROP_USER_ID = self::NS . ':UserId';
protected AbstractStorage $storage; protected AbstractStorage $storage;
protected Server $server; protected Server $server;
public function setStorage(AbstractStorage $storage)
{
$this->storage = $storage;
}
public function setServer(Server $server) public function setServer(Server $server)
{ {
$this->storage = $server->getStorage(); $this->storage = $server->getStorage();
@ -88,20 +94,26 @@ class WOPI
$this->server->log('WOPI: => Found doc_uri: %s', $uri); $this->server->log('WOPI: => Found doc_uri: %s', $uri);
// GetFile
if ($action == 'contents' && $method == 'GET') { if ($action == 'contents' && $method == 'GET') {
$this->server->log('WOPI: => GetFile');
$this->server->http_get($uri); $this->server->http_get($uri);
} }
// PutFile
elseif ($action == 'contents' && $method == 'POST') { elseif ($action == 'contents' && $method == 'POST') {
$this->server->log('WOPI: => PutFile');
$this->server->http_put($uri); $this->server->http_put($uri);
} }
// CheckFileInfo
elseif (!$action && $method == 'GET') { elseif (!$action && $method == 'GET') {
$this->server->log('WOPI: => CheckFileInfo');
$this->getInfo($uri); $this->getInfo($uri);
} }
else { else {
throw new Exception('Invalid URI', 404); throw new Exception('Invalid URI', 404);
} }
$this->server->log('WOPI: <= 200');
http_response_code(200); // This is required for Collabora http_response_code(200); // This is required for Collabora
} }
catch (Exception $e) { catch (Exception $e) {
@ -121,6 +133,7 @@ class WOPI
'DAV::getetag', 'DAV::getetag',
self::PROP_READ_ONLY, self::PROP_READ_ONLY,
self::PROP_USER_NAME, self::PROP_USER_NAME,
self::PROP_USER_ID,
], 0); ], 0);
$modified = !empty($props['DAV::getlastmodified']) ? $props['DAV::getlastmodified']->format(DATE_ISO8601) : null; $modified = !empty($props['DAV::getlastmodified']) ? $props['DAV::getlastmodified']->format(DATE_ISO8601) : null;
@ -128,9 +141,9 @@ class WOPI
$data = [ $data = [
'BaseFileName' => basename($uri), 'BaseFileName' => basename($uri),
'UserFriendlyName' => $props['DAV::PROP_USER_NAME'] ?? 'User', 'UserFriendlyName' => $props[self::PROP_USER_NAME] ?? 'User',
'OwnerId' => 1, 'OwnerId' => 0,
'UserId' => 1, 'UserId' => $props[self::PROP_USER_ID] ?? 0,
'Size' => $size, 'Size' => $size,
'Version' => $props['DAV::getetag'] ?? md5($uri . $size . $modified), 'Version' => $props['DAV::getetag'] ?? md5($uri . $size . $modified),
]; ];
@ -142,9 +155,16 @@ class WOPI
$data['ReadOnly'] = $props['self::PROP_READ_ONLY'] ?? false; $data['ReadOnly'] = $props['self::PROP_READ_ONLY'] ?? false;
$data['UserCanWrite'] = !$data['ReadOnly']; $data['UserCanWrite'] = !$data['ReadOnly'];
$data['UserCanRename'] = !$data['ReadOnly']; $data['UserCanRename'] = !$data['ReadOnly'];
$data['DisableCopy'] = $data['ReadOnly'];
$data['UserCanNotWriteRelative'] = true; // This requires you to implement file name UI
//$data['DisablePrint'] = true;
$json = json_encode($data, JSON_PRETTY_PRINT);
$this->server->log('WOPI: => Info: %s', $json);
http_response_code(200); http_response_code(200);
echo json_encode($data, JSON_PRETTY_PRINT); header('Content-Type: application/json', true);
echo $json;
return true; return true;
} }
@ -160,13 +180,13 @@ class WOPI
* 'application/vnd.oasis.opendocument.presentation' => ['edit' => 'http://'...], * 'application/vnd.oasis.opendocument.presentation' => ['edit' => 'http://'...],
* ]] * ]]
*/ */
public function discover(string $url): array static public function discover(string $url): array
{ {
if (function_exists('curl_init')) { if (function_exists('curl_init')) {
$c = curl_init($url); $c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true); curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
$r = curl_exec($c); $r = curl_exec($c);
$code = curl_getinfo(CURLINFO_HTTP_CODE); $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
if ($code != 200) { if ($code != 200) {
throw new \RuntimeException(sprintf("Discovery URL returned an error: %d\n%s", $code, $r)); throw new \RuntimeException(sprintf("Discovery URL returned an error: %d\n%s", $code, $r));
@ -269,7 +289,7 @@ class WOPI
$url = parse_url($url); $url = parse_url($url);
// Remove available options from URL // Remove available options from URL
$url['query'] = preg_replace('/<(\w+)=(\w+)&>/i', '', $url['query']); $url['query'] = preg_replace('/<(\w+)=(\w+)&>/i', '', $url['query'] ?? '');
// Set options // Set options
parse_str($url['query'], $params); parse_str($url['query'], $params);

View file

@ -134,6 +134,14 @@ class NextCloud extends WebDAV_NextCloud
fclose($pointer); fclose($pointer);
} }
public function listChunks(string $login, string $name): array
{
$path = $this->temporary_chunks_path . '/' . $name;
$list = glob($path . '/*');
$list = array_map(fn($a) => str_replace($path . '/', '', $a), $list);
return $list;
}
public function deleteChunks(string $login, string $name): void public function deleteChunks(string $login, string $name): void
{ {
$path = $this->temporary_chunks_path . '/' . $login . '/' . $name; $path = $this->temporary_chunks_path . '/' . $login . '/' . $name;