74 lines
3.3 KiB
PHP
74 lines
3.3 KiB
PHP
<?php
|
|
/**
|
|
* Minimaler WebDAV-Server für Tests (Router für "php -S"). Speichert unter dem Ordner aus DAV_ROOT, URL-Präfix /dav,
|
|
* Basic-Anmeldung nas / geheim. Kennt MKCOL, PUT, PROPFIND, HEAD, GET, DELETE. Gibt es die Datei ".truncate" im Wurzelordner,
|
|
* speichert PUT nur die Hälfte (simuliert einen abgebrochenen Upload).
|
|
*/
|
|
$root = rtrim((string) getenv('DAV_ROOT'), '/\\');
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
$path = rawurldecode((string) parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
|
|
|
|
if (($_SERVER['PHP_AUTH_USER'] ?? '') !== 'nas' || ($_SERVER['PHP_AUTH_PW'] ?? '') !== (getenv('DAV_PASS') ?: 'geheim')) {
|
|
header('WWW-Authenticate: Basic realm="NAS"');
|
|
http_response_code(401);
|
|
exit;
|
|
}
|
|
if (!str_starts_with($path, '/dav')) { http_response_code(404); exit; }
|
|
$rel = trim(substr($path, 4), '/');
|
|
if (str_contains($rel, '..')) { http_response_code(400); exit; }
|
|
$file = $root . ($rel === '' ? '' : '/' . $rel);
|
|
|
|
$entry = function (string $href, string $name, ?string $full) {
|
|
$isDir = $full === null || is_dir($full);
|
|
$size = $isDir ? '' : '<D:getcontentlength>' . filesize($full) . '</D:getcontentlength>';
|
|
$mod = $full !== null ? gmdate('D, d M Y H:i:s', filemtime($full)) . ' GMT' : '';
|
|
return '<D:response><D:href>' . htmlspecialchars($href) . '</D:href><D:propstat><D:prop>' . $size
|
|
. '<D:getlastmodified>' . $mod . '</D:getlastmodified><D:resourcetype>' . ($isDir ? '<D:collection/>' : '') . '</D:resourcetype></D:prop>'
|
|
. '<D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>';
|
|
};
|
|
|
|
switch ($method) {
|
|
case 'MKCOL':
|
|
if (file_exists($file)) { http_response_code(405); exit; }
|
|
if (!is_dir(dirname($file))) { http_response_code(409); exit; }
|
|
mkdir($file, 0755);
|
|
http_response_code(201);
|
|
break;
|
|
case 'PUT':
|
|
if (!is_dir(dirname($file))) { http_response_code(409); exit; }
|
|
$data = (string) file_get_contents('php://input');
|
|
if (file_exists($root . '/.truncate')) $data = substr($data, 0, (int) (strlen($data) / 2));
|
|
$existed = file_exists($file);
|
|
file_put_contents($file, $data);
|
|
http_response_code($existed ? 204 : 201);
|
|
break;
|
|
case 'DELETE':
|
|
if (!file_exists($file)) { http_response_code(404); exit; }
|
|
is_dir($file) ? @rmdir($file) : unlink($file);
|
|
http_response_code(204);
|
|
break;
|
|
case 'HEAD':
|
|
case 'GET':
|
|
if (!is_file($file)) { http_response_code(404); exit; }
|
|
header('Content-Length: ' . filesize($file));
|
|
if ($method === 'GET') readfile($file);
|
|
break;
|
|
case 'PROPFIND':
|
|
if (!file_exists($file) && $rel !== '') { http_response_code(404); exit; }
|
|
$xml = '<?xml version="1.0" encoding="utf-8"?><D:multistatus xmlns:D="DAV:">';
|
|
$xml .= $entry($path, basename($rel), $rel === '' ? null : $file);
|
|
if (is_dir($file) && ($_SERVER['HTTP_DEPTH'] ?? '1') !== '0') {
|
|
foreach (scandir($file) ?: [] as $e) {
|
|
if ($e === '.' || $e === '..' || $e[0] === '.') continue;
|
|
$xml .= $entry(rtrim($path, '/') . '/' . rawurlencode($e), $e, $file . '/' . $e);
|
|
}
|
|
}
|
|
$xml .= '</D:multistatus>';
|
|
http_response_code(207);
|
|
header('Content-Type: application/xml; charset=utf-8');
|
|
echo $xml;
|
|
break;
|
|
default:
|
|
http_response_code(405);
|
|
}
|