Merge pull request #20636 from owncloud/savre-3.0

sabre/dav 3.0 and related
This commit is contained in:
Thomas Müller 2015-11-24 16:20:48 +01:00
commit 89b9f5518b
14 changed files with 220 additions and 160 deletions

@ -1 +1 @@
Subproject commit d24fd874b6e23c64ad67a3e05f51e1ee5745cf86 Subproject commit 77d15ce0711c74b41356cfd9a8402140915d7d8a

View File

@ -26,8 +26,8 @@ use Sabre\CalDAV\Backend\SchedulingSupport;
use Sabre\CalDAV\Backend\SubscriptionSupport; use Sabre\CalDAV\Backend\SubscriptionSupport;
use Sabre\CalDAV\Backend\SyncSupport; use Sabre\CalDAV\Backend\SyncSupport;
use Sabre\CalDAV\Plugin; use Sabre\CalDAV\Plugin;
use Sabre\CalDAV\Property\ScheduleCalendarTransp; use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
use Sabre\CalDAV\Property\SupportedCalendarComponentSet; use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
use Sabre\DAV; use Sabre\DAV;
use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\Forbidden;
use Sabre\VObject\DateTimeParser; use Sabre\VObject\DateTimeParser;

View File

@ -3,13 +3,9 @@
namespace OCA\DAV\CardDAV; namespace OCA\DAV\CardDAV;
use OCA\DAV\CardDAV\Sharing\IShareableAddressBook; use OCA\DAV\CardDAV\Sharing\IShareableAddressBook;
use OCP\IUserManager;
class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddressBook { class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddressBook {
/** @var IUserManager */
private $userManager;
public function __construct(CardDavBackend $carddavBackend, array $addressBookInfo) { public function __construct(CardDavBackend $carddavBackend, array $addressBookInfo) {
parent::__construct($carddavBackend, $addressBookInfo); parent::__construct($carddavBackend, $addressBookInfo);
} }

View File

@ -2,7 +2,7 @@
namespace OCA\DAV\CardDAV; namespace OCA\DAV\CardDAV;
class UserAddressBooks extends \Sabre\CardDAV\UserAddressBooks { class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
/** /**
* Returns a list of addressbooks * Returns a list of addressbooks

View File

@ -35,6 +35,8 @@ use OCP\IUserSession;
use Sabre\DAV\Auth\Backend\AbstractBasic; use Sabre\DAV\Auth\Backend\AbstractBasic;
use Sabre\DAV\Exception\NotAuthenticated; use Sabre\DAV\Exception\NotAuthenticated;
use Sabre\DAV\Exception\ServiceUnavailable; use Sabre\DAV\Exception\ServiceUnavailable;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
class Auth extends AbstractBasic { class Auth extends AbstractBasic {
const DAV_AUTHENTICATED = 'AUTHENTICATED_TO_DAV_BACKEND'; const DAV_AUTHENTICATED = 'AUTHENTICATED_TO_DAV_BACKEND';
@ -122,22 +124,15 @@ class Auth extends AbstractBasic {
} }
/** /**
* Override function here. We want to cache authentication cookies * @param RequestInterface $request
* in the syncing client to avoid HTTP-401 roundtrips. * @param ResponseInterface $response
* If the sync client supplies the cookies, then OC_User::isLoggedIn() * @return array
* will return true and we can see this WebDAV request as already authenticated,
* even if there are no HTTP Basic Auth headers.
* In other case, just fallback to the parent implementation.
*
* @param \Sabre\DAV\Server $server
* @param string $realm
* @return bool
* @throws ServiceUnavailable
* @throws NotAuthenticated * @throws NotAuthenticated
* @throws ServiceUnavailable
*/ */
public function authenticate(\Sabre\DAV\Server $server, $realm) { function check(RequestInterface $request, ResponseInterface $response) {
try { try {
$result = $this->auth($server, $realm); $result = $this->auth($request, $response);
return $result; return $result;
} catch (NotAuthenticated $e) { } catch (NotAuthenticated $e) {
throw $e; throw $e;
@ -149,11 +144,11 @@ class Auth extends AbstractBasic {
} }
/** /**
* @param \Sabre\DAV\Server $server * @param RequestInterface $request
* @param string $realm * @param ResponseInterface $response
* @return bool * @return array
*/ */
private function auth(\Sabre\DAV\Server $server, $realm) { private function auth(RequestInterface $request, ResponseInterface $response) {
if (\OC_User::handleApacheAuth() || if (\OC_User::handleApacheAuth() ||
($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED))) ($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED)))
) { ) {
@ -161,16 +156,16 @@ class Auth extends AbstractBasic {
\OC_Util::setupFS($user); \OC_Util::setupFS($user);
$this->currentUser = $user; $this->currentUser = $user;
$this->session->close(); $this->session->close();
return true; return [true, $this->principalPrefix . $user];
} }
if ($server->httpRequest->getHeader('X-Requested-With') === 'XMLHttpRequest') { if ($request->getHeader('X-Requested-With') === 'XMLHttpRequest') {
// do not re-authenticate over ajax, use dummy auth name to prevent browser popup // do not re-authenticate over ajax, use dummy auth name to prevent browser popup
$server->httpResponse->addHeader('WWW-Authenticate','DummyBasic realm="' . $realm . '"'); $response->addHeader('WWW-Authenticate','DummyBasic realm="' . $this->realm . '"');
$server->httpResponse->setStatus(401); $response->setStatus(401);
throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls'); throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
} }
return parent::authenticate($server, $realm); return parent::check($request, $response);
} }
} }

View File

@ -22,9 +22,9 @@
namespace OCA\DAV\Connector\Sabre; namespace OCA\DAV\Connector\Sabre;
use Sabre\DAV\Locks\LockInfo; use Sabre\DAV\Locks\LockInfo;
use Sabre\DAV\Property\LockDiscovery;
use Sabre\DAV\Property\SupportedLock;
use Sabre\DAV\ServerPlugin; use Sabre\DAV\ServerPlugin;
use Sabre\DAV\Xml\Property\LockDiscovery;
use Sabre\DAV\Xml\Property\SupportedLock;
use Sabre\HTTP\RequestInterface; use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface; use Sabre\HTTP\ResponseInterface;
use Sabre\DAV\PropFind; use Sabre\DAV\PropFind;
@ -122,12 +122,6 @@ class FakeLockerPlugin extends ServerPlugin {
*/ */
public function fakeLockProvider(RequestInterface $request, public function fakeLockProvider(RequestInterface $request,
ResponseInterface $response) { ResponseInterface $response) {
$dom = new \DOMDocument('1.0', 'utf-8');
$prop = $dom->createElementNS('DAV:', 'd:prop');
$dom->appendChild($prop);
$lockDiscovery = $dom->createElementNS('DAV:', 'd:lockdiscovery');
$prop->appendChild($lockDiscovery);
$lockInfo = new LockInfo(); $lockInfo = new LockInfo();
$lockInfo->token = md5($request->getPath()); $lockInfo->token = md5($request->getPath());
@ -135,10 +129,12 @@ class FakeLockerPlugin extends ServerPlugin {
$lockInfo->depth = \Sabre\DAV\Server::DEPTH_INFINITY; $lockInfo->depth = \Sabre\DAV\Server::DEPTH_INFINITY;
$lockInfo->timeout = 1800; $lockInfo->timeout = 1800;
$lockObj = new LockDiscovery([$lockInfo]); $body = $this->server->xml->write('{DAV:}prop', [
$lockObj->serialize($this->server, $lockDiscovery); '{DAV:}lockdiscovery' =>
new LockDiscovery([$lockInfo])
]);
$response->setBody($dom->saveXML()); $response->setBody($body);
return false; return false;
} }

View File

@ -22,82 +22,100 @@
namespace OCA\DAV\Connector\Sabre; namespace OCA\DAV\Connector\Sabre;
use Sabre\DAV; use Sabre\Xml\Element;
use Sabre\Xml\Reader;
use Sabre\Xml\Writer;
/** /**
* TagList property * TagList property
* *
* This property contains multiple "tag" elements, each containing a tag name. * This property contains multiple "tag" elements, each containing a tag name.
*/ */
class TagList extends DAV\Property { class TagList implements Element {
const NS_OWNCLOUD = 'http://owncloud.org/ns'; const NS_OWNCLOUD = 'http://owncloud.org/ns';
/** /**
* tags * tags
* *
* @var array * @var array
*/ */
private $tags; private $tags;
/** /**
* @param array $tags * @param array $tags
*/ */
public function __construct(array $tags) { public function __construct(array $tags) {
$this->tags = $tags; $this->tags = $tags;
} }
/** /**
* Returns the tags * Returns the tags
* *
* @return array * @return array
*/ */
public function getTags() { public function getTags() {
return $this->tags; return $this->tags;
} }
/** /**
* Serializes this property. * The deserialize method is called during xml parsing.
* *
* @param DAV\Server $server * This method is called statictly, this is because in theory this method
* @param \DOMElement $dom * may be used as a type of constructor, or factory method.
* @return void *
*/ * Often you want to return an instance of the current class, but you are
public function serialize(DAV\Server $server,\DOMElement $dom) { * free to return other data as well.
*
* You are responsible for advancing the reader to the next element. Not
* doing anything will result in a never-ending loop.
*
* If you just want to skip parsing for this element altogether, you can
* just call $reader->next();
*
* $reader->parseInnerTree() will parse the entire sub-tree, and advance to
* the next element.
*
* @param Reader $reader
* @return mixed
*/
static function xmlDeserialize(Reader $reader) {
$tags = [];
$prefix = $server->xmlNamespaces[self::NS_OWNCLOUD]; foreach ($reader->parseInnerTree() as $elem) {
if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}tag') {
$tags[] = $elem['value'];
}
}
return new self($tags);
}
foreach($this->tags as $tag) { /**
* The xmlSerialize metod is called during xml writing.
$elem = $dom->ownerDocument->createElement($prefix . ':tag'); *
$elem->appendChild($dom->ownerDocument->createTextNode($tag)); * Use the $writer argument to write its own xml serialization.
*
$dom->appendChild($elem); * An important note: do _not_ create a parent element. Any element
} * implementing XmlSerializble should only ever write what's considered
* its 'inner xml'.
} *
* The parent of the current element is responsible for writing a
/** * containing element.
* Unserializes this property from a DOM Element *
* * This allows serializers to be re-used for different element names.
* This method returns an instance of this class. *
* It will only decode tag values. * If you are opening new elements, you must also close them again.
* *
* @param \DOMElement $dom * @param Writer $writer
* @param array $propertyMap * @return void
* @return \OCA\DAV\Connector\Sabre\TagList */
*/ function xmlSerialize(Writer $writer) {
static function unserialize(\DOMElement $dom, array $propertyMap) {
$tags = array();
foreach($dom->childNodes as $child) {
if (DAV\XMLUtil::toClarkNotation($child)==='{' . self::NS_OWNCLOUD . '}tag') {
$tags[] = $child->textContent;
}
}
return new self($tags);
}
foreach ($this->tags as $tag) {
$writer->startElement(self::NS_OWNCLOUD . ':tag');
$writer->writeElement($tag);
$writer->endElement();
}
}
} }

View File

@ -23,9 +23,9 @@ namespace Tests\Connector\Sabre;
use DateTime; use DateTime;
use DateTimeZone; use DateTimeZone;
use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalDavBackend;
use Sabre\CalDAV\Property\SupportedCalendarComponentSet; use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
use Sabre\DAV\Property\Href;
use Sabre\DAV\PropPatch; use Sabre\DAV\PropPatch;
use Sabre\DAV\Xml\Property\Href;
use Test\TestCase; use Test\TestCase;
/** /**

View File

@ -21,6 +21,7 @@
namespace OCA\DAV\Tests\Unit\Connector\Sabre; namespace OCA\DAV\Tests\Unit\Connector\Sabre;
use OCA\DAV\Connector\Sabre\FakeLockerPlugin; use OCA\DAV\Connector\Sabre\FakeLockerPlugin;
use Sabre\HTTP\Response;
use Test\TestCase; use Test\TestCase;
/** /**
@ -141,20 +142,19 @@ class FakeLockerPluginTest extends TestCase {
public function testFakeLockProvider() { public function testFakeLockProvider() {
$request = $this->getMock('\Sabre\HTTP\RequestInterface'); $request = $this->getMock('\Sabre\HTTP\RequestInterface');
$response = $this->getMock('\Sabre\HTTP\ResponseInterface'); $response = new Response();
$server = $this->getMock('\Sabre\DAV\Server'); $server = $this->getMock('\Sabre\DAV\Server');
$this->fakeLockerPlugin->initialize($server); $this->fakeLockerPlugin->initialize($server);
$request->expects($this->exactly(2)) $request->expects($this->exactly(2))
->method('getPath') ->method('getPath')
->will($this->returnValue('MyPath')); ->will($this->returnValue('MyPath'));
$response->expects($this->once())
->method('setBody')
->with('<?xml version="1.0" encoding="utf-8"?>
<d:prop xmlns:d="DAV:"><d:lockdiscovery><d:activelock><d:lockscope><d:exclusive/></d:lockscope><d:locktype><d:write/></d:locktype><d:lockroot><d:href>MyPath</d:href></d:lockroot><d:depth>infinity</d:depth><d:timeout>Second-1800</d:timeout><d:locktoken><d:href>opaquelocktoken:fe4f7f2437b151fbcb4e9f5c8118c6b1</d:href></d:locktoken><d:owner/></d:activelock></d:lockdiscovery></d:prop>
');
$this->assertSame(false, $this->fakeLockerPlugin->fakeLockProvider($request, $response)); $this->assertSame(false, $this->fakeLockerPlugin->fakeLockProvider($request, $response));
$expectedXml = '<?xml version="1.0" encoding="utf-8"?><d:prop xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"><d:lockdiscovery><d:activelock><d:lockscope><d:exclusive/></d:lockscope><d:locktype><d:write/></d:locktype><d:lockroot><d:href>MyPath</d:href></d:lockroot><d:depth>infinity</d:depth><d:timeout>Second-1800</d:timeout><d:locktoken><d:href>opaquelocktoken:fe4f7f2437b151fbcb4e9f5c8118c6b1</d:href></d:locktoken><d:owner/></d:activelock></d:lockdiscovery></d:prop>';
$this->assertXmlStringEqualsXmlString($expectedXml, $response->getBody());
} }
public function testFakeUnlockProvider() { public function testFakeUnlockProvider() {

View File

@ -249,9 +249,12 @@ class Auth extends TestCase {
} }
public function testAuthenticateAlreadyLoggedIn() { public function testAuthenticateAlreadyLoggedIn() {
$server = $this->getMockBuilder('\Sabre\DAV\Server') $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
->disableOriginalConstructor()
->getMock();
$this->userSession $this->userSession
->expects($this->once()) ->expects($this->once())
->method('isLoggedIn') ->method('isLoggedIn')
@ -275,13 +278,10 @@ class Auth extends TestCase {
->expects($this->once()) ->expects($this->once())
->method('close'); ->method('close');
$this->assertTrue($this->auth->authenticate($server, 'TestRealm')); $response = $this->auth->check($request, $response);
$this->assertEquals([true, 'principals/MyWrongDavUser'], $response);
} }
/**
* @expectedException \Sabre\DAV\Exception\NotAuthenticated
* @expectedExceptionMessage No basic authentication headers were found
*/
public function testAuthenticateNoBasicAuthenticateHeadersProvided() { public function testAuthenticateNoBasicAuthenticateHeadersProvided() {
$server = $this->getMockBuilder('\Sabre\DAV\Server') $server = $this->getMockBuilder('\Sabre\DAV\Server')
->disableOriginalConstructor() ->disableOriginalConstructor()
@ -292,7 +292,8 @@ class Auth extends TestCase {
$server->httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface') $server->httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface')
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$this->auth->authenticate($server, 'TestRealm'); $response = $this->auth->check($server->httpRequest, $server->httpResponse);
$this->assertEquals([false, 'No \'Authorization: Basic\' header found. Either the client didn\'t send one, or the server is mis-configured'], $response);
} }
/** /**
@ -300,21 +301,20 @@ class Auth extends TestCase {
* @expectedExceptionMessage Cannot authenticate over ajax calls * @expectedExceptionMessage Cannot authenticate over ajax calls
*/ */
public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjax() { public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjax() {
$server = $this->getMockBuilder('\Sabre\DAV\Server') /** @var \Sabre\HTTP\RequestInterface $httpRequest */
$httpRequest = $this->getMockBuilder('\Sabre\HTTP\RequestInterface')
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$server->httpRequest = $this->getMockBuilder('\Sabre\HTTP\RequestInterface') /** @var \Sabre\HTTP\ResponseInterface $httpResponse */
$httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface')
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$server->httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface') $httpRequest
->disableOriginalConstructor()
->getMock();
$server->httpRequest
->expects($this->once()) ->expects($this->once())
->method('getHeader') ->method('getHeader')
->with('X-Requested-With') ->with('X-Requested-With')
->will($this->returnValue('XMLHttpRequest')); ->will($this->returnValue('XMLHttpRequest'));
$this->auth->authenticate($server, 'TestRealm'); $this->auth->check($httpRequest, $httpResponse);
} }
public function testAuthenticateValidCredentials() { public function testAuthenticateValidCredentials() {
@ -352,13 +352,10 @@ class Auth extends TestCase {
->expects($this->exactly(2)) ->expects($this->exactly(2))
->method('getUser') ->method('getUser')
->will($this->returnValue($user)); ->will($this->returnValue($user));
$this->assertTrue($this->auth->authenticate($server, 'TestRealm')); $response = $this->auth->check($server->httpRequest, $server->httpResponse);
$this->assertEquals([true, 'principals/username'], $response);
} }
/**
* @expectedException \Sabre\DAV\Exception\NotAuthenticated
* @expectedExceptionMessage Username or password does not match
*/
public function testAuthenticateInvalidCredentials() { public function testAuthenticateInvalidCredentials() {
$server = $this->getMockBuilder('\Sabre\DAV\Server') $server = $this->getMockBuilder('\Sabre\DAV\Server')
->disableOriginalConstructor() ->disableOriginalConstructor()
@ -384,6 +381,7 @@ class Auth extends TestCase {
->method('login') ->method('login')
->with('username', 'password') ->with('username', 'password')
->will($this->returnValue(false)); ->will($this->returnValue(false));
$this->auth->authenticate($server, 'TestRealm'); $response = $this->auth->check($server->httpRequest, $server->httpResponse);
$this->assertEquals([false, 'Username or password was incorrect'], $response);
} }
} }

View File

@ -9,6 +9,8 @@
namespace OCA\DAV\Tests\Unit\Connector\Sabre\RequestTest; namespace OCA\DAV\Tests\Unit\Connector\Sabre\RequestTest;
use Sabre\DAV\Auth\Backend\BackendInterface; use Sabre\DAV\Auth\Backend\BackendInterface;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
class Auth implements BackendInterface { class Auth implements BackendInterface {
/** /**
@ -32,18 +34,35 @@ class Auth implements BackendInterface {
$this->password = $password; $this->password = $password;
} }
/** /**
* Authenticates the user based on the current request. * When this method is called, the backend must check if authentication was
* successful.
* *
* If authentication is successful, true must be returned. * The returned value must be one of the following
* If authentication fails, an exception must be thrown.
* *
* @param \Sabre\DAV\Server $server * [true, "principals/username"]
* @param string $realm * [false, "reason for failure"]
* @return boolean|null *
* If authentication was successful, it's expected that the authentication
* backend returns a so-called principal url.
*
* Examples of a principal url:
*
* principals/admin
* principals/user1
* principals/users/joe
* principals/uid/123457
*
* If you don't use WebDAV ACL (RFC3744) we recommend that you simply
* return a string such as:
*
* principals/users/[username]
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @return array
*/ */
function authenticate(\Sabre\DAV\Server $server, $realm) { function check(RequestInterface $request, ResponseInterface $response) {
$userSession = \OC::$server->getUserSession(); $userSession = \OC::$server->getUserSession();
$result = $userSession->login($this->user, $this->password); $result = $userSession->login($this->user, $this->password);
if ($result) { if ($result) {
@ -52,18 +71,33 @@ class Auth implements BackendInterface {
\OC_Util::setupFS($user); \OC_Util::setupFS($user);
//trigger creation of user home and /files folder //trigger creation of user home and /files folder
\OC::$server->getUserFolder($user); \OC::$server->getUserFolder($user);
return [true, "principals/$user"];
} }
return $result; return [false, "login failed"];
} }
/** /**
* Returns information about the currently logged in username. * This method is called when a user could not be authenticated, and
* authentication was required for the current request.
* *
* If nobody is currently logged in, this method should return null. * This gives you the opportunity to set authentication headers. The 401
* status code will already be set.
* *
* @return string * In this case of Basic Auth, this would for example mean that the
* following header needs to be set:
*
* $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV');
*
* Keep in mind that in the case of multiple authentication backends, other
* WWW-Authenticate headers may already have been set, and you'll want to
* append your own WWW-Authenticate header instead of overwriting the
* existing one.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @return void
*/ */
function getCurrentUser() { function challenge(RequestInterface $request, ResponseInterface $response) {
return $this->user; // TODO: Implement challenge() method.
} }
} }

View File

@ -145,10 +145,7 @@
var k = 0; var k = 0;
for (k = 0; k < propNode.childNodes.length; k++) { for (k = 0; k < propNode.childNodes.length; k++) {
var prop = propNode.childNodes[k]; var prop = propNode.childNodes[k];
var value = prop.textContent || prop.text; var value = this._parsePropNode(prop);
if (prop.childNodes && prop.childNodes.length > 0 && prop.childNodes[0].nodeType === 1) {
value = prop.childNodes;
}
propStat.properties['{' + prop.namespaceURI + '}' + (prop.localName || prop.baseName)] = value; propStat.properties['{' + prop.namespaceURI + '}' + (prop.localName || prop.baseName)] = value;
} }

View File

@ -148,6 +148,33 @@ dav.Client.prototype = {
}, },
/**
* Parses a property node.
*
* Either returns a string if the node only contains text, or returns an
* array of non-text subnodes.
*
* @param {Object} propNode node to parse
* @return {string|Array} text content as string or array of subnodes, excluding text nodes
*/
_parsePropNode: function(propNode) {
var content = null;
if (propNode.childNodes && propNode.childNodes.length > 0) {
var subNodes = [];
// filter out text nodes
for (var j = 0; j < propNode.childNodes.length; j++) {
var node = propNode.childNodes[j];
if (node.nodeType === 1) {
subNodes.push(node);
}
}
if (subNodes.length) {
content = subNodes;
}
}
return content || propNode.textContent || propNode.text;
},
/** /**
* Parses a multi-status response body. * Parses a multi-status response body.
@ -197,11 +224,7 @@ dav.Client.prototype = {
var propNode = propIterator.iterateNext(); var propNode = propIterator.iterateNext();
while(propNode) { while(propNode) {
var content = propNode.textContent; var content = this._parsePropNode(propNode);
if (propNode.childNodes && propNode.childNodes.length > 0 && propNode.childNodes[0].nodeType === 1) {
content = propNode.childNodes;
}
propStat.properties['{' + propNode.namespaceURI + '}' + propNode.localName] = content; propStat.properties['{' + propNode.namespaceURI + '}' + propNode.localName] = content;
propNode = propIterator.iterateNext(); propNode = propIterator.iterateNext();

View File

@ -47,6 +47,7 @@ use OCP\Files\StorageNotAvailableException;
use OCP\Util; use OCP\Util;
use Sabre\DAV\Client; use Sabre\DAV\Client;
use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\Xml\Property\ResourceType;
use Sabre\HTTP\ClientException; use Sabre\HTTP\ClientException;
use Sabre\HTTP\ClientHttpException; use Sabre\HTTP\ClientHttpException;
@ -137,7 +138,7 @@ class DAV extends Common {
$this->client->setThrowExceptions(true); $this->client->setThrowExceptions(true);
if ($this->secure === true && $this->certPath) { if ($this->secure === true && $this->certPath) {
$this->client->addTrustedCertificates($this->certPath); $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
} }
} }
@ -280,7 +281,8 @@ class DAV extends Common {
$response = $this->propfind($path); $response = $this->propfind($path);
$responseType = array(); $responseType = array();
if (isset($response["{DAV:}resourcetype"])) { if (isset($response["{DAV:}resourcetype"])) {
$responseType = $response["{DAV:}resourcetype"]->resourceType; /** @var ResourceType[] $response */
$responseType = $response["{DAV:}resourcetype"]->getValue();
} }
return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
} catch (ClientHttpException $e) { } catch (ClientHttpException $e) {
@ -554,7 +556,8 @@ class DAV extends Common {
$response = $this->propfind($path); $response = $this->propfind($path);
$responseType = array(); $responseType = array();
if (isset($response["{DAV:}resourcetype"])) { if (isset($response["{DAV:}resourcetype"])) {
$responseType = $response["{DAV:}resourcetype"]->resourceType; /** @var ResourceType[] $response */
$responseType = $response["{DAV:}resourcetype"]->getValue();
} }
$type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
if ($type == 'dir') { if ($type == 'dir') {