mirror of
https://github.com/owncloud/ocis
synced 2026-04-25 17:25:21 +02:00
reorganize test folders within the acceptance directory
Signed-off-by: prashant-gurung899 <prasantgrg777@gmail.com>
This commit is contained in:
972
tests/acceptance/TestHelpers/WebDavHelper.php
Normal file
972
tests/acceptance/TestHelpers/WebDavHelper.php
Normal file
@@ -0,0 +1,972 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
* @copyright Copyright (c) 2017 Artur Neumann artur@jankaritech.com
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License,
|
||||
* as published by the Free Software Foundation;
|
||||
* either version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
namespace TestHelpers;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use InvalidArgumentException;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Helper to make WebDav Requests
|
||||
*
|
||||
* @author Artur Neumann <artur@jankaritech.com>
|
||||
*
|
||||
*/
|
||||
class WebDavHelper {
|
||||
public const DAV_VERSION_OLD = 1;
|
||||
public const DAV_VERSION_NEW = 2;
|
||||
public const DAV_VERSION_SPACES = 3;
|
||||
public static string $SPACE_ID_FROM_OCIS = '';
|
||||
|
||||
/**
|
||||
* @var array of users with their different space ids
|
||||
*/
|
||||
public static array $spacesIdRef = [];
|
||||
|
||||
/**
|
||||
* clear space id reference for user
|
||||
*
|
||||
* @param string|null $user
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function removeSpaceIdReferenceForUser(
|
||||
?string $user
|
||||
):void {
|
||||
if (\array_key_exists($user, self::$spacesIdRef)) {
|
||||
unset(self::$spacesIdRef[$user]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespaceString
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function parseNamespace(string $namespaceString): object {
|
||||
//calculate the namespace prefix and namespace
|
||||
$matches = [];
|
||||
\preg_match("/^(.*)='(.*)'$/", $namespaceString, $matches);
|
||||
return (object)["namespace" => $matches[2], "prefix" => $matches[1]];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the id of a file
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $path
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $davPathVersionToUse
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public static function getFileIdForPath(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $path,
|
||||
?string $xRequestId = '',
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW
|
||||
): string {
|
||||
$body
|
||||
= '<?xml version="1.0"?>
|
||||
<d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
|
||||
<d:prop>
|
||||
<oc:fileid />
|
||||
</d:prop>
|
||||
</d:propfind>';
|
||||
$response = self::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PROPFIND",
|
||||
$path,
|
||||
null,
|
||||
$xRequestId,
|
||||
$body,
|
||||
$davPathVersionToUse
|
||||
);
|
||||
\preg_match(
|
||||
'/\<oc:fileid\>([^\<]*)\<\/oc:fileid\>/',
|
||||
$response->getBody()->getContents(),
|
||||
$matches
|
||||
);
|
||||
|
||||
if (!isset($matches[1])) {
|
||||
throw new Exception("could not find fileId of $path");
|
||||
}
|
||||
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns body for propfind
|
||||
*
|
||||
* @param array|null $properties
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getBodyForPropfind(?array $properties): string {
|
||||
$propertyBody = "";
|
||||
$extraNamespaces = "";
|
||||
foreach ($properties as $namespaceString => $property) {
|
||||
if (\is_int($namespaceString)) {
|
||||
//default namespace prefix if the property has no array key
|
||||
//also used if no prefix is given in the property value
|
||||
$namespacePrefix = null;
|
||||
} else {
|
||||
$ns = self::parseNamespace($namespaceString);
|
||||
$namespacePrefix = $ns->prefix;
|
||||
$extraNamespaces .= " xmlns:$namespacePrefix=\"$ns->namespace\" ";
|
||||
}
|
||||
//if a namespace prefix is given in the property value use that
|
||||
if (\strpos($property, ":") !== false) {
|
||||
$propertyParts = \explode(":", $property);
|
||||
$namespacePrefix = $propertyParts[0];
|
||||
$property = $propertyParts[1];
|
||||
}
|
||||
|
||||
if ($namespacePrefix) {
|
||||
$propertyBody .= "<$namespacePrefix:$property/>";
|
||||
} else {
|
||||
$propertyBody .= "<$property/>";
|
||||
}
|
||||
}
|
||||
$body = "<?xml version=\"1.0\"?>
|
||||
<d:propfind
|
||||
xmlns:d=\"DAV:\"
|
||||
xmlns:oc=\"http://owncloud.org/ns\"
|
||||
xmlns:ocs=\"http://open-collaboration-services.org/ns\"
|
||||
$extraNamespaces>
|
||||
<d:prop>$propertyBody</d:prop>
|
||||
</d:propfind>";
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a PROPFIND request
|
||||
* with these registered namespaces:
|
||||
* | prefix | namespace |
|
||||
* | d | DAV: |
|
||||
* | oc | http://owncloud.org/ns |
|
||||
* | ocs | http://open-collaboration-services.org/ns |
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $path
|
||||
* @param string[] $properties
|
||||
* string can contain namespace prefix,
|
||||
* if no prefix is given 'd:' is used as prefix
|
||||
* if an associative array is used, then the key will be used as namespace
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $folderDepth
|
||||
* @param string|null $type
|
||||
* @param int|null $davPathVersionToUse
|
||||
* @param string|null $doDavRequestAsUser
|
||||
* @param array|null $headers
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws Exception
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function propfind(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $path,
|
||||
?array $properties,
|
||||
?string $xRequestId = '',
|
||||
?string $folderDepth = '1',
|
||||
?string $type = "files",
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW,
|
||||
?string $doDavRequestAsUser = null,
|
||||
?array $headers = []
|
||||
):ResponseInterface {
|
||||
$body = self::getBodyForPropfind($properties);
|
||||
$folderDepth = (string) $folderDepth;
|
||||
if ($folderDepth !== '0' && $folderDepth !== '1' && $folderDepth !== 'infinity') {
|
||||
if ($folderDepth !== '') {
|
||||
throw new InvalidArgumentException('Invalid depth value ' . $folderDepth);
|
||||
}
|
||||
$folderDepth = '1'; // oCIS server's default value
|
||||
}
|
||||
$headers['Depth'] = $folderDepth;
|
||||
return self::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PROPFIND",
|
||||
$path,
|
||||
$headers,
|
||||
$xRequestId,
|
||||
$body,
|
||||
$davPathVersionToUse,
|
||||
$type,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
[],
|
||||
$doDavRequestAsUser
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $path
|
||||
* @param string|null $propertyName
|
||||
* @param string|null $propertyValue
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null $namespaceString string containing prefix and namespace
|
||||
* e.g "x1='http://whatever.org/ns'"
|
||||
* @param int|null $davPathVersionToUse
|
||||
* @param string|null $type
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function proppatch(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $path,
|
||||
?string $propertyName,
|
||||
?string $propertyValue,
|
||||
?string $xRequestId = '',
|
||||
?string $namespaceString = null,
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW,
|
||||
?string $type="files"
|
||||
):ResponseInterface {
|
||||
if ($namespaceString !== null) {
|
||||
$ns = self::parseNamespace($namespaceString);
|
||||
$propertyBody = "<$ns->prefix:$propertyName" .
|
||||
" xmlns:$ns->prefix=\"$ns->namespace\">" .
|
||||
"$propertyValue" .
|
||||
"</$ns->prefix:$propertyName>";
|
||||
} else {
|
||||
$propertyBody = "<$propertyName>$propertyValue</$propertyName>";
|
||||
}
|
||||
$body = "<?xml version=\"1.0\"?>
|
||||
<d:propertyupdate xmlns:d=\"DAV:\"
|
||||
xmlns:oc=\"http://owncloud.org/ns\">
|
||||
<d:set>
|
||||
<d:prop>$propertyBody</d:prop>
|
||||
</d:set>
|
||||
</d:propertyupdate>";
|
||||
return self::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PROPPATCH",
|
||||
$path,
|
||||
[],
|
||||
$xRequestId,
|
||||
$body,
|
||||
$davPathVersionToUse,
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets namespace-prefix, namespace url and propName from provided namespaceString or property
|
||||
* or otherwise use default
|
||||
*
|
||||
* @param string $namespaceString
|
||||
* @param string $property
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPropertyWithNamespaceInfo(string $namespaceString = "", string $property = ""):array {
|
||||
$namespace = "";
|
||||
$namespacePrefix = "";
|
||||
if (\is_int($namespaceString)) {
|
||||
//default namespace prefix if the property has no array key
|
||||
//also used if no prefix is given in the property value
|
||||
$namespacePrefix = "d";
|
||||
$namespace = "DAV:";
|
||||
} elseif ($namespaceString) {
|
||||
$ns = self::parseNamespace($namespaceString);
|
||||
$namespacePrefix = $ns->prefix;
|
||||
$namespace = $ns->namespace;
|
||||
}
|
||||
//if a namespace prefix is given in the property value use that
|
||||
if ($property && \strpos($property, ":")) {
|
||||
$propertyParts = \explode(":", $property);
|
||||
$namespacePrefix = $propertyParts[0];
|
||||
$property = $propertyParts[1];
|
||||
}
|
||||
return [$namespacePrefix, $namespace, $property];
|
||||
}
|
||||
|
||||
/**
|
||||
* sends HTTP request PROPPATCH method with multiple properties
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string $path
|
||||
* @param array|null $propertiesArray
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $davPathVersion
|
||||
* @param string|null $namespaceString
|
||||
* @param string|null $type
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function proppatchWithMultipleProps(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
string $path,
|
||||
?array $propertiesArray,
|
||||
?string $xRequestId = '',
|
||||
?int $davPathVersion = null,
|
||||
?string $namespaceString = null,
|
||||
?string $type="files"
|
||||
):ResponseInterface {
|
||||
$propertyBody = "";
|
||||
foreach ($propertiesArray as $propertyArray) {
|
||||
$property = $propertyArray["propertyName"];
|
||||
$value = $propertyArray["propertyValue"];
|
||||
|
||||
if ($namespaceString !== null) {
|
||||
$matches = [];
|
||||
[$namespacePrefix, $namespace, $property] = self::getPropertyWithNamespaceInfo(
|
||||
$namespaceString,
|
||||
$property
|
||||
);
|
||||
$propertyBody .= "\n\t<$namespacePrefix:$property>" .
|
||||
"$value" .
|
||||
"</$namespacePrefix:$property>";
|
||||
} else {
|
||||
$propertyBody .= "<$property>$value</$property>";
|
||||
}
|
||||
}
|
||||
$body = "<?xml version=\"1.0\"?>
|
||||
<d:propertyupdate xmlns:d=\"DAV:\"
|
||||
xmlns:oc=\"http://owncloud.org/ns\">
|
||||
<d:set>
|
||||
<d:prop>$propertyBody
|
||||
</d:prop>
|
||||
</d:set>
|
||||
</d:propertyupdate>";
|
||||
return self::makeDavRequest(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
"PROPPATCH",
|
||||
$path,
|
||||
[],
|
||||
$xRequestId,
|
||||
$body,
|
||||
$davPathVersion,
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the response to listing a folder (collection)
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $path
|
||||
* @param string|null $folderDepth
|
||||
* @param string|null $xRequestId
|
||||
* @param string[] $properties
|
||||
* @param string|null $type
|
||||
* @param int|null $davPathVersionToUse
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function listFolder(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $path,
|
||||
?string $folderDepth,
|
||||
?string $xRequestId = '',
|
||||
?array $properties = null,
|
||||
?string $type = "files",
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW
|
||||
):ResponseInterface {
|
||||
if (!$properties) {
|
||||
$properties = [
|
||||
'd:getetag', 'd:resourcetype'
|
||||
];
|
||||
}
|
||||
return self::propfind(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
$path,
|
||||
$properties,
|
||||
$xRequestId,
|
||||
$folderDepth,
|
||||
$type,
|
||||
$davPathVersionToUse
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates UUIDv4
|
||||
* Example: 123e4567-e89b-12d3-a456-426614174000
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function generateUUIDv4():string {
|
||||
// generate 16 bytes (128 bits) of random data or use the data passed into the function.
|
||||
$data = random_bytes(16);
|
||||
\assert(\strlen($data) == 16);
|
||||
|
||||
$data[6] = \chr(\ord($data[6]) & 0x0f | 0x40); // set version to 0100
|
||||
$data[8] = \chr(\ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
|
||||
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
*
|
||||
* @return string
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getSharesSpaceIdForUser(string $baseUrl, string $user, string $password, string $xRequestId): string {
|
||||
if (\array_key_exists($user, self::$spacesIdRef) && \array_key_exists("virtual", self::$spacesIdRef[$user])) {
|
||||
return self::$spacesIdRef[$user]["virtual"];
|
||||
}
|
||||
|
||||
$response = GraphHelper::getMySpaces($baseUrl, $user, $password, '', $xRequestId);
|
||||
$body = HttpRequestHelper::getJsonDecodedResponseBodyContent($response);
|
||||
|
||||
$spaceId = null;
|
||||
foreach ($body->value as $spaces) {
|
||||
if ($spaces->driveType === "virtual") {
|
||||
$spaceId = $spaces->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches personal space id for provided user
|
||||
*
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
*
|
||||
* @return string
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getPersonalSpaceIdForUser(string $baseUrl, string $user, string $password, string $xRequestId):string {
|
||||
if (\array_key_exists($user, self::$spacesIdRef) && \array_key_exists("personal", self::$spacesIdRef[$user])) {
|
||||
return self::$spacesIdRef[$user]["personal"];
|
||||
}
|
||||
$trimmedBaseUrl = \trim($baseUrl, "/");
|
||||
$drivesPath = '/graph/v1.0/me/drives';
|
||||
$fullUrl = $trimmedBaseUrl . $drivesPath;
|
||||
$response = HttpRequestHelper::get(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$user,
|
||||
$password
|
||||
);
|
||||
$bodyContents = $response->getBody()->getContents();
|
||||
$json = \json_decode($bodyContents);
|
||||
$personalSpaceId = '';
|
||||
if ($json === null) {
|
||||
// the graph endpoint did not give a useful answer
|
||||
// try getting the information from the webdav endpoint
|
||||
$fullUrl = $trimmedBaseUrl . '/remote.php/webdav';
|
||||
$response = HttpRequestHelper::sendRequest(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
'PROPFIND',
|
||||
$user,
|
||||
$password
|
||||
);
|
||||
// we expect to get a multipart XML response with status 207
|
||||
$status = $response->getStatusCode();
|
||||
if ($status === 401) {
|
||||
throw new SpaceNotFoundException(__METHOD__ . " Personal space not found for user " . $user);
|
||||
} elseif ($status !== 207) {
|
||||
throw new Exception(
|
||||
__METHOD__ . " webdav propfind for user $user failed with status $status - so the personal space id cannot be discovered"
|
||||
);
|
||||
}
|
||||
$responseXmlObject = HttpRequestHelper::getResponseXml(
|
||||
$response,
|
||||
__METHOD__
|
||||
);
|
||||
$xmlPart = $responseXmlObject->xpath("/d:multistatus/d:response[1]/d:propstat/d:prop/oc:id");
|
||||
if ($xmlPart === false) {
|
||||
throw new Exception(
|
||||
__METHOD__ . " oc:id not found in webdav propfind for user $user - so the personal space id cannot be discovered"
|
||||
);
|
||||
}
|
||||
$ocIdRawString = $xmlPart[0]->__toString();
|
||||
$separator = "!";
|
||||
if (\strpos($ocIdRawString, $separator) !== false) {
|
||||
// The string is not base64-encoded, because the exclamation mark is not in the base64 alphabet.
|
||||
// We expect to have a string with 2 parts separated by the exclamation mark.
|
||||
// This is the format introduced in 2022-02
|
||||
// oc:id should be something like:
|
||||
// "7464caf6-1799-103c-9046-c7b74deb5f63!7464caf6-1799-103c-9046-c7b74deb5f63"
|
||||
// There is no encoding to decode.
|
||||
$decodedId = $ocIdRawString;
|
||||
} else {
|
||||
// fall-back to assuming that the oc:id is base64-encoded
|
||||
// That is the format used before and up to 2022-02
|
||||
// This can be removed after both the edge and master branches of cs3org/reva are using the new format.
|
||||
// oc:id should be some base64 encoded string like:
|
||||
// "NzQ2NGNhZjYtMTc5OS0xMDNjLTkwNDYtYzdiNzRkZWI1ZjYzOjc0NjRjYWY2LTE3OTktMTAzYy05MDQ2LWM3Yjc0ZGViNWY2Mw=="
|
||||
// That should decode to something like:
|
||||
// "7464caf6-1799-103c-9046-c7b74deb5f63:7464caf6-1799-103c-9046-c7b74deb5f63"
|
||||
$decodedId = base64_decode($ocIdRawString);
|
||||
$separator = ":";
|
||||
}
|
||||
$ocIdParts = \explode($separator, $decodedId);
|
||||
if (\count($ocIdParts) !== 2) {
|
||||
throw new Exception(
|
||||
__METHOD__ . " the oc:id $decodedId for user $user does not have 2 parts separated by '$separator', so the personal space id cannot be discovered"
|
||||
);
|
||||
}
|
||||
$personalSpaceId = $ocIdParts[0];
|
||||
} else {
|
||||
foreach ($json->value as $spaces) {
|
||||
if ($spaces->driveType === "personal") {
|
||||
$personalSpaceId = $spaces->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($personalSpaceId) {
|
||||
// If env var LOG_PERSONAL_SPACE_ID is defined, then output the details of the personal space id.
|
||||
// This is a useful debugging tool to have confidence that the personal space id is found correctly.
|
||||
if (\getenv('LOG_PERSONAL_SPACE_ID') !== false) {
|
||||
echo __METHOD__ . " personal space id of user $user is $personalSpaceId\n";
|
||||
}
|
||||
self::$spacesIdRef[$user] = [];
|
||||
self::$spacesIdRef[$user]["personal"] = $personalSpaceId;
|
||||
return $personalSpaceId;
|
||||
} else {
|
||||
throw new SpaceNotFoundException(__METHOD__ . " Personal space not found for user " . $user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* First checks if a user exists to return its space ID
|
||||
* In case of any exception, it returns a fake space ID
|
||||
*
|
||||
* @param string $baseUrl
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $xRequestId
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public static function getPersonalSpaceIdForUserOrFakeIfNotFound(string $baseUrl, string $user, string $password, string $xRequestId):string {
|
||||
try {
|
||||
$spaceId = self::getPersonalSpaceIdForUser(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
$xRequestId,
|
||||
);
|
||||
} catch (SpaceNotFoundException $e) {
|
||||
// if the fetch fails, and the user is not found, then a fake space id is prepared
|
||||
// this is useful for testing when the personal space is of a non-existing user
|
||||
$fakeSpaceId = self::generateUUIDv4();
|
||||
self::$spacesIdRef[$user]["personal"] = $fakeSpaceId;
|
||||
$spaceId = $fakeSpaceId;
|
||||
}
|
||||
return $spaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a DAV request
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* URL of owncloud e.g. http://localhost:8080
|
||||
* should include the subfolder if owncloud runs in a subfolder
|
||||
* e.g. http://localhost:8080/owncloud-core
|
||||
* @param string|null $user
|
||||
* @param string|null $password or token when bearer auth is used
|
||||
* @param string|null $method PUT, GET, DELETE, etc.
|
||||
* @param string|null $path
|
||||
* @param array|null $headers
|
||||
* @param string|null $xRequestId
|
||||
* @param string|null|resource|StreamInterface $body
|
||||
* @param int|null $davPathVersionToUse (1|2|3)
|
||||
* @param string|null $type of request
|
||||
* @param string|null $sourceIpAddress to initiate the request from
|
||||
* @param string|null $authType basic|bearer
|
||||
* @param bool $stream Set to true to stream a response rather
|
||||
* than download it all up-front.
|
||||
* @param int|null $timeout
|
||||
* @param Client|null $client
|
||||
* @param array|null $urlParameter to concatenate with path
|
||||
* @param string|null $doDavRequestAsUser run the DAV as this user, if null it is the same as $user
|
||||
* @param bool $isGivenStep is set to true if makeDavRequest is called from a "given" step
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function makeDavRequest(
|
||||
?string $baseUrl,
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $method,
|
||||
?string $path,
|
||||
?array $headers,
|
||||
?string $xRequestId = '',
|
||||
$body = null,
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_OLD,
|
||||
?string $type = "files",
|
||||
?string $sourceIpAddress = null,
|
||||
?string $authType = "basic",
|
||||
?bool $stream = false,
|
||||
?int $timeout = 0,
|
||||
?Client $client = null,
|
||||
?array $urlParameter = [],
|
||||
?string $doDavRequestAsUser = null,
|
||||
?bool $isGivenStep = false
|
||||
):ResponseInterface {
|
||||
$baseUrl = self::sanitizeUrl($baseUrl, true);
|
||||
|
||||
// We need to manipulate and use path as a string.
|
||||
// So ensure that it is a string to avoid any type-conversion errors.
|
||||
if ($path === null) {
|
||||
$path = "";
|
||||
}
|
||||
|
||||
// get space id if testing with spaces dav
|
||||
if (self::$SPACE_ID_FROM_OCIS === '' && $davPathVersionToUse === self::DAV_VERSION_SPACES) {
|
||||
$path = \ltrim($path, "/");
|
||||
if (\str_starts_with($path, "Shares/")) {
|
||||
$spaceId = self::getSharesSpaceIdForUser(
|
||||
$baseUrl,
|
||||
$doDavRequestAsUser ?? $user,
|
||||
$password,
|
||||
$xRequestId
|
||||
);
|
||||
$path = "/" . preg_replace("/^Shares\//", "", $path);
|
||||
} else {
|
||||
$spaceId = self::getPersonalSpaceIdForUserOrFakeIfNotFound(
|
||||
$baseUrl,
|
||||
$doDavRequestAsUser ?? $user,
|
||||
$password,
|
||||
$xRequestId
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$spaceId = self::$SPACE_ID_FROM_OCIS;
|
||||
}
|
||||
|
||||
$davPath = self::getDavPath($doDavRequestAsUser ?? $user, $davPathVersionToUse, $type, $spaceId);
|
||||
|
||||
//replace %, # and ? and in the path, Guzzle will not encode them
|
||||
$urlSpecialChar = [['%', '#', '?'], ['%25', '%23', '%3F']];
|
||||
$path = \str_replace($urlSpecialChar[0], $urlSpecialChar[1], $path);
|
||||
|
||||
if (!empty($urlParameter)) {
|
||||
$urlParameter = \http_build_query($urlParameter, '', '&');
|
||||
$path .= '?' . $urlParameter;
|
||||
}
|
||||
$fullUrl = self::sanitizeUrl($baseUrl . "/$davPath" . $path);
|
||||
|
||||
if ($authType === 'bearer') {
|
||||
$headers['Authorization'] = 'Bearer ' . $password;
|
||||
$user = null;
|
||||
$password = null;
|
||||
}
|
||||
if ($type === "public-files-new") {
|
||||
if ($password === null || $password === "") {
|
||||
$user = null;
|
||||
} else {
|
||||
$user = "public";
|
||||
}
|
||||
}
|
||||
$config = null;
|
||||
if ($sourceIpAddress !== null) {
|
||||
$config = [ 'curl' => [ CURLOPT_INTERFACE => $sourceIpAddress ]];
|
||||
}
|
||||
|
||||
if ($headers !== null) {
|
||||
foreach ($headers as $key => $value) {
|
||||
//? and # need to be encoded in the Destination URL
|
||||
if ($key === "Destination") {
|
||||
$headers[$key] = \str_replace(
|
||||
$urlSpecialChar[0],
|
||||
$urlSpecialChar[1],
|
||||
$value
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Clear the space ID from ocis after each request
|
||||
self::$SPACE_ID_FROM_OCIS = '';
|
||||
return HttpRequestHelper::sendRequest(
|
||||
$fullUrl,
|
||||
$xRequestId,
|
||||
$method,
|
||||
$user,
|
||||
$password,
|
||||
$headers,
|
||||
$body,
|
||||
$config,
|
||||
null,
|
||||
$stream,
|
||||
$timeout,
|
||||
$client,
|
||||
$isGivenStep
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the dav path
|
||||
*
|
||||
* @param string|null $user
|
||||
* @param int|null $davPathVersionToUse (1|2)
|
||||
* @param string|null $type
|
||||
* @param string|null $spaceId
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getDavPath(
|
||||
?string $user,
|
||||
?int $davPathVersionToUse = null,
|
||||
?string $type = "files",
|
||||
?string $spaceId = null
|
||||
):string {
|
||||
$newTrashbinDavPath = "remote.php/dav/trash-bin/$user/";
|
||||
|
||||
switch ($type) {
|
||||
case 'public-files':
|
||||
case 'public-files-old':
|
||||
return "public.php/webdav/";
|
||||
case 'public-files-new':
|
||||
return "remote.php/dav/public-files/$user/";
|
||||
case 'archive':
|
||||
return "remote.php/dav/archive/$user/files";
|
||||
case 'versions':
|
||||
case 'customgroups':
|
||||
return "remote.php/dav/";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ($davPathVersionToUse === self::DAV_VERSION_SPACES) {
|
||||
// return spaces root path if spaceid is null
|
||||
// REPORT request uses spaces root path
|
||||
if ($spaceId === null) {
|
||||
return "remote.php/dav/spaces/";
|
||||
}
|
||||
if ($type === "trash-bin") {
|
||||
return "remote.php/dav/spaces/trash-bin/" . $spaceId . '/';
|
||||
}
|
||||
return "remote.php/dav/spaces/" . $spaceId . '/';
|
||||
} else {
|
||||
if ($davPathVersionToUse === self::DAV_VERSION_OLD) {
|
||||
if ($type === "trash-bin") {
|
||||
// Since there is no trash bin endpoint for old dav version, new dav version's endpoint is used here.
|
||||
return $newTrashbinDavPath;
|
||||
}
|
||||
return "remote.php/webdav/";
|
||||
} elseif ($davPathVersionToUse === self::DAV_VERSION_NEW) {
|
||||
if ($type === "files") {
|
||||
$path = 'remote.php/dav/files/';
|
||||
return $path . $user . '/';
|
||||
} elseif ($type === "trash-bin") {
|
||||
return $newTrashbinDavPath;
|
||||
} else {
|
||||
return "remote.php/dav";
|
||||
}
|
||||
} else {
|
||||
throw new InvalidArgumentException(
|
||||
"DAV path version $davPathVersionToUse is unknown"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure there are no double-slashes in the URL
|
||||
*
|
||||
* @param string|null $url
|
||||
* @param bool|null $trailingSlash forces a trailing slash
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitizeUrl(?string $url, ?bool $trailingSlash = false):string {
|
||||
if ($trailingSlash === true) {
|
||||
$url = $url . "/";
|
||||
} else {
|
||||
$url = \rtrim($url, "/");
|
||||
}
|
||||
return \preg_replace("/([^:]\/)\/+/", '$1', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides if the proposed dav version and chunking version are
|
||||
* a valid combination.
|
||||
* If no chunkingVersion is specified, then any dav version is valid.
|
||||
* If a chunkingVersion is specified, then it has to match the dav version.
|
||||
* Note: in future, the dav and chunking versions might or might not
|
||||
* move together and/or be supported together. So a more complex
|
||||
* matrix could be needed here.
|
||||
*
|
||||
* @param string|int $davPathVersion
|
||||
* @param string|int|null $chunkingVersion
|
||||
*
|
||||
* @return boolean is this a valid combination
|
||||
*/
|
||||
public static function isValidDavChunkingCombination(
|
||||
$davPathVersion,
|
||||
$chunkingVersion
|
||||
): bool {
|
||||
if ($davPathVersion === self::DAV_VERSION_SPACES) {
|
||||
// allow only old chunking version when using the spaces dav
|
||||
return $chunkingVersion === 1;
|
||||
}
|
||||
return (
|
||||
($chunkingVersion === 'no' || $chunkingVersion === null) ||
|
||||
($davPathVersion === $chunkingVersion)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get Mtime of File in a public link share
|
||||
*
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $fileName
|
||||
* @param string|null $token
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $davVersionToUse
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception|GuzzleException
|
||||
*/
|
||||
public static function getMtimeOfFileInPublicLinkShare(
|
||||
?string $baseUrl,
|
||||
?string $fileName,
|
||||
?string $token,
|
||||
?string $xRequestId = '',
|
||||
?int $davVersionToUse = self::DAV_VERSION_NEW
|
||||
):string {
|
||||
$response = self::propfind(
|
||||
$baseUrl,
|
||||
null,
|
||||
null,
|
||||
"/public-files/$token/$fileName",
|
||||
['d:getlastmodified'],
|
||||
$xRequestId,
|
||||
'1',
|
||||
null,
|
||||
$davVersionToUse
|
||||
);
|
||||
$responseXmlObject = HttpRequestHelper::getResponseXml(
|
||||
$response,
|
||||
__METHOD__
|
||||
);
|
||||
$xmlPart = $responseXmlObject->xpath("//d:getlastmodified");
|
||||
|
||||
return $xmlPart[0]->__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* get Mtime of a resource
|
||||
*
|
||||
* @param string|null $user
|
||||
* @param string|null $password
|
||||
* @param string|null $baseUrl
|
||||
* @param string|null $resource
|
||||
* @param string|null $xRequestId
|
||||
* @param int|null $davPathVersionToUse
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getMtimeOfResource(
|
||||
?string $user,
|
||||
?string $password,
|
||||
?string $baseUrl,
|
||||
?string $resource,
|
||||
?string $xRequestId = '',
|
||||
?int $davPathVersionToUse = self::DAV_VERSION_NEW
|
||||
):string {
|
||||
$response = self::propfind(
|
||||
$baseUrl,
|
||||
$user,
|
||||
$password,
|
||||
$resource,
|
||||
["d:getlastmodified"],
|
||||
$xRequestId,
|
||||
"0",
|
||||
"files",
|
||||
$davPathVersionToUse
|
||||
);
|
||||
$responseXmlObject = HttpRequestHelper::getResponseXml(
|
||||
$response,
|
||||
__METHOD__
|
||||
);
|
||||
$xmlPart = $responseXmlObject->xpath("//d:getlastmodified");
|
||||
Assert::assertArrayHasKey(
|
||||
0,
|
||||
$xmlPart,
|
||||
__METHOD__ . " XML part does not have key 0. Expected a value at index 0 of 'xmlPart' but, found: " . json_encode($xmlPart)
|
||||
);
|
||||
$mtime = new DateTime($xmlPart[0]->__toString());
|
||||
return $mtime->format('U');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user