Update dependencies

This commit is contained in:
bohwaz 2023-12-31 15:48:50 +01:00
parent 97479f801c
commit 23da9a2d33
4 changed files with 146 additions and 30 deletions

View file

@ -377,18 +377,18 @@ class ErrorManager
$boundary = sprintf('-----=%s', md5(uniqid(rand())));
$header = sprintf("MIME-Version: 1.0\r\nFrom: \"%s\" <%s>\r\nIn-Reply-To: <%s>\r\nMessage-Id: <%s>\r\n", $from, self::$email_errors, $msgid, $msgid);
$header.= sprintf("Content-Type: multipart/mixed; boundary=\"%s\"\r\n", $boundary);
$header.= sprintf("Content-Type: multipart/alternative; boundary=\"%s\"\r\n", $boundary);
$header.= "\r\n";
$msg = "This message contains multiple MIME parts.\r\n\r\n";
$msg.= sprintf("--%s\r\n", $boundary);
$msg.= "Content-Type: text/plain; charset=\"utf-8\"\r\n";
$msg.= "Content-Transfer-Encoding: 8bit\r\n\r\n";
$msg.= $log . "\r\n\r\n";
$msg.= wordwrap($log, 990) . "\r\n\r\n";
$msg.= sprintf("--%s\r\n", $boundary);
$msg.= "Content-Type: text/html; charset=\"utf-8\"\r\n";
$msg.= "Content-Transfer-Encoding: 8bit\r\n\r\n";
$msg.= $html . "\r\n\r\n";
$msg.= wordwrap($html, 990) . "\r\n\r\n";
$msg.= sprintf("--%s--", $boundary);
mail(self::$email_errors, sprintf('Error #%s: %s', $report->context->id, $title), $msg, $header);

View file

