2022-09-29 23:39:24 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace KaraDAV;
|
|
|
|
|
|
|
|
use KD2\WebDAV\NextCloud as WebDAV_NextCloud;
|
2022-09-30 13:17:47 +00:00
|
|
|
use KD2\WebDAV\Exception as WebDAV_Exception;
|
2024-04-08 15:34:09 +00:00
|
|
|
use KD2\Graphics\SVG\Avatar;
|
2022-09-29 23:39:24 +00:00
|
|
|
|
|
|
|
class NextCloud extends WebDAV_NextCloud
|
|
|
|
{
|
|
|
|
protected Users $users;
|
add nullable user property to nextcloud class (#32)
this fixes a deprecation notices on php8.2 which results in an error and
therefore inability to register on nextcloud client
ErrorReport:
```
[24-Apr-2023 19:07:01 UTC] =========== Error ref. 25qisnme ===========
ErrorException: Deprecated: Creation of dynamic property KaraDAV\NextCloud::$user is deprecated in /home/lubiana/dev/private/karadav/lib/KaraDAV/NextCloud.php:33
Stack trace:
#0 /home/lubiana/dev/private/karadav/lib/KaraDAV/NextCloud.php(33): KD2\ErrorManager::errorHandler(8192, '...', '...', 33)
#1 /home/lubiana/dev/private/karadav/lib/KD2/WebDAV/NextCloud.php(380): KaraDAV\NextCloud->auth('...', '...')
#2 /home/lubiana/dev/private/karadav/lib/KD2/WebDAV/NextCloud.php(559): KD2\WebDAV\NextCloud->requireAuth()
#3 /home/lubiana/dev/private/karadav/lib/KD2/WebDAV/NextCloud.php(317): KD2\WebDAV\NextCloud->nc_user('...')
#4 /home/lubiana/dev/private/karadav/lib/KaraDAV/Server.php(44): KD2\WebDAV\NextCloud->route('...')
#5 /home/lubiana/dev/private/karadav/www/_router.php(47): KaraDAV\Server->route('...')
#6 {main}
<errorReport>
{
"errors": [
{
"message": "Deprecated: Creation of dynamic property KaraDAV\\NextCloud::$user is deprecated",
"errorCode": 0,
"type": "PHP error",
"backtrace": [
{
"file": "\/home\/lubiana\/dev\/private\/karadav\/lib\/KaraDAV\/NextCloud.php",
"line": 33,
"code": {
"29": "\t\tif (!$user) {",
"30": "\t\t\treturn false;",
"31": "\t\t}",
"32": "",
"33": "\t\t$this->user = $user;",
"34": "",
"35": "\t\treturn true;",
"36": "\t}",
"37": ""
}
},
{
"function": "KaraDAV\\NextCloud->auth",
"file": "\/home\/lubiana\/dev\/private\/karadav\/lib\/KD2\/WebDAV\/NextCloud.php",
"line": 380,
"args": {
"$login": "string(7) \"lubiana\"",
"$password": "string(33) \"redacted\""
},
"code": {
"376": "\t}",
"377": "",
"378": "\tprotected function requireAuth(): void",
"379": "\t{",
"380": "\t\tif (!$this->auth($_SERVER['PHP_AUTH_USER'] ?? null, $_SERVER['PHP_AUTH_PW'] ?? null)) {",
"381": "\t\t\theader('WWW-Authenticate: Basic realm=\"Please login\"');",
"382": "\t\t\tthrow new Exception('Please login to access this resource', 401);",
"383": "\t\t}",
"384": "\t}"
}
},
{
"function": "KD2\\WebDAV\\NextCloud->requireAuth",
"file": "\/home\/lubiana\/dev\/private\/karadav\/lib\/KD2\/WebDAV\/NextCloud.php",
"line": 559,
"code": {
"555": "\t}",
"556": "",
"557": "\tpublic function nc_user(): array",
"558": "\t{",
"559": "\t\t$this->requireAuth();",
"560": "",
"561": "\t\t$quota = $this->getUserQuota();",
"562": "\t\t$user = $this->getUserName() ?? 'null';",
"563": ""
}
},
{
"function": "KD2\\WebDAV\\NextCloud->nc_user",
"file": "\/home\/lubiana\/dev\/private\/karadav\/lib\/KD2\/WebDAV\/NextCloud.php",
"line": 317,
"args": [
"string(21) \"ocs\/v1.php\/cloud\/user\""
],
"code": {
"313": "\t\t$method = $_SERVER['REQUEST_METHOD'] ?? null;",
"314": "\t\t$this->server->log('NC <= %s %s => routed to: %s', $method, $uri, $route);",
"315": "",
"316": "\t\ttry {",
"317": "\t\t\t$v = $this->{'nc_' . $route}($uri);",
"318": "\t\t}",
"319": "\t\tcatch (Exception $e) {",
"320": "\t\t\t$this->server->log('NC => %d - %s', $e->getCode(), $e->getMessage());",
"321": "\t\t\thttp_response_code($e->getCode());"
}
},
{
"function": "KD2\\WebDAV\\NextCloud->route",
"file": "\/home\/lubiana\/dev\/private\/karadav\/lib\/KaraDAV\/Server.php",
"line": 44,
"args": {
"$uri": "string(21) \"ocs\/v1.php\/cloud\/user\""
},
"code": {
"40": "\t\t}",
"41": "",
"42": "\t\t$this->nc->setServer($this->dav);",
"43": "",
"44": "\t\tif ($r = $this->nc->route($uri)) {",
"45": "\t\t\t\/\/ NextCloud route already replied something, stop here",
"46": "\t\t\treturn true;",
"47": "\t\t}",
"48": ""
}
},
{
"function": "KaraDAV\\Server->route",
"file": "...\/_router.php",
"line": 47,
"args": {
"$uri": "string(22) \"\/ocs\/v1.php\/cloud\/user\""
},
"code": {
"43": "if (isset($_SERVER['REDIRECT_REQUEST_METHOD'])) {",
"44": "\t$_SERVER['REQUEST_METHOD'] = $_SERVER['REDIRECT_REQUEST_METHOD'];",
"45": "}",
"46": "",
"47": "if (!$s->route($uri)) {",
"48": "\tif (PHP_SAPI == 'cli-server') {",
"49": "\t\t$s->dav->log(\"ROUTER: => Route is not managed: 404\");",
"50": "\t}",
"51": ""
}
}
]
}
],
"context": {
"date": "2023-04-24T19:07:01+00:00",
"duration": 155.84897994995117,
"environment": "development",
"hostname": "0.0.0.0",
"http_files": "array(0) {\n}",
"http_method": "GET",
"http_post": "array(0) {\n}",
"http_user_agent": "Mozilla\/5.0 (Linux) mirall\/3.8.1git (Nextcloud, arch-6.2.12-arch1-1 ClientArchitecture: x86_64 OsArchitecture: x86_64)",
"id": "25qisnme",
"language": "PHP 8.2.5",
"memory_peak": 2097152,
"memory_used": 2097152,
"os": "Linux",
"php_sapi": "cli-server",
"remote_ip": "127.0.0.1",
"root_directory": "\/home\/lubiana\/dev\/private\/karadav\/www",
"user_addr": "127.0.0.1",
"url": "http:\/\/127.0.0.1:8080\/ocs\/v1.php\/cloud\/user?format=json"
}
}
</errorReport>
```
2023-04-24 19:54:47 +00:00
|
|
|
protected ?\stdClass $user;
|
2022-09-30 13:17:47 +00:00
|
|
|
protected string $temporary_chunks_path;
|
2022-09-29 23:39:24 +00:00
|
|
|
|
2022-11-04 19:52:02 +00:00
|
|
|
public function __construct(Users $users)
|
2022-09-29 23:39:24 +00:00
|
|
|
{
|
|
|
|
$this->users = $users;
|
2022-10-03 01:03:19 +00:00
|
|
|
$this->temporary_chunks_path = sprintf(STORAGE_PATH, '_chunks');
|
2022-10-02 00:41:46 +00:00
|
|
|
$this->setRootURL(WWW_URL);
|
2022-09-29 23:39:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function auth(?string $login, ?string $password): bool
|
|
|
|
{
|
2022-10-02 01:27:19 +00:00
|
|
|
$user = $this->users->login($login, $password);
|
|
|
|
|
|
|
|
if (!$user) {
|
|
|
|
// Try app session
|
|
|
|
$user = $this->users->appSessionLogin($login, $password);
|
|
|
|
}
|
2022-09-29 23:39:24 +00:00
|
|
|
|
|
|
|
if (!$user) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->user = $user;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getUserName(): ?string
|
|
|
|
{
|
|
|
|
return $this->users->current()->login ?? null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setUserName(string $login): bool
|
|
|
|
{
|
|
|
|
$ok = $this->users->setCurrent($login);
|
|
|
|
|
|
|
|
if ($ok) {
|
|
|
|
$this->user = $this->users->current();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getUserQuota(): array
|
|
|
|
{
|
|
|
|
return (array) $this->users->quota($this->users->current());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function generateToken(): string
|
|
|
|
{
|
|
|
|
return sha1(random_bytes(16));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function validateToken(string $token): ?array
|
|
|
|
{
|
|
|
|
$session = $this->users->appSessionValidateToken($token);
|
|
|
|
|
|
|
|
if (!$session) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-10-24 22:34:50 +00:00
|
|
|
return ['login' => $session->user->login, 'password' => $session->password];
|
2022-09-29 23:39:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getLoginURL(?string $token): string
|
|
|
|
{
|
|
|
|
if ($token) {
|
|
|
|
return sprintf('%slogin.php?nc=%s', WWW_URL, $token);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return sprintf('%slogin.php?nc=redirect', WWW_URL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getDirectDownloadSecret(string $uri, string $login): string
|
|
|
|
{
|
|
|
|
$user = $this->users->get($login);
|
|
|
|
|
|
|
|
if (!$user) {
|
|
|
|
throw new WebDAV_Exception('No user with that name', 401);
|
|
|
|
}
|
|
|
|
|
2022-10-29 22:52:35 +00:00
|
|
|
return WebDAV::hmac([$uri, $user->login, $user->password]);
|
2022-09-29 23:39:24 +00:00
|
|
|
}
|
2022-09-30 13:17:47 +00:00
|
|
|
|
|
|
|
protected function cleanChunks(): void
|
|
|
|
{
|
|
|
|
$expire = time() - 36*3600;
|
|
|
|
|
2022-10-25 23:01:01 +00:00
|
|
|
foreach (glob($this->temporary_chunks_path . '/*/*') as $dir) {
|
|
|
|
$first_file = current(glob($dir . '/*'));
|
|
|
|
|
|
|
|
if (filemtime($first_file) < $expire) {
|
|
|
|
Storage::deleteDirectory($dir);
|
2022-09-30 13:17:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function storeChunk(string $login, string $name, string $part, $pointer): void
|
|
|
|
{
|
|
|
|
$this->cleanChunks();
|
|
|
|
|
|
|
|
$path = $this->temporary_chunks_path . '/' . $login . '/' . $name;
|
|
|
|
@mkdir($path, 0777, true);
|
|
|
|
|
|
|
|
$file_path = $path . '/' . $part;
|
|
|
|
$out = fopen($file_path, 'wb');
|
|
|
|
$quota = $this->getUserQuota();
|
|
|
|
$used = $quota['used'] + Storage::getDirectorySize($path);
|
|
|
|
|
|
|
|
while (!feof($pointer)) {
|
|
|
|
$data = fread($pointer, 8192);
|
|
|
|
$used += strlen($used);
|
|
|
|
|
|
|
|
if ($used > $quota['free']) {
|
|
|
|
$this->deleteChunks($login, $name);
|
|
|
|
throw new WebDAV_Exception('Your quota does not allow for the upload of this file', 403);
|
|
|
|
}
|
|
|
|
|
|
|
|
fwrite($out, $data);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose($out);
|
|
|
|
fclose($pointer);
|
|
|
|
}
|
|
|
|
|
2023-01-28 02:11:31 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-09-30 13:17:47 +00:00
|
|
|
public function deleteChunks(string $login, string $name): void
|
|
|
|
{
|
|
|
|
$path = $this->temporary_chunks_path . '/' . $login . '/' . $name;
|
|
|
|
Storage::deleteDirectory($path);
|
|
|
|
}
|
|
|
|
|
2022-10-02 00:41:46 +00:00
|
|
|
public function assembleChunks(string $login, string $name, string $target, ?int $mtime): array
|
2022-09-30 13:17:47 +00:00
|
|
|
{
|
|
|
|
$target = $this->users->current()->path . $target;
|
|
|
|
$parent = dirname($target);
|
|
|
|
|
|
|
|
if (!is_dir($parent)) {
|
|
|
|
throw new WebDAV_Exception('Target parent directory does not exist', 409);
|
|
|
|
}
|
|
|
|
|
|
|
|
$path = $this->temporary_chunks_path . '/' . $login . '/' . $name;
|
|
|
|
$exists = file_exists($target);
|
|
|
|
|
|
|
|
if ($exists && is_dir($target)) {
|
2022-10-25 23:01:01 +00:00
|
|
|
throw new WebDAV_Exception('Target exists and is a directory', 409);
|
2022-09-30 13:17:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$out = fopen($target, 'wb');
|
|
|
|
|
|
|
|
foreach (glob($path . '/*') as $file) {
|
|
|
|
$in = fopen($file, 'rb');
|
|
|
|
|
|
|
|
while (!feof($in)) {
|
|
|
|
fwrite($out, fread($in, 8192));
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose($in);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose($out);
|
|
|
|
$this->deleteChunks($login, $name);
|
2022-10-02 00:41:46 +00:00
|
|
|
|
|
|
|
if ($mtime) {
|
|
|
|
touch($target, $mtime);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ['created' => !$exists, 'etag' => md5(filemtime($target) . filesize($target))];
|
2022-09-30 13:17:47 +00:00
|
|
|
}
|
|
|
|
|
2024-04-08 15:34:09 +00:00
|
|
|
protected function nc_avatar(): void
|
|
|
|
{
|
|
|
|
header('Content-Type: image/svg+xml; charset=utf-8');
|
|
|
|
echo Avatar::beam($_SERVER['REQUEST_URI'] ?? '', ['colors' => ['#009', '#ccf', '#9cf']]);
|
|
|
|
}
|
2022-09-29 23:39:24 +00:00
|
|
|
}
|