From 5c51d84a674f7f91b91666596f4619892003bf63 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Wed, 24 Oct 2018 18:39:30 +0200 Subject: Calendar invitation: use system default instead of sender's language as fallback Signed-off-by: Georg Ehrke --- apps/dav/lib/CalDAV/Schedule/IMipPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'apps/dav') diff --git a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php index 6f303acba33..3ff3ed0c569 100644 --- a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php +++ b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php @@ -170,7 +170,7 @@ class IMipPlugin extends SabreIMipPlugin { $vevent = $iTipMessage->message->VEVENT; $attendee = $this->getCurrentAttendee($iTipMessage); - $defaultLang = $this->config->getUserValue($this->userId, 'core', 'lang', $this->l10nFactory->findLanguage()); + $defaultLang = $this->l10nFactory->findLanguage(); $lang = $this->getAttendeeLangOrDefault($defaultLang, $attendee); $l10n = $this->l10nFactory->get('dav', $lang); -- cgit v1.2.3 From 45d8aeb9f2708cc53473d8a344a1a8fe6a0331bc Mon Sep 17 00:00:00 2001 From: Julius Härtl Date: Wed, 17 Oct 2018 21:16:13 +0200 Subject: Remove federated sharing address books which are the same as local ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- apps/dav/lib/CardDAV/AddressBookImpl.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'apps/dav') diff --git a/apps/dav/lib/CardDAV/AddressBookImpl.php b/apps/dav/lib/CardDAV/AddressBookImpl.php index 5034b16ed2f..a592b9f0888 100644 --- a/apps/dav/lib/CardDAV/AddressBookImpl.php +++ b/apps/dav/lib/CardDAV/AddressBookImpl.php @@ -262,8 +262,12 @@ class AddressBookImpl implements IAddressBook { } } - if ($this->addressBookInfo['principaluri'] === 'principals/system/system' && - $this->addressBookInfo['uri'] === 'system') { + if ( + $this->addressBookInfo['principaluri'] === 'principals/system/system' && ( + $this->addressBookInfo['uri'] === 'system' || + $this->addressBookInfo['{DAV:}displayname'] === $this->urlGenerator->getBaseUrl() + ) + ) { $result['isLocalSystemBook'] = true; } return $result; -- cgit v1.2.3 From 8b92a6c9cb0e4dea84aa0bfac75ad05c358d0b23 Mon Sep 17 00:00:00 2001 From: Julius Härtl Date: Thu, 18 Oct 2018 12:31:57 +0200 Subject: Add type of properties to address book results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- apps/dav/lib/CardDAV/AddressBookImpl.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'apps/dav') diff --git a/apps/dav/lib/CardDAV/AddressBookImpl.php b/apps/dav/lib/CardDAV/AddressBookImpl.php index a592b9f0888..e9946e65aba 100644 --- a/apps/dav/lib/CardDAV/AddressBookImpl.php +++ b/apps/dav/lib/CardDAV/AddressBookImpl.php @@ -255,7 +255,13 @@ class AddressBookImpl implements IAddressBook { $result[$property->name] = []; } - $result[$property->name][] = $property->getValue(); + $type = $this->getTypeFromProperty($property); + if ($type !== null) { + $result[$property->name][$type] = $property->getValue(); + } else { + $result[$property->name][] = $property->getValue(); + } + } else { $result[$property->name] = $property->getValue(); -- cgit v1.2.3 From 61af60752555bb0c7f0a30cb7b7f35e7ca45fa45 Mon Sep 17 00:00:00 2001 From: Julius Härtl Date: Mon, 29 Oct 2018 17:00:09 +0100 Subject: Make enhancing entries with type property optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- apps/dav/lib/CardDAV/AddressBookImpl.php | 13 +++++--- .../Collaboration/Collaborators/MailPlugin.php | 11 +++++-- .../Collaboration/Collaborators/RemotePlugin.php | 19 +++++++++--- lib/public/IAddressBook.php | 14 +++++---- .../Collaborators/RemotePluginTest.php | 36 ++++++++++++++++++---- 5 files changed, 69 insertions(+), 24 deletions(-) (limited to 'apps/dav') diff --git a/apps/dav/lib/CardDAV/AddressBookImpl.php b/apps/dav/lib/CardDAV/AddressBookImpl.php index e9946e65aba..4f2491e1895 100644 --- a/apps/dav/lib/CardDAV/AddressBookImpl.php +++ b/apps/dav/lib/CardDAV/AddressBookImpl.php @@ -95,9 +95,11 @@ class AddressBookImpl implements IAddressBook { public function search($pattern, $searchProperties, $options) { $results = $this->backend->search($this->getKey(), $pattern, $searchProperties); + $withTypes = \array_key_exists('types', $options) && $options['types'] === true; + $vCards = []; foreach ($results as $result) { - $vCards[] = $this->vCard2Array($result['uri'], $this->readCard($result['carddata'])); + $vCards[] = $this->vCard2Array($result['uri'], $this->readCard($result['carddata']), $withTypes); } return $vCards; @@ -220,7 +222,7 @@ class AddressBookImpl implements IAddressBook { * @param VCard $vCard * @return array */ - protected function vCard2Array($uri, VCard $vCard) { + protected function vCard2Array($uri, VCard $vCard, $withTypes = false) { $result = [ 'URI' => $uri, ]; @@ -256,8 +258,11 @@ class AddressBookImpl implements IAddressBook { } $type = $this->getTypeFromProperty($property); - if ($type !== null) { - $result[$property->name][$type] = $property->getValue(); + if ($withTypes) { + $result[$property->name][] = [ + 'type' => $type, + 'value' => $property->getValue() + ]; } else { $result[$property->name][] = $property->getValue(); } diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php index e89e22d012e..1d0738677bb 100644 --- a/lib/private/Collaboration/Collaborators/MailPlugin.php +++ b/lib/private/Collaboration/Collaborators/MailPlugin.php @@ -84,11 +84,16 @@ class MailPlugin implements ISearchPlugin { foreach ($addressBookContacts as $contact) { if (isset($contact['EMAIL'])) { $emailAddresses = $contact['EMAIL']; - if (!is_array($emailAddresses)) { + if (\is_string($emailAddresses)) { $emailAddresses = [$emailAddresses]; } foreach ($emailAddresses as $type => $emailAddress) { $displayName = $emailAddress; + if (\is_array($emailAddress)) { + $emailAddressData = $emailAddress; + $emailAddress = $emailAddressData['value']; + $emailAddressType = $emailAddressData['type']; + } if (isset($contact['FN'])) { $displayName = $contact['FN'] . ' (' . $emailAddress . ')'; } @@ -163,7 +168,7 @@ class MailPlugin implements ISearchPlugin { $result['exact'][] = [ 'label' => $displayName, 'uuid' => $contact['UID'], - 'type' => $type, + 'type' => $emailAddressType, 'value' => [ 'shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => $emailAddress, @@ -173,7 +178,7 @@ class MailPlugin implements ISearchPlugin { $result['wide'][] = [ 'label' => $displayName, 'uuid' => $contact['UID'], - 'type' => $type, + 'type' => $emailAddressType, 'value' => [ 'shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => $emailAddress, diff --git a/lib/private/Collaboration/Collaborators/RemotePlugin.php b/lib/private/Collaboration/Collaborators/RemotePlugin.php index bde6d15b7b6..3da6bdeb637 100644 --- a/lib/private/Collaboration/Collaborators/RemotePlugin.php +++ b/lib/private/Collaboration/Collaborators/RemotePlugin.php @@ -44,12 +44,15 @@ class RemotePlugin implements ISearchPlugin { private $config; /** @var IUserManager */ private $userManager; + /** @var string */ + private $userId; - public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config, IUserManager $userManager) { + public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config, IUserManager $userManager, $userId) { $this->contactsManager = $contactsManager; $this->cloudIdManager = $cloudIdManager; $this->config = $config; $this->userManager = $userManager; + $this->userId = $userId; $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; } @@ -67,11 +70,17 @@ class RemotePlugin implements ISearchPlugin { } if (isset($contact['CLOUD'])) { $cloudIds = $contact['CLOUD']; - if (!is_array($cloudIds)) { + if (is_string($cloudIds)) { $cloudIds = [$cloudIds]; } $lowerSearch = strtolower($search); - foreach ($cloudIds as $type => $cloudId) { + foreach ($cloudIds as $cloudId) { + $cloudIdType = ''; + if (\is_array($cloudId)) { + $cloudIdData = $cloudId; + $cloudId = $cloudIdData['value']; + $cloudIdType = $cloudIdData['type']; + } try { list($remoteUser, $serverUrl) = $this->splitUserRemote($cloudId); } catch (\InvalidArgumentException $e) { @@ -90,7 +99,7 @@ class RemotePlugin implements ISearchPlugin { $result['exact'][] = [ 'label' => $contact['FN'] . " ($cloudId)", 'uuid' => $contact['UID'], - 'type' => $type, + 'type' => $cloudIdType, 'value' => [ 'shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => $cloudId, @@ -101,7 +110,7 @@ class RemotePlugin implements ISearchPlugin { $result['wide'][] = [ 'label' => $contact['FN'] . " ($cloudId)", 'uuid' => $contact['UID'], - 'type' => $type, + 'type' => $cloudIdType, 'value' => [ 'shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => $cloudId, diff --git a/lib/public/IAddressBook.php b/lib/public/IAddressBook.php index 67c34c9e8c9..4739e6f0c5b 100644 --- a/lib/public/IAddressBook.php +++ b/lib/public/IAddressBook.php @@ -55,16 +55,18 @@ namespace OCP { /** * @param string $pattern which should match within the $searchProperties * @param array $searchProperties defines the properties within the query pattern should match - * @param array $options - for future use. One should always have options! + * @param array $options Options to define the output format + * - types boolean (since 15.0.0) If set to true, fields that come with a TYPE property will be an array + * example: ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['type => 'HOME', 'value' => 'g@h.i']] * @return array an array of contacts which are arrays of key-value-pairs + * example result: + * [ + * ['id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => 'a@b.c', 'GEO' => '37.386013;-122.082932'], + * ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['d@e.f', 'g@h.i']] + * ] * @since 5.0.0 */ public function search($pattern, $searchProperties, $options); - // // dummy results - // return array( - // array('id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => 'a@b.c', 'GEO' => '37.386013;-122.082932'), - // array('id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => array('d@e.f', 'g@h.i')), - // ); /** * @param array $properties this array if key-value-pairs defines a contact diff --git a/tests/lib/Collaboration/Collaborators/RemotePluginTest.php b/tests/lib/Collaboration/Collaborators/RemotePluginTest.php index ac050e23562..e253f2dfc9f 100644 --- a/tests/lib/Collaboration/Collaborators/RemotePluginTest.php +++ b/tests/lib/Collaboration/Collaborators/RemotePluginTest.php @@ -66,7 +66,7 @@ class RemotePluginTest extends TestCase { } public function instantiatePlugin() { - $this->plugin = new RemotePlugin($this->contactsManager, $this->cloudIdManager, $this->config, $this->userManager); + $this->plugin = new RemotePlugin($this->contactsManager, $this->cloudIdManager, $this->config, $this->userManager, 'admin'); } /** @@ -158,14 +158,17 @@ class RemotePluginTest extends TestCase { 'test', [ [ + 'UID' => 'uid', 'FN' => 'User3 @ Localhost', ], [ + 'UID' => 'uid', 'FN' => 'User2 @ Localhost', 'CLOUD' => [ ], ], [ + 'UID' => 'uid1', 'FN' => 'User @ Localhost', 'CLOUD' => [ 'username@localhost', @@ -173,7 +176,7 @@ class RemotePluginTest extends TestCase { ], ], true, - ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => ['remotes' => []]], + ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid1', 'type' => '', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => ['remotes' => []]], false, true, ], @@ -181,14 +184,17 @@ class RemotePluginTest extends TestCase { 'test', [ [ + 'UID' => 'uid', 'FN' => 'User3 @ Localhost', ], [ + 'UID' => 'uid', 'FN' => 'User2 @ Localhost', 'CLOUD' => [ ], ], [ + 'UID' => 'uid', 'FN' => 'User @ Localhost', 'CLOUD' => [ 'username@localhost', @@ -204,14 +210,17 @@ class RemotePluginTest extends TestCase { 'test@remote', [ [ + 'UID' => 'uid', 'FN' => 'User3 @ Localhost', ], [ + 'UID' => 'uid', 'FN' => 'User2 @ Localhost', 'CLOUD' => [ ], ], [ + 'UID' => 'uid', 'FN' => 'User @ Localhost', 'CLOUD' => [ 'username@localhost', @@ -219,7 +228,7 @@ class RemotePluginTest extends TestCase { ], ], true, - ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => ['remotes' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]]]], + ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid', 'type' => '', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => ['remotes' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]]]], false, true, ], @@ -227,14 +236,17 @@ class RemotePluginTest extends TestCase { 'test@remote', [ [ + 'UID' => 'uid', 'FN' => 'User3 @ Localhost', ], [ + 'UID' => 'uid', 'FN' => 'User2 @ Localhost', 'CLOUD' => [ ], ], [ + 'UID' => 'uid', 'FN' => 'User @ Localhost', 'CLOUD' => [ 'username@localhost', @@ -250,14 +262,17 @@ class RemotePluginTest extends TestCase { 'username@localhost', [ [ + 'UID' => 'uid3', 'FN' => 'User3 @ Localhost', ], [ + 'UID' => '2', 'FN' => 'User2 @ Localhost', 'CLOUD' => [ ], ], [ + 'UID' => 'uid1', 'FN' => 'User @ Localhost', 'CLOUD' => [ 'username@localhost', @@ -265,7 +280,7 @@ class RemotePluginTest extends TestCase { ], ], true, - ['remotes' => [], 'exact' => ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]]]], + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid1', 'type' => '', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]]]], true, true, ], @@ -273,14 +288,17 @@ class RemotePluginTest extends TestCase { 'username@localhost', [ [ + 'UID' => 'uid3', 'FN' => 'User3 @ Localhost', ], [ + 'UID' => 'uid2', 'FN' => 'User2 @ Localhost', 'CLOUD' => [ ], ], [ + 'UID' => 'uid1', 'FN' => 'User @ Localhost', 'CLOUD' => [ 'username@localhost', @@ -288,7 +306,7 @@ class RemotePluginTest extends TestCase { ], ], false, - ['remotes' => [], 'exact' => ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]]]], + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid1', 'type' => '', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]]]], true, true, ], @@ -297,14 +315,17 @@ class RemotePluginTest extends TestCase { 'user name@localhost', [ [ + 'UID' => 'uid1', 'FN' => 'User3 @ Localhost', ], [ + 'UID' => 'uid2', 'FN' => 'User2 @ Localhost', 'CLOUD' => [ ], ], [ + 'UID' => 'uid3', 'FN' => 'User Name @ Localhost', 'CLOUD' => [ 'user name@localhost', @@ -312,7 +333,7 @@ class RemotePluginTest extends TestCase { ], ], false, - ['remotes' => [], 'exact' => ['remotes' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost']]]]], + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'User Name @ Localhost (user name@localhost)', 'uuid' => 'uid3', 'type' => '', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost']]]]], true, true, ], @@ -321,14 +342,17 @@ class RemotePluginTest extends TestCase { 'user space@remote', [ [ + 'UID' => 'uid3', 'FN' => 'User3 @ Localhost', ], [ + 'UID' => 'uid2', 'FN' => 'User2 @ Localhost', 'CLOUD' => [ ], ], [ + 'UID' => 'uid1', 'FN' => 'User @ Localhost', 'CLOUD' => [ 'username@localhost', -- cgit v1.2.3 From b9c5e569d36fb57baae6c340d31970d5f852261e Mon Sep 17 00:00:00 2001 From: Julius Härtl Date: Tue, 30 Oct 2018 14:43:08 +0100 Subject: Update AddressBookImpl documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- apps/dav/lib/CardDAV/AddressBookImpl.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'apps/dav') diff --git a/apps/dav/lib/CardDAV/AddressBookImpl.php b/apps/dav/lib/CardDAV/AddressBookImpl.php index 4f2491e1895..1aedd5d5643 100644 --- a/apps/dav/lib/CardDAV/AddressBookImpl.php +++ b/apps/dav/lib/CardDAV/AddressBookImpl.php @@ -88,7 +88,15 @@ class AddressBookImpl implements IAddressBook { /** * @param string $pattern which should match within the $searchProperties * @param array $searchProperties defines the properties within the query pattern should match - * @param array $options - for future use. One should always have options! + * @param array $options Options to define the output format + * - types boolean (since 15.0.0) If set to true, fields that come with a TYPE property will be an array + * example: ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['type => 'HOME', 'value' => 'g@h.i']] + * @return array an array of contacts which are arrays of key-value-pairs + * example result: + * [ + * ['id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => 'a@b.c', 'GEO' => '37.386013;-122.082932'], + * ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['d@e.f', 'g@h.i']] + * ] * @return array an array of contacts which are arrays of key-value-pairs * @since 5.0.0 */ -- cgit v1.2.3 From 93de63777e4a519484ad8f50f7a8ee809697c20a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2018 19:15:23 +0200 Subject: extend storage api to allow directly writing a stream to storage this removes the need for temporary storages with some external storage backends. The new method is added to a separate interface to maintain compatibility with storage backends implementing the storage interface directly (without inheriting common) Currently the interface is implemented for objectstorage based storages and local storage and used by webdav uploads Signed-off-by: Robin Appelman --- apps/dav/lib/Connector/Sabre/File.php | 27 +++++---- .../Files/ObjectStore/ObjectStoreStorage.php | 68 +++++++++++++++------- lib/private/Files/Storage/Common.php | 19 +++++- lib/private/Files/Storage/Local.php | 4 ++ lib/private/Files/Stream/CountReadStream.php | 65 +++++++++++++++++++++ lib/public/Files/Storage/IWriteStreamStorage.php | 40 +++++++++++++ tests/lib/Files/Stream/CountReadStreamTest.php | 49 ++++++++++++++++ 7 files changed, 238 insertions(+), 34 deletions(-) create mode 100644 lib/private/Files/Stream/CountReadStream.php create mode 100644 lib/public/Files/Storage/IWriteStreamStorage.php create mode 100644 tests/lib/Files/Stream/CountReadStreamTest.php (limited to 'apps/dav') diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index 9e927ff85e5..a8e6d8b907c 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -164,14 +164,19 @@ class File extends Node implements IFile { $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE); } - $target = $partStorage->fopen($internalPartPath, 'wb'); - if ($target === false) { - \OC::$server->getLogger()->error('\OC\Files\Filesystem::fopen() failed', ['app' => 'webdav']); - // because we have no clue about the cause we can only throw back a 500/Internal Server Error - throw new Exception('Could not write file contents'); + if ($partStorage->instanceOfStorage(Storage\IWriteStreamStorage::class)) { + $count = $partStorage->writeStream($internalPartPath, $data); + $result = $count > 0; + } else { + $target = $partStorage->fopen($internalPartPath, 'wb'); + if ($target === false) { + \OC::$server->getLogger()->error('\OC\Files\Filesystem::fopen() failed', ['app' => 'webdav']); + // because we have no clue about the cause we can only throw back a 500/Internal Server Error + throw new Exception('Could not write file contents'); + } + list($count, $result) = \OC_Helper::streamCopy($data, $target); + fclose($target); } - list($count, $result) = \OC_Helper::streamCopy($data, $target); - fclose($target); if ($result === false) { $expected = -1; @@ -185,7 +190,7 @@ class File extends Node implements IFile { // double check if the file was fully received // compare expected and actual size if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['REQUEST_METHOD'] === 'PUT') { - $expected = (int) $_SERVER['CONTENT_LENGTH']; + $expected = (int)$_SERVER['CONTENT_LENGTH']; if ($count !== $expected) { throw new BadRequest('expected filesize ' . $expected . ' got ' . $count); } @@ -219,7 +224,7 @@ class File extends Node implements IFile { $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath); $fileExists = $storage->file_exists($internalPath); if ($renameOkay === false || $fileExists === false) { - \OC::$server->getLogger()->error('renaming part file to final file failed ($run: ' . ( $run ? 'true' : 'false' ) . ', $renameOkay: ' . ( $renameOkay ? 'true' : 'false' ) . ', $fileExists: ' . ( $fileExists ? 'true' : 'false' ) . ')', ['app' => 'webdav']); + \OC::$server->getLogger()->error('renaming part file to final file failed ($run: ' . ($run ? 'true' : 'false') . ', $renameOkay: ' . ($renameOkay ? 'true' : 'false') . ', $fileExists: ' . ($fileExists ? 'true' : 'false') . ')', ['app' => 'webdav']); throw new Exception('Could not rename part file to final file'); } } catch (ForbiddenException $ex) { @@ -246,7 +251,7 @@ class File extends Node implements IFile { $this->header('X-OC-MTime: accepted'); } } - + if ($view) { $this->emitPostHooks($exists); } @@ -443,7 +448,7 @@ class File extends Node implements IFile { //detect aborted upload if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT') { if (isset($_SERVER['CONTENT_LENGTH'])) { - $expected = (int) $_SERVER['CONTENT_LENGTH']; + $expected = (int)$_SERVER['CONTENT_LENGTH']; if ($bytesWritten !== $expected) { $chunk_handler->remove($info['index']); throw new BadRequest( diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index 3ce919a4cbe..71acd27783c 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -28,6 +28,7 @@ namespace OC\Files\ObjectStore; use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use OC\Files\Cache\CacheEntry; +use OC\Files\Stream\CountReadStream; use OCP\Files\ObjectStore\IObjectStore; class ObjectStoreStorage extends \OC\Files\Storage\Common { @@ -382,25 +383,48 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { } public function writeBack($tmpFile, $path) { + $size = filesize($tmpFile); + $this->writeStream($path, fopen($tmpFile, 'r'), $size); + } + + /** + * external changes are not supported, exclusive access to the object storage is assumed + * + * @param string $path + * @param int $time + * @return false + */ + public function hasUpdated($path, $time) { + return false; + } + + public function needsPartFile() { + return false; + } + + public function file_put_contents($path, $data) { + $stream = fopen('php://temp', 'r+'); + fwrite($stream, $data); + rewind($stream); + return $this->writeStream($path, $stream, strlen($data)) > 0; + } + + public function writeStream(string $path, $stream, int $size = null): int { $stat = $this->stat($path); if (empty($stat)) { // create new file - $stat = array( + $stat = [ 'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, - ); + ]; } // update stat with new data $mTime = time(); - $stat['size'] = filesize($tmpFile); + $stat['size'] = (int)$size; $stat['mtime'] = $mTime; $stat['storage_mtime'] = $mTime; - // run path based detection first, to use file extension because $tmpFile is only a random string $mimetypeDetector = \OC::$server->getMimeTypeDetector(); $mimetype = $mimetypeDetector->detectPath($path); - if ($mimetype === 'application/octet-stream') { - $mimetype = $mimetypeDetector->detect($tmpFile); - } $stat['mimetype'] = $mimetype; $stat['etag'] = $this->getETag($path); @@ -408,7 +432,20 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { $fileId = $this->getCache()->put($path, $stat); try { //upload to object storage - $this->objectStore->writeObject($this->getURN($fileId), fopen($tmpFile, 'r')); + if ($size === null) { + $countStream = CountReadStream::wrap($stream, function ($writtenSize) use ($fileId, &$size) { + $this->getCache()->update($fileId, [ + 'size' => $writtenSize + ]); + $size = $writtenSize; + }); + $this->objectStore->writeObject($this->getURN($fileId), $countStream); + if (is_resource($countStream)) { + fclose($countStream); + } + } else { + $this->objectStore->writeObject($this->getURN($fileId), $stream); + } } catch (\Exception $ex) { $this->getCache()->remove($path); $this->logger->logException($ex, [ @@ -417,20 +454,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { ]); throw $ex; // make this bubble up } - } - /** - * external changes are not supported, exclusive access to the object storage is assumed - * - * @param string $path - * @param int $time - * @return false - */ - public function hasUpdated($path, $time) { - return false; - } - - public function needsPartFile() { - return false; + return $size; } } diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index b6c82f3a1df..6324050b472 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -54,6 +54,7 @@ use OCP\Files\InvalidPathException; use OCP\Files\ReservedWordException; use OCP\Files\Storage\ILockingStorage; use OCP\Files\Storage\IStorage; +use OCP\Files\Storage\IWriteStreamStorage; use OCP\ILogger; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; @@ -69,7 +70,7 @@ use OCP\Lock\LockedException; * Some \OC\Files\Storage\Common methods call functions which are first defined * in classes which extend it, e.g. $this->stat() . */ -abstract class Common implements Storage, ILockingStorage { +abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { use LocalTempFileTrait; @@ -809,4 +810,20 @@ abstract class Common implements Storage, ILockingStorage { public function needsPartFile() { return true; } + + /** + * fallback implementation + * + * @param string $path + * @param resource $stream + * @param int $size + * @return int + */ + public function writeStream(string $path, $stream, int $size = null): int { + $target = $this->fopen($path, 'w'); + list($count, $result) = \OC_Helper::streamCopy($stream, $target); + fclose($stream); + fclose($target); + return $count; + } } diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index 46b53dcf95c..5f7232e64b3 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -462,4 +462,8 @@ class Local extends \OC\Files\Storage\Common { return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); } } + + public function writeStream(string $path, $stream, int $size = null): int { + return (int)file_put_contents($this->getSourcePath($path), $stream); + } } diff --git a/lib/private/Files/Stream/CountReadStream.php b/lib/private/Files/Stream/CountReadStream.php new file mode 100644 index 00000000000..93cadf8f214 --- /dev/null +++ b/lib/private/Files/Stream/CountReadStream.php @@ -0,0 +1,65 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program 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 (at your option) 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 . + * + */ + +namespace OC\Files\Stream; + +use Icewind\Streams\Wrapper; + +class CountReadStream extends Wrapper { + /** @var int */ + private $count; + + /** @var callback */ + private $callback; + + public static function wrap($source, $callback) { + $context = stream_context_create(array( + 'count' => array( + 'source' => $source, + 'callback' => $callback, + ) + )); + return Wrapper::wrapSource($source, $context, 'count', self::class); + } + + public function dir_opendir($path, $options) { + return false; + } + + public function stream_open($path, $mode, $options, &$opened_path) { + $context = $this->loadContext('count'); + + $this->callback = $context['callback']; + return true; + } + + public function stream_read($count) { + $result = parent::stream_read($count); + $this->count += strlen($result); + return $result; + } + + public function stream_close() { + $result = parent::stream_close(); + call_user_func($this->callback, $this->count); + return $result; + } +} diff --git a/lib/public/Files/Storage/IWriteStreamStorage.php b/lib/public/Files/Storage/IWriteStreamStorage.php new file mode 100644 index 00000000000..39a28dd037b --- /dev/null +++ b/lib/public/Files/Storage/IWriteStreamStorage.php @@ -0,0 +1,40 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program 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 (at your option) 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 . + * + */ + +namespace OCP\Files\Storage; + +/** + * Interface that adds the ability to write a stream directly to file + * + * @since 15.0.0 + */ +interface IWriteStreamStorage extends IStorage { + /** + * Write the data from a stream to a file + * + * @param string $path + * @param resource $stream + * @param int|null $size the size of the stream if known in advance + * @return int the number of bytes written + * @since 15.0.0 + */ + public function writeStream(string $path, $stream, int $size = null): int; +} diff --git a/tests/lib/Files/Stream/CountReadStreamTest.php b/tests/lib/Files/Stream/CountReadStreamTest.php new file mode 100644 index 00000000000..99291d1644f --- /dev/null +++ b/tests/lib/Files/Stream/CountReadStreamTest.php @@ -0,0 +1,49 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program 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 (at your option) 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 . + * + */ + +namespace Test\Files\Stream; + +use OC\Files\Stream\CountReadStream; +use Test\TestCase; + +class CountReadStreamTest extends TestCase { + private function getStream($data) { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, $data); + rewind($handle); + return $handle; + } + + public function testBasicCount() { + $source = $this->getStream('foo'); + $stream = CountReadStream::wrap($source, function ($size) { + $this->assertEquals(3, $size); + }); + stream_get_contents($stream); + } + + public function testLarger() { + $stream = CountReadStream::wrap(fopen(__DIR__ . '/../../../data/testimage.mp4', 'r'), function ($size) { + $this->assertEquals(383631, $size); + }); + stream_get_contents($stream); + } +} -- cgit v1.2.3 From 4094a5e74a6d64050bbaeac8325895c9b226f328 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 31 Oct 2018 16:31:42 +0100 Subject: update dav tests Signed-off-by: Robin Appelman --- apps/dav/tests/unit/Connector/Sabre/FileTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'apps/dav') diff --git a/apps/dav/tests/unit/Connector/Sabre/FileTest.php b/apps/dav/tests/unit/Connector/Sabre/FileTest.php index 5e7a6374206..edb61edc6ed 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FileTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FileTest.php @@ -164,7 +164,7 @@ class FileTest extends \Test\TestCase { public function testSimplePutFails($thrownException, $expectedException, $checkPreviousClass = true) { // setup $storage = $this->getMockBuilder(Local::class) - ->setMethods(['fopen']) + ->setMethods(['writeStream']) ->setConstructorArgs([['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]]) ->getMock(); \OC\Files\Filesystem::mount($storage, [], $this->user . '/'); @@ -182,11 +182,11 @@ class FileTest extends \Test\TestCase { if ($thrownException !== null) { $storage->expects($this->once()) - ->method('fopen') + ->method('writeStream') ->will($this->throwException($thrownException)); } else { $storage->expects($this->once()) - ->method('fopen') + ->method('writeStream') ->will($this->returnValue(false)); } -- cgit v1.2.3 From 9b3cc72f7c033c4dba9a2b1e21e0e38f488318b5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 31 Oct 2018 19:41:55 +0100 Subject: fix writeStream for jail wrapper Signed-off-by: Robin Appelman --- apps/dav/lib/Connector/Sabre/File.php | 2 +- lib/private/Files/Storage/Common.php | 3 +++ lib/private/Files/Storage/Wrapper/Jail.php | 15 +++++++++++++++ tests/lib/Files/Storage/Storage.php | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) (limited to 'apps/dav') diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index a8e6d8b907c..57c072fda47 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -224,7 +224,7 @@ class File extends Node implements IFile { $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath); $fileExists = $storage->file_exists($internalPath); if ($renameOkay === false || $fileExists === false) { - \OC::$server->getLogger()->error('renaming part file to final file failed ($run: ' . ($run ? 'true' : 'false') . ', $renameOkay: ' . ($renameOkay ? 'true' : 'false') . ', $fileExists: ' . ($fileExists ? 'true' : 'false') . ')', ['app' => 'webdav']); + \OC::$server->getLogger()->error('renaming part file to final file failed $renameOkay: ' . ($renameOkay ? 'true' : 'false') . ', $fileExists: ' . ($fileExists ? 'true' : 'false') . ')', ['app' => 'webdav']); throw new Exception('Could not rename part file to final file'); } } catch (ForbiddenException $ex) { diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 6324050b472..72fe3a79792 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -821,6 +821,9 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { */ public function writeStream(string $path, $stream, int $size = null): int { $target = $this->fopen($path, 'w'); + if (!$target) { + return 0; + } list($count, $result) = \OC_Helper::streamCopy($stream, $target); fclose($stream); fclose($target); diff --git a/lib/private/Files/Storage/Wrapper/Jail.php b/lib/private/Files/Storage/Wrapper/Jail.php index 56514af6d80..f21b5716467 100644 --- a/lib/private/Files/Storage/Wrapper/Jail.php +++ b/lib/private/Files/Storage/Wrapper/Jail.php @@ -29,6 +29,7 @@ use OC\Files\Cache\Wrapper\CacheJail; use OC\Files\Cache\Wrapper\JailPropagator; use OC\Files\Filesystem; use OCP\Files\Storage\IStorage; +use OCP\Files\Storage\IWriteStreamStorage; use OCP\Lock\ILockingProvider; /** @@ -515,4 +516,18 @@ class Jail extends Wrapper { $this->propagator = new JailPropagator($storage, \OC::$server->getDatabaseConnection()); return $this->propagator; } + + public function writeStream(string $path, $stream, int $size = null): int { + $storage = $this->getWrapperStorage(); + if ($storage->instanceOfStorage(IWriteStreamStorage::class)) { + /** @var IWriteStreamStorage $storage */ + return $storage->writeStream($this->getUnjailedPath($path), $stream, $size); + } else { + $target = $this->fopen($path, 'w'); + list($count, $result) = \OC_Helper::streamCopy($stream, $target); + fclose($stream); + fclose($target); + return $count; + } + } } diff --git a/tests/lib/Files/Storage/Storage.php b/tests/lib/Files/Storage/Storage.php index 04aafece2e3..a25a3f74f9e 100644 --- a/tests/lib/Files/Storage/Storage.php +++ b/tests/lib/Files/Storage/Storage.php @@ -23,6 +23,7 @@ namespace Test\Files\Storage; use OC\Files\Cache\Watcher; +use OCP\Files\Storage\IWriteStreamStorage; abstract class Storage extends \Test\TestCase { /** @@ -628,4 +629,20 @@ abstract class Storage extends \Test\TestCase { $this->instance->rename('bar.txt.part', 'bar.txt'); $this->assertEquals('bar', $this->instance->file_get_contents('bar.txt')); } + + public function testWriteStream() { + $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt'; + + if (!$this->instance->instanceOfStorage(IWriteStreamStorage::class)) { + $this->markTestSkipped('Not a WriteSteamStorage'); + } + /** @var IWriteStreamStorage $storage */ + $storage = $this->instance; + + $source = fopen($textFile, 'r'); + + $storage->writeStream('test.txt', $source); + $this->assertTrue($storage->file_exists('test.txt')); + $this->assertEquals(file_get_contents($textFile), $storage->file_get_contents('test.txt')); + } } -- cgit v1.2.3 From 9afff2fb20ec2c00cc958639342add7b19a00e78 Mon Sep 17 00:00:00 2001 From: "John Molakvoæ (skjnldsv)" Date: Sat, 27 Oct 2018 11:54:57 +0200 Subject: Properly set uid and create and update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- apps/dav/lib/CardDAV/CardDavBackend.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'apps/dav') diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php index a2d3b03147b..a9d65f2f0f9 100644 --- a/apps/dav/lib/CardDAV/CardDavBackend.php +++ b/apps/dav/lib/CardDAV/CardDavBackend.php @@ -494,7 +494,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { */ function getCards($addressBookId) { $query = $this->db->getQueryBuilder(); - $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata']) + $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid']) ->from('cards') ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))); @@ -525,7 +525,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { */ function getCard($addressBookId, $cardUri) { $query = $this->db->getQueryBuilder(); - $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata']) + $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid']) ->from('cards') ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri))) @@ -563,7 +563,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { $cards = []; $query = $this->db->getQueryBuilder(); - $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata']) + $query->select(['id', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid']) ->from('cards') ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->andWhere($query->expr()->in('uri', $query->createParameter('uri'))); @@ -609,6 +609,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { */ function createCard($addressBookId, $cardUri, $cardData) { $etag = md5($cardData); + $uid = $this->getUID($cardData); $query = $this->db->getQueryBuilder(); $query->insert('cards') @@ -619,6 +620,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { 'addressbookid' => $query->createNamedParameter($addressBookId), 'size' => $query->createNamedParameter(strlen($cardData)), 'etag' => $query->createNamedParameter($etag), + 'uid' => $query->createNamedParameter($uid), ]) ->execute(); @@ -661,6 +663,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { */ function updateCard($addressBookId, $cardUri, $cardData) { + $uid = $this->getUID($cardData); $etag = md5($cardData); $query = $this->db->getQueryBuilder(); $query->update('cards') @@ -668,6 +671,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { ->set('lastmodified', $query->createNamedParameter(time())) ->set('size', $query->createNamedParameter(strlen($cardData))) ->set('etag', $query->createNamedParameter($etag)) + ->set('uid', $query->createNamedParameter($uid)) ->where($query->expr()->eq('uri', $query->createNamedParameter($cardUri))) ->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) ->execute(); @@ -1125,4 +1129,10 @@ class CardDavBackend implements BackendInterface, SyncSupport { $addressbookInfo[$displaynameKey] = $principalInformation['{DAV:}displayname']; } } + + private function getUID($cardData) { + $vCard = Reader::read($cardData); + $uid = $vCard->UID->getValue(); + return $uid; + } } -- cgit v1.2.3 From d89edb28c4218eeadc5c2c5bbea6253dfc599a5f Mon Sep 17 00:00:00 2001 From: "John Molakvoæ (skjnldsv)" Date: Tue, 30 Oct 2018 13:03:49 +0100 Subject: move migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- apps/dav/composer/composer/autoload_classmap.php | 1 + apps/dav/composer/composer/autoload_static.php | 1 + apps/dav/lib/CardDAV/CardDavBackend.php | 15 +++- .../Migration/Version1008Date20181030113700.php | 52 +++++++++++++ core/Migrations/Version15000Date20180927120000.php | 87 ---------------------- lib/composer/composer/autoload_classmap.php | 1 - lib/composer/composer/autoload_static.php | 1 - lib/private/Repair/NC15/SetVcardDatabaseUID.php | 28 ++++--- 8 files changed, 84 insertions(+), 102 deletions(-) create mode 100644 apps/dav/lib/Migration/Version1008Date20181030113700.php delete mode 100644 core/Migrations/Version15000Date20180927120000.php (limited to 'apps/dav') diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index fe7557f7e08..c9680651ff9 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -156,6 +156,7 @@ return array( 'OCA\\DAV\\Migration\\Version1005Date20180530124431' => $baseDir . '/../lib/Migration/Version1005Date20180530124431.php', 'OCA\\DAV\\Migration\\Version1006Date20180619154313' => $baseDir . '/../lib/Migration/Version1006Date20180619154313.php', 'OCA\\DAV\\Migration\\Version1007Date20181007225117' => $baseDir . '/../lib/Migration/Version1007Date20181007225117.php', + 'OCA\\DAV\\Migration\\Version1008Date20181030113700' => $baseDir . '/../lib/Migration/Version1008Date20181030113700.php', 'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php', 'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php', 'OCA\\DAV\\Settings\\CalDAVSettings' => $baseDir . '/../lib/Settings/CalDAVSettings.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index 1668f1270f5..71879abc0a5 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -171,6 +171,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Migration\\Version1005Date20180530124431' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180530124431.php', 'OCA\\DAV\\Migration\\Version1006Date20180619154313' => __DIR__ . '/..' . '/../lib/Migration/Version1006Date20180619154313.php', 'OCA\\DAV\\Migration\\Version1007Date20181007225117' => __DIR__ . '/..' . '/../lib/Migration/Version1007Date20181007225117.php', + 'OCA\\DAV\\Migration\\Version1008Date20181030113700' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181030113700.php', 'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php', 'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php', 'OCA\\DAV\\Settings\\CalDAVSettings' => __DIR__ . '/..' . '/../lib/Settings/CalDAVSettings.php', diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php index a9d65f2f0f9..8ef0e0baf56 100644 --- a/apps/dav/lib/CardDAV/CardDavBackend.php +++ b/apps/dav/lib/CardDAV/CardDavBackend.php @@ -1130,9 +1130,20 @@ class CardDavBackend implements BackendInterface, SyncSupport { } } + /** + * Extract UID from vcard + * + * @param string $cardData the vcard raw data + * @return string the uid or empty if none + * @throws BadRequest + */ private function getUID($cardData) { $vCard = Reader::read($cardData); - $uid = $vCard->UID->getValue(); - return $uid; + if ($vCard->UID) { + $uid = $vCard->UID->getValue(); + return $uid; + } + // should already be handled, but just in case + throw new BadRequest('vCards on CardDAV servers MUST have a UID property'); } } diff --git a/apps/dav/lib/Migration/Version1008Date20181030113700.php b/apps/dav/lib/Migration/Version1008Date20181030113700.php new file mode 100644 index 00000000000..1cc6223a9e9 --- /dev/null +++ b/apps/dav/lib/Migration/Version1008Date20181030113700.php @@ -0,0 +1,52 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program 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 (at your option) 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 . + * + */ + +namespace OCA\DAV\Migration; + +use Closure; + +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\SimpleMigrationStep; +use OCP\Migration\IOutput; + +/** + * add column for share notes + * + * Class Version15000Date20180927120000 + */ +class Version1008Date20181030113700 extends SimpleMigrationStep { + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { + + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('cards'); + $table->addColumn('uid', Type::STRING, [ + 'notnull' => false, + 'length' => 255 + ]); + + return $schema; + } +} diff --git a/core/Migrations/Version15000Date20180927120000.php b/core/Migrations/Version15000Date20180927120000.php deleted file mode 100644 index d38914314ff..00000000000 --- a/core/Migrations/Version15000Date20180927120000.php +++ /dev/null @@ -1,87 +0,0 @@ - - * - * @license GNU AGPL version 3 or any later version - * - * This program 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 (at your option) 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 . - * - */ - -namespace OC\Core\Migrations; - -use Doctrine\DBAL\Types\Type; -use OCP\DB\ISchemaWrapper; -use OCP\Migration\SimpleMigrationStep; - -/** - * add column for share notes - * - * Class Version15000Date20180927120000 - */ -class Version15000Date20180927120000 extends SimpleMigrationStep { - public function changeSchema(\OCP\Migration\IOutput $output, \Closure $schemaClosure, array $options) { - - /** @var ISchemaWrapper $schema */ - $schema = $schemaClosure(); - - if ($schema->hasTable('cards')) { - $table = $schema->getTable('cards'); - $table->addColumn('uid', Type::STRING, [ - 'notnull' => false, - 'length' => 255 - ]); - } else { - $table = $schema->createTable('cards'); - $table->addColumn('id', 'bigint', [ - 'autoincrement' => true, - 'notnull' => true, - 'length' => 11, - 'unsigned' => true, - ]); - $table->addColumn('addressbookid', 'integer', [ - 'notnull' => true, - 'default' => 0, - ]); - $table->addColumn('carddata', 'blob', [ - 'notnull' => false, - ]); - $table->addColumn('uri', 'string', [ - 'notnull' => false, - 'length' => 255, - ]); - $table->addColumn('lastmodified', 'bigint', [ - 'notnull' => false, - 'length' => 11, - 'unsigned' => true, - ]); - $table->addColumn('etag', 'string', [ - 'notnull' => false, - 'length' => 32, - ]); - $table->addColumn('size', 'bigint', [ - 'notnull' => true, - 'length' => 11, - 'unsigned' => true, - ]); - $table->addColumn('uid', Type::STRING, [ - 'notnull' => false, - 'length' => 255 - ]); - $table->setPrimaryKey(['id']); - } - - return $schema; - } -} diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index dc20789576b..d7523d52caa 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -670,7 +670,6 @@ return array( 'OC\\Core\\Migrations\\Version14000Date20180710092004' => $baseDir . '/core/Migrations/Version14000Date20180710092004.php', 'OC\\Core\\Migrations\\Version14000Date20180712153140' => $baseDir . '/core/Migrations/Version14000Date20180712153140.php', 'OC\\Core\\Migrations\\Version15000Date20180926101451' => $baseDir . '/core/Migrations/Version15000Date20180926101451.php', - 'OC\\Core\\Migrations\\Version15000Date20180927120000' => $baseDir . '/core/Migrations/Version15000Date20180927120000.php', 'OC\\Core\\Migrations\\Version15000Date20181015062942' => $baseDir . '/core/Migrations/Version15000Date20181015062942.php', 'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php', 'OC\\DB\\AdapterMySQL' => $baseDir . '/lib/private/DB/AdapterMySQL.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index fe2ff5d760e..cf2fef4282d 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -700,7 +700,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Migrations\\Version14000Date20180710092004' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180710092004.php', 'OC\\Core\\Migrations\\Version14000Date20180712153140' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180712153140.php', 'OC\\Core\\Migrations\\Version15000Date20180926101451' => __DIR__ . '/../../..' . '/core/Migrations/Version15000Date20180926101451.php', - 'OC\\Core\\Migrations\\Version15000Date20180927120000' => __DIR__ . '/../../..' . '/core/Migrations/Version15000Date20180927120000.php', 'OC\\Core\\Migrations\\Version15000Date20181015062942' => __DIR__ . '/../../..' . '/core/Migrations/Version15000Date20181015062942.php', 'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php', 'OC\\DB\\AdapterMySQL' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterMySQL.php', diff --git a/lib/private/Repair/NC15/SetVcardDatabaseUID.php b/lib/private/Repair/NC15/SetVcardDatabaseUID.php index 4d2805247bf..ccf6c47cbc8 100644 --- a/lib/private/Repair/NC15/SetVcardDatabaseUID.php +++ b/lib/private/Repair/NC15/SetVcardDatabaseUID.php @@ -27,6 +27,7 @@ use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; +use Sabre\VObject\Reader; class SetVcardDatabaseUID implements IRepairStep { const MAX_ROWS = 1000; @@ -70,20 +71,27 @@ class SetVcardDatabaseUID implements IRepairStep { } while (count($rows) > 0); } - private function getUid($carddata) { - preg_match('/UID:(.*)$/m', $carddata, $matches); - if (count($matches) > 1) { - return $matches[1]; + /** + * Extract UID from vcard + * + * @param string $cardData the vcard raw data + * @return string the uid or empty if none + */ + private function getUID(string $cardData): string { + $vCard = Reader::read($cardData); + if ($vCard->UID) { + $uid = $vCard->UID->getValue(); + return $uid; } - return false; + return ''; } /** * @param int $id * @param string $uid */ - private function update($id, $uid) { + private function update(int $id, string $uid) { if (!$this->updateQuery) { $builder = $this->connection->getQueryBuilder(); @@ -98,16 +106,14 @@ class SetVcardDatabaseUID implements IRepairStep { $this->updateQuery->execute(); } - private function repair() { + private function repair(): int { $this->connection->beginTransaction(); $entries = $this->getInvalidEntries(); $count = 0; foreach ($entries as $entry) { $count++; - $uid = $this->getUid($entry['carddata']); - if ($uid !== false) { - $this->update($entry['id'], $uid); - } + $uid = $this->getUID($entry['carddata']); + $this->update($entry['id'], $uid); } $this->connection->commit(); -- cgit v1.2.3 From 7246f2ace50e41f68433cd89755413b512fa7909 Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Thu, 1 Nov 2018 14:44:35 +0100 Subject: Update PHPDoc to reflect the actual behaviour Signed-off-by: Morris Jobke --- apps/dav/lib/CardDAV/CardDavBackend.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'apps/dav') diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php index 8ef0e0baf56..80a3fe2f11f 100644 --- a/apps/dav/lib/CardDAV/CardDavBackend.php +++ b/apps/dav/lib/CardDAV/CardDavBackend.php @@ -1134,8 +1134,8 @@ class CardDavBackend implements BackendInterface, SyncSupport { * Extract UID from vcard * * @param string $cardData the vcard raw data - * @return string the uid or empty if none - * @throws BadRequest + * @return string the uid + * @throws BadRequest if no UID is available */ private function getUID($cardData) { $vCard = Reader::read($cardData); -- cgit v1.2.3