diff --git a/Makefile b/Makefile index 6cf5d07..916427c 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,6 @@ js-deps: wget -O www/webdav.css https://raw.githubusercontent.com/kd2org/webdav-manager.js/main/webdav.css php-deps: - mkdir lib/KD2/WebDAV wget -O lib/KD2/ErrorManager.php '${KD2FW_URL}ErrorManager.php' wget -O lib/KD2/WebDAV/Server.php '${KD2FW_URL}WebDAV/Server.php' wget -O lib/KD2/WebDAV/AbstractStorage.php '${KD2FW_URL}WebDAV/AbstractStorage.php' diff --git a/lib/KD2/WebDAV/NextCloud.php b/lib/KD2/WebDAV/NextCloud.php index 9a4695a..25c0ebf 100644 --- a/lib/KD2/WebDAV/NextCloud.php +++ b/lib/KD2/WebDAV/NextCloud.php @@ -49,6 +49,7 @@ abstract class NextCloud const PROP_OC_SHARETYPES = self::OC_NAMESPACE . ':share-types'; const PROP_NC_NOTE = self::NC_NAMESPACE . ':note'; const PROP_NC_IS_ENCRYPTED = self::NC_NAMESPACE . ':is-encrypted'; + const PROP_NC_DDC = self::NC_NAMESPACE . ':dDC'; const NC_PROPERTIES = [ self::PROP_OC_ID, @@ -59,6 +60,7 @@ abstract class NextCloud self::PROP_NC_HAS_PREVIEW, self::PROP_NC_NOTE, self::PROP_NC_IS_ENCRYPTED, + self::PROP_NC_DDC, ]; protected string $root_url; @@ -664,9 +666,9 @@ abstract class NextCloud } /** - * Called when removing an account from Android pap + * Called when removing an account from Android app */ - protected function nc_app_password(): void + protected function nc_delete_app_password(): void { $method = $_SERVER['REQUEST_METHOD'] ?? null; diff --git a/lib/KD2/WebDAV/WOPI.php b/lib/KD2/WebDAV/WOPI.php index d5a0a7b..57206a5 100644 --- a/lib/KD2/WebDAV/WOPI.php +++ b/lib/KD2/WebDAV/WOPI.php @@ -22,13 +22,10 @@ namespace KD2\WebDAV; class WOPI { const NS = 'https://interoperability.blob.core.windows.net/files/MS-WOPI/'; - const PROP_FILE_ID = self::NS . ':file-id'; const PROP_FILE_URL = self::NS . ':file-url'; const PROP_TOKEN = self::NS . ':token'; const PROP_TOKEN_TTL = self::NS . ':token-ttl'; const PROP_READ_ONLY = self::NS . ':ReadOnly'; - const PROP_CAN_WRITE = self::NS . ':UserCanWrite'; - const PROP_CAN_RENAME = self::NS . ':UserCanRename'; const PROP_USER_NAME = self::NS . ':FriendlyUserName'; protected AbstractStorage $storage; @@ -40,6 +37,22 @@ class WOPI $this->server = $server; } + public function getAuthToken() + { + // HTTP_AUTHORIZATION might be missing in some installs + $header = apache_request_headers()['Authorization'] ?? ''; + + if ($header && 0 === stripos($header, 'Bearer ')) { + return trim(substr($header, strlen('Bearer '))); + } + elseif (!empty($_GET['access_token'])) { + return trim($_GET['access_token']); + } + else { + throw new Exception('No access_token was provided', 401); + } + } + public function route(?string $uri = null): bool { if (!method_exists($this->storage, 'getWopiURI')) { @@ -58,42 +71,43 @@ class WOPI $uri = substr($uri, strlen('wopi/files/')); - if (!empty($_SERVER['HTTP_AUTHORIZATION']) && 0 === stripos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer ')) { - $auth_token = trim(substr($_SERVER['HTTP_AUTHORIZATION'], strlen('Bearer '))); - } - elseif (!empty($_GET['access_token'])) { - $auth_token = trim($_GET['access_token']); - } - else { - $auth_token = null; - } + $this->server->log('WOPI: => %s', $uri); - if (!$auth_token) { - throw new Exception('No access_token was provided', 401); - } + try { + $auth_token = $this->getAuthToken(); - $method = $_SERVER['REQUEST_METHOD']; - $id = rawurldecode(strtok($uri, '/')); - $action = trim(strtok(false), '/'); + $method = $_SERVER['REQUEST_METHOD']; + $id = rawurldecode(strtok($uri, '/')); + $action = trim(strtok(false), '/'); - $uri = $this->storage->getWopiURI($id, $auth_token); + $uri = $this->storage->getWopiURI($id, $auth_token); - if (!$uri) { - throw new Exception('Invalid file ID or invalid token', 404); - } + if (!$uri) { + throw new Exception('Invalid file ID or invalid token', 404); + } + + $this->server->log('WOPI: => Found doc_uri: %s', $uri); + + + if ($action == 'contents' && $method == 'GET') { + $this->server->http_get($uri); + } + elseif ($action == 'contents' && $method == 'POST') { + $this->server->http_put($uri); + } + elseif (!$action && $method == 'GET') { + $this->getInfo($uri); + } + else { + throw new Exception('Invalid URI', 404); + } - if ($action == 'contents' && $method == 'GET') { - $this->server->http_get($uri); - } - elseif ($action == 'contents' && $method == 'POST') { - $this->server->http_put($uri); http_response_code(200); // This is required for Collabora } - elseif (!$action && $method == 'GET') { - $this->getInfo($uri); - } - else { - throw new Exception('Invalid URI', 404); + catch (Exception $e) { + $this->server->log('WOPI: => %d: %s', $e->getCode(), $e->getMessage()); + http_response_code($e->getCode()); + echo json_encode(['error' => $e->getMessage()]); } return true; @@ -266,20 +280,19 @@ class WOPI public function getEditorHTML(string $editor_url, string $document_uri, string $title = 'Document') { // You need to extend this method by creating a token for the document_uri first! - // Store the token in the document properties using ::PROP_TOKEN + // Return the token with the document properties using ::PROP_TOKEN - $props = $this->storage->properties($document_uri, [self::PROP_TOKEN, self::PROP_TOKEN_TTL], 0); - $src = $props[self::PROP_FILE_URL] ?? null; + $props = $this->storage->properties($document_uri, [self::PROP_TOKEN, self::PROP_TOKEN_TTL, self::PROP_FILE_URL], 0); - if (!$src) { - throw new Exception('Storage did not provide a file URL for WOPI src', 500); + if (count($props) != 3) { + throw new Exception('Missing properties for document', 500); } + $src = $props[self::PROP_FILE_URL] ?? null; $token = $props[self::PROP_TOKEN] ?? null; // access_token_TTL: A 64-bit integer containing the number of milliseconds since January 1, 1970 UTC and representing the expiration date and time stamp of the access_token. $token_ttl = $props[self::PROP_TOKEN_TTL] ?? (time() + 10 * 3600) * 1000; - // Append WOPI host URL $url = $this->setEditorOptions($editor_url, ['WOPISrc' => $src]); diff --git a/www/webdav.js b/www/webdav.js index 9169432..d431db6 100644 --- a/www/webdav.js +++ b/www/webdav.js @@ -147,6 +147,8 @@ const WebDAVNavigator = (url, options) => { } }); }); + + reloadListing(); }; const wopi_getEditURL = (name, mime) => { @@ -634,11 +636,15 @@ const WebDAVNavigator = (url, options) => { const wopi_discovery_url = options.wopi_discovery_url || null; - wopi_init(); - document.querySelector('html').innerHTML = html_tpl; - reloadListing(); + if (wopi_discovery_url) { + // Wait for WOPI discovery before creating the list + wopi_init(); + } + else { + reloadListing(); + } window.addEventListener('paste', (e) => { let items = e.clipboardData.items;