@ -45,14 +45,20 @@ abstract class AbstractStorage
* Or it MUST return an array, where the keys are 'namespace_url:property_name' tuples,
* and the value is the content of the property tag.
*/
abstract public function properties(string $uri, ?array $requested_properties, int $depth): ?array;
abstract public function propfind(string $uri, ?array $requested_properties, int $depth): ?array;
/**
* Store resource properties
* @param string $uri
* @param string $body XML PROPPATCH request, parsing it is up to you
* @param string $properties List of PROPPATCH request:
* key = property name (namespace:tag_name)
* value = array ['action' => 'set'|'remove', 'attributes' => array|null, 'content' => string|null]
*
* Should return an array:
* key = property name
* value = HTTP code (200 = OK, 403 = forbidden, 409 = conflict)
*/
public function setProperties(string $uri, string $body): void
public function proppatch(string $uri, array $properties): array
{
// By default, properties are not saved
}
@ -65,10 +71,9 @@ abstract class AbstractStorage
* @param null|string $hash A hash of the resource to store.
* If it is supplied and doesn't match the uploaded file, this method should fail with a
* 400 code WebDAV exception and not proceed to store the resource.
* @param null|int $mtime The modification timestamp to set on the file
* @return bool Return TRUE if the resource has been created, or FALSE it has just been updated.
*/
abstract public function put(string $uri, $pointer, ?string $hash_algo, ?string $hash, ?int $mtime): bool;
abstract public function put(string $uri, $pointer, ?string $hash_algo, ?string $hash): bool;
/**
* Delete a resource
@ -113,6 +118,15 @@ abstract class AbstractStorage
*/
abstract public function list(string $uri, array $properties): iterable;
/**
* Change the file modification time for target $uri
*
* @param string $uri
* @param \DateTimeInterface $datetime
* @return bool TRUE if the file modification time has been set correctly
*/
abstract public function touch(string $uri, \DateTimeInterface $timestamp): bool;
/**
* Lock the requested resource
* @param string $uri Requested resource

View file

@ -67,6 +67,22 @@ class Server
'DAV::quota-available-bytes',
];
const PROP_NAMESPACE_MICROSOFT = 'urn:schemas-microsoft-com:';
/**
* File modification time accepted properties
* @see https://github.com/mar10/wsgidav/issues/112
* @see https://github.com/sabre-io/dav/issues/1277
* @see https://github.com/owncloud/core/issues/33502
*/
const MODIFICATION_TIME_PROPERTIES = [
'DAV::lastmodified',
'DAV::creationdate',
'DAV::getlastmodified',
'urn:schemas-microsoft-com::Win32LastModifiedTime',
'urn:schemas-microsoft-com::Win32CreationTime',
];
// Custom properties
/**
* File MD5 hash
@ -170,7 +186,7 @@ class Server
foreach ($list as $file => $props) {
if (null === $props) {
$props = $this->storage->properties(trim($uri . '/' . $file, '/'), self::BASIC_PROPERTIES, 0);
$props = $this->storage->propfind(trim($uri . '/' . $file, '/'), self::BASIC_PROPERTIES, 0);
}
$collection = !empty($props['DAV::resourcetype']) && $props['DAV::resourcetype'] == 'collection';
@ -276,7 +292,7 @@ class Server
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(false);
$hash = strtok('');
}
$uri = $this->_prefix($uri);
@ -285,7 +301,7 @@ class Server
if (!empty($_SERVER['HTTP_IF_MATCH'])) {
$etag = trim($_SERVER['HTTP_IF_MATCH'], '" ');
$prop = $this->storage->properties($uri, ['DAV::getetag'], 0);
$prop = $this->storage->propfind($uri, ['DAV::getetag'], 0);
if (!empty($prop['DAV::getetag']) && $prop['DAV::getetag'] != $etag) {
throw new Exception('ETag did not match condition', 412);
@ -296,10 +312,6 @@ class Server
// 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');
@ -319,9 +331,18 @@ class Server
fseek($stream, 0, SEEK_SET);
}
$created = $this->storage->put($uri, $stream, $hash_algo, $hash, $mtime);
$created = $this->storage->put($uri, $stream, $hash_algo, $hash);
$prop = $this->storage->properties($uri, ['DAV::getetag'], 0);
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);
if (!empty($prop['DAV::getetag'])) {
$value = $prop['DAV::getetag'];
@ -349,7 +370,7 @@ class Server
$requested_props[] = self::PROP_DIGEST_MD5;
}
$props = $this->storage->properties($uri, $requested_props, 0);
$props = $this->storage->propfind($uri, $requested_props, 0);
if (!$props) {
throw new Exception('Resource Not Found', 404);
@ -641,7 +662,7 @@ class Server
// should do just nothing, see 'depth_zero_copy' test in litmus
if ($depth == 0
&& $this->storage->exists($destination)
&& current($this->storage->properties($destination, ['DAV::resourcetype'], 0)) == 'collection') {
&& current($this->storage->propfind($destination, ['DAV::resourcetype'], 0)) == 'collection') {
$overwritten = $this->storage->exists($uri);
}
else {
@ -765,18 +786,24 @@ class Server
$requested_keys = $requested ? array_keys($requested) : null;
// Find root element properties
$properties = $this->storage->properties($uri, $requested_keys, $depth);
$properties = $this->storage->propfind($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->properties($path, $requested_keys, 0);
$properties = $properties ?? $this->storage->propfind($path, $requested_keys, 0);
if (!$properties) {
$this->log('!!! Cannot find "%s"', $path);
@ -1020,19 +1047,93 @@ class Server
$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');
$this->storage->setProperties($uri, $body);
$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));
// 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');
header('Content-Type: application/xml; charset=utf-8', true);
$out = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
$out .= '<d:multistatus xmlns:d="DAV:">';
$out .= '</d:multistatus>';
if (!count($properties)) {
return $prefix . $suffix;
}
return $out;
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;
}
public function http_lock(string $uri): ?string
@ -1190,7 +1291,7 @@ class Server
&& preg_match('/\(<([^>]*)>\s+\["([^""]+)"\]\)/', $_SERVER['HTTP_IF'], $match)) {
$token = $match[1];
$request_etag = $match[2];
$etag = current($this->storage->properties($uri, ['DAV::getetag'], 0));
$etag = current($this->storage->propfind($uri, ['DAV::getetag'], 0));
if ($request_etag != $etag) {
throw new Exception('Resource is locked and etag does not match', 412);
@ -1271,6 +1372,7 @@ class Server
$uri = $_SERVER['REQUEST_URI'] ?? '/';
}
$uri = '/' . ltrim($uri, '/');
$this->original_uri = $uri;
if ($uri . '/' == $this->base_uri) {
@ -1281,7 +1383,7 @@ class Server
$uri = substr($uri, strlen($this->base_uri));
}
else {
$this->log('<= %s is not a managed URL', $uri);
$this->log('<= %s is not a managed URL (%s)', $uri, $this->base_uri);
return false;
}

View file

@ -84,7 +84,7 @@ class WOPI
$method = $_SERVER['REQUEST_METHOD'];
$id = rawurldecode(strtok($uri, '/'));
$action = trim(strtok(false), '/');
$action = trim(strtok(''), '/');
$uri = $this->storage->getWopiURI($id, $auth_token);