mirror of
https://github.com/kd2org/picodav.git
synced 2024-09-30 04:01:31 +02:00
Compare commits
No commits in common. "main" and "0.1.7" have entirely different histories.
1
Makefile
1
Makefile
@ -1,5 +1,4 @@
|
||||
all: clean index.php
|
||||
KD2FW_URL := https://fossil.kd2.org/kd2fw/doc/trunk/src/lib/KD2/
|
||||
|
||||
deps:
|
||||
@-mkdir -p lib/KD2/WebDAV
|
||||
|
@ -35,9 +35,9 @@ You can use any WebDAV client, but we recommend these:
|
||||
|
||||
It's really as simple as it says: just upload the [`index.php`](https://github.com/kd2org/picodav/raw/main/index.php) file to a directory on your web-server, and it will now be available via WebDAV!
|
||||
|
||||
If you are using Apache (version 2.3.9 or later is required), a .htaccess file will be created if it does not exist, to redirect requests to `index.php`. If not, you can use the provided `.htaccess` as a basis for your server configuration.
|
||||
If you are using Apache (version 2.3.9 or later is required), a .htaccess file will be created if it does not exist, to redirect requests to `index.php`. If not, you can use
|
||||
|
||||
The only requirement is PHP 7.4, or more recent (8.0-8.2 are also supported).
|
||||
The only requirement is PHP 7.4, or more recent (8.0 and 8.1 are also supported, not tested with PHP 8.2).
|
||||
|
||||
Note that by default, write access is disabled for security purposes. See below to enable write access.
|
||||
|
||||
@ -71,9 +71,9 @@ password = verySecret
|
||||
write = true
|
||||
```
|
||||
|
||||
Note that PicoDAV will replace this password with a hashed version the next time it is accessed, don't worry about that, this is for extra safety, just in case the `.picodav.ini` is accessed by a hacker if you made mistake in your web server configuration.
|
||||
Note that PicoDAV will replace this password with an encrypted version the next time it is accessed, don't worry about that, this is for extra safety, just in case the `.picodav.ini` is accessed by a hacker if you made mistake in your web server configuration.
|
||||
|
||||
Here is an example of the password once it has been hashed:
|
||||
Here is an example of the password once it has been encrypted:
|
||||
|
||||
```
|
||||
password = '$2y$10$fbdabTjNPN3gMAUlaSEoR.kKHLnh0yMGneuJ7P2AOhSSNr8gUaCPu'
|
||||
|
231
index.php
231
index.php
@ -25,16 +25,6 @@ namespace KD2\WebDAV
|
||||
'DAV::quota-available-bytes',
|
||||
];
|
||||
|
||||
const PROP_NAMESPACE_MICROSOFT = 'urn:schemas-microsoft-com:';
|
||||
|
||||
const MODIFICATION_TIME_PROPERTIES = [
|
||||
'DAV::lastmodified',
|
||||
'DAV::creationdate',
|
||||
'DAV::getlastmodified',
|
||||
'urn:schemas-microsoft-com::Win32LastModifiedTime',
|
||||
'urn:schemas-microsoft-com::Win32CreationTime',
|
||||
];
|
||||
|
||||
// Custom properties
|
||||
|
||||
const PROP_DIGEST_MD5 = 'urn:karadav:digest_md5';
|
||||
@ -66,8 +56,7 @@ namespace KD2\WebDAV
|
||||
|
||||
public function setBaseURI(string $uri): void
|
||||
{
|
||||
$this->base_uri = '/' . ltrim($uri, '/');
|
||||
$this->base_uri = rtrim($this->base_uri, '/') . '/';
|
||||
$this->base_uri = rtrim($uri, '/') . '/';
|
||||
}
|
||||
|
||||
protected function extendExecutionTime(): void
|
||||
@ -115,7 +104,7 @@ namespace KD2\WebDAV
|
||||
|
||||
foreach ($list as $file => $props) {
|
||||
if (null === $props) {
|
||||
$props = $this->storage->propfind(trim($uri . '/' . $file, '/'), self::BASIC_PROPERTIES, 0);
|
||||
$props = $this->storage->properties(trim($uri . '/' . $file, '/'), self::BASIC_PROPERTIES, 0);
|
||||
}
|
||||
|
||||
$collection = !empty($props['DAV::resourcetype']) && $props['DAV::resourcetype'] == 'collection';
|
||||
@ -220,7 +209,7 @@ namespace KD2\WebDAV
|
||||
elseif (!empty($_SERVER['HTTP_OC_CHECKSUM'])
|
||||
&& preg_match('/MD5:[a-f0-9]{32}|SHA1:[a-f0-9]{40}/', $_SERVER['HTTP_OC_CHECKSUM'], $match)) {
|
||||
$hash_algo = strtok($match[0], ':');
|
||||
$hash = strtok('');
|
||||
$hash = strtok(false);
|
||||
}
|
||||
|
||||
$uri = $this->_prefix($uri);
|
||||
@ -229,7 +218,7 @@ namespace KD2\WebDAV
|
||||
|
||||
if (!empty($_SERVER['HTTP_IF_MATCH'])) {
|
||||
$etag = trim($_SERVER['HTTP_IF_MATCH'], '" ');
|
||||
$prop = $this->storage->propfind($uri, ['DAV::getetag'], 0);
|
||||
$prop = $this->storage->properties($uri, ['DAV::getetag'], 0);
|
||||
|
||||
if (!empty($prop['DAV::getetag']) && $prop['DAV::getetag'] != $etag) {
|
||||
throw new Exception('ETag did not match condition', 412);
|
||||
@ -240,6 +229,10 @@ namespace KD2\WebDAV
|
||||
// This expects a UNIX timestamp
|
||||
$mtime = (int)($_SERVER['HTTP_X_OC_MTIME'] ?? 0) ?: null;
|
||||
|
||||
if ($mtime) {
|
||||
header('X-OC-MTime: accepted');
|
||||
}
|
||||
|
||||
$this->extendExecutionTime();
|
||||
|
||||
$stream = fopen('php://input', 'r');
|
||||
@ -259,17 +252,9 @@ namespace KD2\WebDAV
|
||||
fseek($stream, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
$created = $this->storage->put($uri, $stream, $hash_algo, $hash);
|
||||
$created = $this->storage->put($uri, $stream, $hash_algo, $hash, $mtime);
|
||||
|
||||
if ($mtime) {
|
||||
$mtime = new \DateTime('@' . $mtime);
|
||||
|
||||
if ($this->storage->touch($uri, $mtime)) {
|
||||
header('X-OC-MTime: accepted');
|
||||
}
|
||||
}
|
||||
|
||||
$prop = $this->storage->propfind($uri, ['DAV::getetag'], 0);
|
||||
$prop = $this->storage->properties($uri, ['DAV::getetag'], 0);
|
||||
|
||||
if (!empty($prop['DAV::getetag'])) {
|
||||
$value = $prop['DAV::getetag'];
|
||||
@ -297,7 +282,7 @@ namespace KD2\WebDAV
|
||||
$requested_props[] = self::PROP_DIGEST_MD5;
|
||||
}
|
||||
|
||||
$props = $this->storage->propfind($uri, $requested_props, 0);
|
||||
$props = $this->storage->properties($uri, $requested_props, 0);
|
||||
|
||||
if (!$props) {
|
||||
throw new Exception('Resource Not Found', 404);
|
||||
@ -378,7 +363,7 @@ namespace KD2\WebDAV
|
||||
}
|
||||
|
||||
if (!isset($file['content']) && !isset($file['resource']) && !isset($file['path'])) {
|
||||
throw new \RuntimeException('Invalid file array returned by ::get(): ' . print_r($file, true));
|
||||
throw new \RuntimeException('Invalid file array returned by ::get()');
|
||||
}
|
||||
|
||||
$this->extendExecutionTime();
|
||||
@ -588,7 +573,7 @@ namespace KD2\WebDAV
|
||||
// should do just nothing, see 'depth_zero_copy' test in litmus
|
||||
if ($depth == 0
|
||||
&& $this->storage->exists($destination)
|
||||
&& current($this->storage->propfind($destination, ['DAV::resourcetype'], 0)) == 'collection') {
|
||||
&& current($this->storage->properties($destination, ['DAV::resourcetype'], 0)) == 'collection') {
|
||||
$overwritten = $this->storage->exists($uri);
|
||||
}
|
||||
else {
|
||||
@ -707,24 +692,18 @@ namespace KD2\WebDAV
|
||||
$requested_keys = $requested ? array_keys($requested) : null;
|
||||
|
||||
// Find root element properties
|
||||
$properties = $this->storage->propfind($uri, $requested_keys, $depth);
|
||||
$properties = $this->storage->properties($uri, $requested_keys, $depth);
|
||||
|
||||
if (null === $properties) {
|
||||
throw new Exception('This does not exist', 404);
|
||||
}
|
||||
|
||||
if (isset($properties['DAV::getlastmodified'])) {
|
||||
foreach (self::MODIFICATION_TIME_PROPERTIES as $name) {
|
||||
$properties[$name] = $properties['DAV::getlastmodified'];
|
||||
}
|
||||
}
|
||||
|
||||
$items = [$uri => $properties];
|
||||
|
||||
if ($depth) {
|
||||
foreach ($this->storage->list($uri, $requested) as $file => $properties) {
|
||||
$path = trim($uri . '/' . $file, '/');
|
||||
$properties = $properties ?? $this->storage->propfind($path, $requested_keys, 0);
|
||||
$properties = $properties ?? $this->storage->properties($path, $requested_keys, 0);
|
||||
|
||||
if (!$properties) {
|
||||
$this->log('!!! Cannot find "%s"', $path);
|
||||
@ -968,93 +947,19 @@ namespace KD2\WebDAV
|
||||
$uri = $this->_prefix($uri);
|
||||
$this->checkLock($uri);
|
||||
|
||||
$prefix = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
|
||||
$prefix.= '<d:multistatus xmlns:d="DAV:"';
|
||||
$suffix = "</d:multistatus>\n";
|
||||
|
||||
$body = file_get_contents('php://input');
|
||||
|
||||
$properties = $this->parsePropPatch($body);
|
||||
$root_namespaces = [];
|
||||
$i = 0;
|
||||
$set_time = null;
|
||||
$set_time_name = null;
|
||||
|
||||
foreach ($properties as $name => $value) {
|
||||
$pos = strrpos($name, ':');
|
||||
$ns = substr($name, 0, $pos);
|
||||
|
||||
if (!array_key_exists($ns, $root_namespaces)) {
|
||||
$alias = 'rns' . $i++;
|
||||
$root_namespaces[$ns] = $alias;
|
||||
$prefix .= sprintf(' xmlns:%s="%s"', $alias, htmlspecialchars($ns, ENT_XML1));
|
||||
}
|
||||
}
|
||||
|
||||
// See if the client wants to set the modification time
|
||||
foreach (self::MODIFICATION_TIME_PROPERTIES as $name) {
|
||||
if (!array_key_exists($name, $properties) || $value['action'] !== 'set' || empty($value['content'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ts = $value['content'];
|
||||
|
||||
if (ctype_digit($ts)) {
|
||||
$ts = '@' . $ts;
|
||||
}
|
||||
|
||||
$set_time = new \DateTime($value['content']);
|
||||
$set_time_name = $name;
|
||||
}
|
||||
|
||||
$prefix .= sprintf(">\n<d:response>\n <d:href>%s</d:href>\n", htmlspecialchars($url, ENT_XML1));
|
||||
$this->storage->setProperties($uri, $body);
|
||||
|
||||
// http_response_code doesn't know the 207 status code
|
||||
header('HTTP/1.1 207 Multi-Status', true);
|
||||
header('Content-Type: application/xml; charset=utf-8', true);
|
||||
header('Content-Type: application/xml; charset=utf-8');
|
||||
|
||||
if (!count($properties)) {
|
||||
return $prefix . $suffix;
|
||||
}
|
||||
$out = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
|
||||
$out .= '<d:multistatus xmlns:d="DAV:">';
|
||||
$out .= '</d:multistatus>';
|
||||
|
||||
if ($set_time) {
|
||||
unset($properties[$set_time_name]);
|
||||
}
|
||||
|
||||
$return = $this->storage->proppatch($uri, $properties);
|
||||
|
||||
if ($set_time && $this->touch($uri, $set_time)) {
|
||||
$return[$set_time_name] = 200;
|
||||
}
|
||||
|
||||
$out = '';
|
||||
|
||||
static $messages = [
|
||||
200 => 'OK',
|
||||
403 => 'Forbidden',
|
||||
409 => 'Conflict',
|
||||
427 => 'Failed Dependency',
|
||||
507 => 'Insufficient Storage',
|
||||
];
|
||||
|
||||
foreach ($return as $name => $status) {
|
||||
$pos = strrpos($name, ':');
|
||||
$ns = substr($name, 0, $pos);
|
||||
$name = substr($name, $pos + 1);
|
||||
|
||||
$out .= " <d:propstat>\n <d:prop>";
|
||||
$out .= sprintf("<%s:%s /></d:prop>\n <d:status>HTTP/1.1 %d %s</d:status>",
|
||||
$root_namespaces[$ns],
|
||||
$name,
|
||||
$status,
|
||||
$messages[$status] ?? ''
|
||||
);
|
||||
$out .= "\n </d:propstat>\n";
|
||||
}
|
||||
|
||||
$out .= "</d:response>\n";
|
||||
|
||||
return $prefix . $out . $suffix;
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function http_lock(string $uri): ?string
|
||||
@ -1205,7 +1110,7 @@ namespace KD2\WebDAV
|
||||
&& preg_match('/\(<([^>]*)>\s+\["([^""]+)"\]\)/', $_SERVER['HTTP_IF'], $match)) {
|
||||
$token = $match[1];
|
||||
$request_etag = $match[2];
|
||||
$etag = current($this->storage->propfind($uri, ['DAV::getetag'], 0));
|
||||
$etag = current($this->storage->properties($uri, ['DAV::getetag'], 0));
|
||||
|
||||
if ($request_etag != $etag) {
|
||||
throw new Exception('Resource is locked and etag does not match', 412);
|
||||
@ -1258,10 +1163,9 @@ namespace KD2\WebDAV
|
||||
{
|
||||
$uri = parse_url($source, PHP_URL_PATH);
|
||||
$uri = rawurldecode($uri);
|
||||
$uri = trim($uri, '/');
|
||||
$uri = '/' . $uri;
|
||||
$uri = rtrim($uri, '/');
|
||||
|
||||
if ($uri . '/' === $this->base_uri) {
|
||||
if ($uri . '/' == $this->base_uri) {
|
||||
$uri .= '/';
|
||||
}
|
||||
|
||||
@ -1286,7 +1190,6 @@ namespace KD2\WebDAV
|
||||
$uri = $_SERVER['REQUEST_URI'] ?? '/';
|
||||
}
|
||||
|
||||
$uri = '/' . ltrim($uri, '/');
|
||||
$this->original_uri = $uri;
|
||||
|
||||
if ($uri . '/' == $this->base_uri) {
|
||||
@ -1297,7 +1200,7 @@ namespace KD2\WebDAV
|
||||
$uri = substr($uri, strlen($this->base_uri));
|
||||
}
|
||||
else {
|
||||
$this->log('<= %s is not a managed URL (%s)', $uri, $this->base_uri);
|
||||
$this->log('<= %s is not a managed URL', $uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1390,14 +1293,14 @@ namespace KD2\WebDAV
|
||||
|
||||
abstract public function exists(string $uri): bool;
|
||||
|
||||
abstract public function propfind(string $uri, ?array $requested_properties, int $depth): ?array;
|
||||
abstract public function properties(string $uri, ?array $requested_properties, int $depth): ?array;
|
||||
|
||||
public function proppatch(string $uri, array $properties): array
|
||||
public function setProperties(string $uri, string $body): void
|
||||
{
|
||||
// By default, properties are not saved
|
||||
}
|
||||
|
||||
abstract public function put(string $uri, $pointer, ?string $hash_algo, ?string $hash): bool;
|
||||
abstract public function put(string $uri, $pointer, ?string $hash_algo, ?string $hash, ?int $mtime): bool;
|
||||
|
||||
abstract public function delete(string $uri): void;
|
||||
|
||||
@ -1409,8 +1312,6 @@ namespace KD2\WebDAV
|
||||
|
||||
abstract public function list(string $uri, array $properties): iterable;
|
||||
|
||||
abstract public function touch(string $uri, \DateTimeInterface $timestamp): bool;
|
||||
|
||||
public function lock(string $uri, string $token, string $scope): void
|
||||
{
|
||||
// By default locking is not implemented
|
||||
@ -1466,15 +1367,13 @@ namespace PicoDAV
|
||||
$user = $_SERVER['PHP_AUTH_USER'] ?? null;
|
||||
$password = $_SERVER['PHP_AUTH_PW'] ?? null;
|
||||
|
||||
if (!array_key_exists($user, $this->users)) {
|
||||
$hash = $this->users[$user]['password'] ?? null;
|
||||
|
||||
if (!$hash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash = $this->users[$user]['password'] ?? null;
|
||||
|
||||
// If no password is set, we accept any password as we consider that a .htaccess/.htpasswd
|
||||
// access has been granted
|
||||
if (null !== $hash && !password_verify($password, $hash)) {
|
||||
if (!password_verify($password, $hash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1633,7 +1532,12 @@ namespace PicoDAV
|
||||
case 'DAV::resourcetype':
|
||||
return is_dir($target) ? 'collection' : '';
|
||||
case 'DAV::getlastmodified':
|
||||
if (!$uri && $depth == 0 && is_dir($target)) {
|
||||
$mtime = self::getDirectoryMTime($target);
|
||||
}
|
||||
else {
|
||||
$mtime = filemtime($target);
|
||||
}
|
||||
|
||||
if (!$mtime) {
|
||||
return null;
|
||||
@ -1681,7 +1585,7 @@ namespace PicoDAV
|
||||
return null;
|
||||
}
|
||||
|
||||
public function propfind(string $uri, ?array $properties, int $depth): ?array
|
||||
public function properties(string $uri, ?array $properties, int $depth): ?array
|
||||
{
|
||||
$target = $this->path . $uri;
|
||||
|
||||
@ -1706,7 +1610,7 @@ namespace PicoDAV
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function put(string $uri, $pointer, ?string $hash_algo, ?string $hash): bool
|
||||
public function put(string $uri, $pointer, ?string $hash_algo, ?string $hash, ?int $mtime): bool
|
||||
{
|
||||
if (preg_match(self::PUT_IGNORE_PATTERN, basename($uri))) {
|
||||
return false;
|
||||
@ -1766,6 +1670,10 @@ namespace PicoDAV
|
||||
rename($tmp_file, $target);
|
||||
}
|
||||
|
||||
if ($mtime) {
|
||||
@touch($target, $mtime);
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
@ -1892,10 +1800,28 @@ namespace PicoDAV
|
||||
mkdir($target, 0770);
|
||||
}
|
||||
|
||||
public function touch(string $uri, \DateTimeInterface $datetime): bool
|
||||
static public function getDirectoryMTime(string $path): int
|
||||
{
|
||||
$target = $this->path . $uri;
|
||||
return @touch($target, $datetime->getTimestamp());
|
||||
$last = 0;
|
||||
$path = rtrim($path, '/');
|
||||
|
||||
foreach (self::glob($path, '/*', GLOB_NOSORT) as $f) {
|
||||
if (is_dir($f)) {
|
||||
$m = self::getDirectoryMTime($f);
|
||||
|
||||
if ($m > $last) {
|
||||
$last = $m;
|
||||
}
|
||||
}
|
||||
|
||||
$m = filemtime($f);
|
||||
|
||||
if ($m > $last) {
|
||||
$last = $m;
|
||||
}
|
||||
}
|
||||
|
||||
return $last;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2014,11 +1940,11 @@ RewriteRule ^.*$ /index.php [END]
|
||||
$fp = fopen(__FILE__, 'r');
|
||||
|
||||
if ($relative_uri == '.webdav/webdav.js') {
|
||||
fseek($fp, 55024, SEEK_SET);
|
||||
echo fread($fp, 27891);
|
||||
fseek($fp, 52608, SEEK_SET);
|
||||
echo fread($fp, 27789);
|
||||
}
|
||||
else {
|
||||
fseek($fp, 55024 + 27891, SEEK_SET);
|
||||
fseek($fp, 52608 + 27789, SEEK_SET);
|
||||
echo fread($fp, 7004);
|
||||
}
|
||||
|
||||
@ -2154,14 +2080,14 @@ const WebDAVNavigator = (url, options) => {
|
||||
const dir_row_tpl = `<tr data-permissions="%permissions%"><td class="thumb"><span class="icon dir"><b>%icon%</b></span></td><th colspan="2"><a href="%uri%">%name%</a></th><td>%modified%</td><td class="buttons"><div></div></td></tr>`;
|
||||
const file_row_tpl = `<tr data-permissions="%permissions%" data-mime="%mime%" data-size="%size%"><td class="thumb"><span class="icon %icon%"><b>%icon%</b></span></td><th><a href="%uri%">%name%</a></th><td class="size">%size_bytes%</td><td>%modified%</td><td class="buttons"><div><a href="%uri%" download class="btn">${_('Download')}</a></div></td></tr>`;
|
||||
|
||||
const propfind_tpl = '<'+ `?xml version="1.0" encoding="UTF-8"?>
|
||||
const propfind_tpl = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<D:propfind xmlns:D="DAV:" xmlns:oc="http://owncloud.org/ns">
|
||||
<D:prop>
|
||||
<D:getlastmodified/><D:getcontenttype/><D:getcontentlength/><D:resourcetype/><D:displayname/><oc:permissions/>
|
||||
</D:prop>
|
||||
</D:propfind>`;
|
||||
|
||||
const wopi_propfind_tpl = '<' + `?xml version="1.0" encoding="UTF-8"?>
|
||||
const wopi_propfind_tpl = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<D:propfind xmlns:D="DAV:" xmlns:W="https://interoperability.blob.core.windows.net/files/MS-WOPI/">
|
||||
<D:prop>
|
||||
<W:file-url/><W:token/><W:token-ttl/>
|
||||
@ -2261,14 +2187,12 @@ const WebDAVNavigator = (url, options) => {
|
||||
};
|
||||
|
||||
const wopi_init = async () => {
|
||||
try {
|
||||
var d = await reqXML('GET', wopi_discovery_url);
|
||||
}
|
||||
catch (e) {
|
||||
reloadListing();
|
||||
if (!wopi_discovery_url) {
|
||||
return;
|
||||
}
|
||||
|
||||
var d = await reqXML('GET', wopi_discovery_url);
|
||||
|
||||
d.querySelectorAll('app').forEach(app => {
|
||||
var mime = (a = app.getAttribute('name').match(/^.*\/.*$/)) ? a[0] : null;
|
||||
wopi_mimes[mime] = {};
|
||||
@ -2457,10 +2381,6 @@ const WebDAVNavigator = (url, options) => {
|
||||
};
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (isNaN(date)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var now = new Date;
|
||||
var nb_hours = (+(now) - +(date)) / 3600 / 1000;
|
||||
|
||||
@ -2651,7 +2571,7 @@ const WebDAVNavigator = (url, options) => {
|
||||
$('.download_all').onclick = download_all;
|
||||
}
|
||||
|
||||
if (!root_permissions || root_permissions.indexOf('C') != -1 || root_permissions.indexOf('K') != -1) {
|
||||
if (!root_permissions || root_permissions.indexOf('CK') != -1) {
|
||||
$('.upload').insertAdjacentHTML('afterbegin', create_buttons);
|
||||
|
||||
$('.mkdir').onclick = () => {
|
||||
@ -2886,10 +2806,11 @@ const WebDAVNavigator = (url, options) => {
|
||||
|
||||
document.querySelector('html').innerHTML = html_tpl;
|
||||
|
||||
// Wait for WOPI discovery before creating the list
|
||||
if (wopi_discovery_url) {
|
||||
// Wait for WOPI discovery before creating the list
|
||||
wopi_init();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
reloadListing();
|
||||
}
|
||||
|
||||
|
47
server.php
47
server.php
@ -43,15 +43,13 @@ namespace PicoDAV
|
||||
$user = $_SERVER['PHP_AUTH_USER'] ?? null;
|
||||
$password = $_SERVER['PHP_AUTH_PW'] ?? null;
|
||||
|
||||
if (!array_key_exists($user, $this->users)) {
|
||||
$hash = $this->users[$user]['password'] ?? null;
|
||||
|
||||
if (!$hash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash = $this->users[$user]['password'] ?? null;
|
||||
|
||||
// If no password is set, we accept any password as we consider that a .htaccess/.htpasswd
|
||||
// access has been granted
|
||||
if (null !== $hash && !password_verify($password, $hash)) {
|
||||
if (!password_verify($password, $hash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -210,7 +208,12 @@ namespace PicoDAV
|
||||
case 'DAV::resourcetype':
|
||||
return is_dir($target) ? 'collection' : '';
|
||||
case 'DAV::getlastmodified':
|
||||
if (!$uri && $depth == 0 && is_dir($target)) {
|
||||
$mtime = self::getDirectoryMTime($target);
|
||||
}
|
||||
else {
|
||||
$mtime = filemtime($target);
|
||||
}
|
||||
|
||||
if (!$mtime) {
|
||||
return null;
|
||||
@ -258,7 +261,7 @@ namespace PicoDAV
|
||||
return null;
|
||||
}
|
||||
|
||||
public function propfind(string $uri, ?array $properties, int $depth): ?array
|
||||
public function properties(string $uri, ?array $properties, int $depth): ?array
|
||||
{
|
||||
$target = $this->path . $uri;
|
||||
|
||||
@ -283,7 +286,7 @@ namespace PicoDAV
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function put(string $uri, $pointer, ?string $hash_algo, ?string $hash): bool
|
||||
public function put(string $uri, $pointer, ?string $hash_algo, ?string $hash, ?int $mtime): bool
|
||||
{
|
||||
if (preg_match(self::PUT_IGNORE_PATTERN, basename($uri))) {
|
||||
return false;
|
||||
@ -343,6 +346,10 @@ namespace PicoDAV
|
||||
rename($tmp_file, $target);
|
||||
}
|
||||
|
||||
if ($mtime) {
|
||||
@touch($target, $mtime);
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
@ -469,10 +476,28 @@ namespace PicoDAV
|
||||
mkdir($target, 0770);
|
||||
}
|
||||
|
||||
public function touch(string $uri, \DateTimeInterface $datetime): bool
|
||||
static public function getDirectoryMTime(string $path): int
|
||||
{
|
||||
$target = $this->path . $uri;
|
||||
return @touch($target, $datetime->getTimestamp());
|
||||
$last = 0;
|
||||
$path = rtrim($path, '/');
|
||||
|
||||
foreach (self::glob($path, '/*', GLOB_NOSORT) as $f) {
|
||||
if (is_dir($f)) {
|
||||
$m = self::getDirectoryMTime($f);
|
||||
|
||||
if ($m > $last) {
|
||||
$last = $m;
|
||||
}
|
||||
}
|
||||
|
||||
$m = filemtime($f);
|
||||
|
||||
if ($m > $last) {
|
||||
$last = $m;
|
||||
}
|
||||
}
|
||||
|
||||
return $last;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user