Merge branch 'master' into master

This commit is contained in:
mbi 2015-12-30 10:34:42 +01:00
commit 63974992f9
651 changed files with 10958 additions and 2702 deletions

View File

@ -1,4 +1,3 @@
# Version: 9.0.0
<IfModule mod_headers.c>
<IfModule mod_fcgid.c>
<IfModule mod_setenvif.c>
@ -51,9 +50,6 @@
# Rewrite rules for `front_controller_active`
Options -MultiViews
<IfModule mod_dir.c>
DirectorySlash off
</IfModule>
RewriteRule ^core/js/oc.js$ index.php/core/js/oc.js [PT,E=PATH_INFO:$1]
RewriteRule ^core/preview.png$ index.php/core/preview.png [PT,E=PATH_INFO:$1]
RewriteCond %{REQUEST_FILENAME} !\.(css|js|svg|gif|png|html|ttf|woff)$

View File

@ -305,7 +305,8 @@ Robin Appelman <icewind@owncloud.com> Robin <robin@Amaya.(none)>
Robin Appelman <icewind@owncloud.com> Robin Appelman <icewind1991@gmail.com>
Robin Appelman <icewind@owncloud.com> Robin Appelman <icewind1991@gmail>
Robin Appelman <icewind@owncloud.com> Robin Appelman <robin@icewind.nl>
Robin McCorkell <rmccorkell@karoshi.org.uk> Robin McCorkell <rmccorkell@owncloud.com>
Robin McCorkell <robin@mccorkell.me.uk> Robin McCorkell <rmccorkell@karoshi.org.uk>
Robin McCorkell <robin@mccorkell.me.uk> Robin McCorkell <rmccorkell@owncloud.com>
Rodrigo Hjort <rodrigo.hjort@gmail.com>
Roeland Jago Douma <rullzer@owncloud.com> Roeland Jago Douma <roeland@famdouma.nl>
rok <brejktru@gmail.com>

View File

@ -5,7 +5,11 @@
{
"name": "DeepDiver1975",
"files": ["apps/dav/**"]
},
{
"name": "Xenopathic",
"files": ["apps/files_external/**"]
}
],
"userBlacklist": ["owncloud-bot"]
"userBlacklist": ["owncloud-bot", "scrutinizer-auto-fixer"]
}

View File

@ -25,22 +25,22 @@ before_install:
install:
- sh -c "if [ '$TEST_DAV' = '1' ]; then bash tests/travis/install.sh $DB; fi"
- sh -c "if [ '$TEST_DAV' = '1' ]; then bash apps/dav/tests/travis/$TC/install.sh; fi"
script:
- sh -c "if [ '$TEST_DAV' != '1' ]; then echo \"Not testing DAV\"; fi"
- sh -c "if [ '$TEST_DAV' = '1' ]; then echo \"Testing DAV\"; fi"
- sh -c "if [ '$TEST_DAV' = '1' ]; then bash apps/dav/tests/travis/$TC.sh; fi"
- sh -c "if [ '$TEST_DAV' = '1' ]; then bash apps/dav/tests/travis/$TC/script.sh; fi"
matrix:
include:
- php: 5.4
env: DB=pgsql;TC=litmus-v1
- php: 5.4
env: DB=sqlite;TC=carddavtester
# - php: 5.4
# env: DB=pgsql;TC=carddavtester
# - php: 5.4
# env: DB=mysql;TC=caldavtester
env: DB=sqlite;TC=carddav
- php: 5.4
env: DB=sqlite;TC=caldav
fast_finish: true

@ -1 +1 @@
Subproject commit a7b34d6f831c8fa363f389d27acd0150128fc0b9
Subproject commit a5b2a3cdb03cbf5a7246c6185cd4d473f697803e

View File

@ -18,20 +18,8 @@ If you have questions about how to install or use ownCloud, please direct these
- [iOS client](https://github.com/owncloud/ios/issues)
- [Desktop client](https://github.com/owncloud/client/issues)
- [Documentation](https://github.com/owncloud/documentation/issues)
- Apps:
- [Activity](https://github.com/owncloud/activity/issues)
- [Bookmarks](https://github.com/owncloud/bookmarks/issues)
- [Calendar](https://github.com/owncloud/calendar/issues)
- [Contacts](https://github.com/owncloud/contacts/issues)
- [Documents](https://github.com/owncloud/documents/issues)
- [Gallery](https://github.com/owncloud/gallery/issues)
- [Mail](https://github.com/owncloud/mail/issues)
- [Music](https://github.com/owncloud/music/issues)
- [News](https://github.com/owncloud/news/issues)
- [Notes](https://github.com/owncloud/notes/issues)
- [Shorty](https://github.com/owncloud/shorty/issues)
- [Tasks](https://github.com/owncloud/tasks/issues)
- [All other apps](https://github.com/owncloud/apps/issues)
- [ownCloud apps](https://github.com/owncloud/core/wiki/Maintainers#apps-repo)
* Report the issue using our [template][template], it includes all the information we need to track down the issue.
Help us to maximize the effort we can spend fixing issues and adding new features, by not reporting duplicate issues.

View File

@ -27,10 +27,14 @@ https://doc.owncloud.org/server/9.0/developer_manual/app/index.html
## Contribution Guidelines
https://owncloud.org/contribute/
## Support
Learn about the diffrent ways you can get support for ownCloud: https://owncloud.org/support/
## Get in touch
* :clipboard: [Forum](https://forum.owncloud.org)
* :envelope: [Mailing list](https://mailman.owncloud.org/mailman/listinfo)
* :busts_in_silhouette: [IRC channel](https://webchat.freenode.net/?channels=owncloud)
* :hash: [IRC channel](https://webchat.freenode.net/?channels=owncloud)
* :busts_in_silhouette: [Facebook] (https://facebook.com/ownclouders)
* :hatching_chick: [Twitter](https://twitter.com/ownClouders)
## Important notice on translations

42
apps/dav/appinfo/app.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
$cm = \OC::$server->getContactsManager();
$cm->register(function() use ($cm) {
$db = \OC::$server->getDatabaseConnection();
$userId = \OC::$server->getUserSession()->getUser()->getUID();
$principal = new \OCA\DAV\Connector\Sabre\Principal(
\OC::$server->getConfig(),
\OC::$server->getUserManager()
);
$cardDav = new \OCA\DAV\CardDAV\CardDavBackend($db, $principal, \OC::$server->getLogger());
$addressBooks = $cardDav->getAddressBooksForUser("principals/$userId");
foreach ($addressBooks as $addressBookInfo) {
$addressBook = new \OCA\DAV\CardDAV\AddressBook($cardDav, $addressBookInfo);
$cm->registerAddressBook(
new OCA\DAV\CardDAV\AddressBookImpl(
$addressBook,
$addressBookInfo,
$cardDav
)
);
}
});

View File

@ -571,6 +571,78 @@ CREATE TABLE calendarobjects (
</declaration>
</table>
<table>
<name>*dbprefix*cards_properties</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<unsigned>true</unsigned>
<length>11</length>
</field>
<field>
<name>addressbookid</name>
<type>integer</type>
<default></default>
<notnull>true</notnull>
<length>11</length>
</field>
<field>
<name>cardid</name>
<type>integer</type>
<default></default>
<notnull>true</notnull>
<unsigned>true</unsigned>
<length>11</length>
</field>
<field>
<name>name</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>64</length>
</field>
<field>
<name>value</name>
<type>text</type>
<default></default>
<notnull>false</notnull>
<length>255</length>
</field>
<field>
<name>preferred</name>
<type>integer</type>
<default>1</default>
<notnull>true</notnull>
<length>4</length>
</field>
<index>
<name>card_contactid_index</name>
<field>
<name>cardid</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>card_name_index</name>
<field>
<name>name</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>card_value_index</name>
<field>
<name>value</name>
<sorting>ascending</sorting>
</field>
</index>
</declaration>
</table>
<table>
<name>*dbprefix*dav_shares</name>
<declaration>

View File

@ -8,8 +8,9 @@ $config = \OC::$server->getConfig();
$dbConnection = \OC::$server->getDatabaseConnection();
$userManager = OC::$server->getUserManager();
$config = \OC::$server->getConfig();
$logger = \OC::$server->getLogger();
/** @var Symfony\Component\Console\Application $application */
$application->add(new CreateAddressBook($userManager, $dbConnection, $config));
$application->add(new CreateAddressBook($userManager, $dbConnection, $config, $logger));
$application->add(new CreateCalendar($userManager, $dbConnection));
$application->add(new SyncSystemAddressBook($userManager, $dbConnection, $config));

View File

@ -6,6 +6,7 @@ use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\ILogger;
use OCP\IUserManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@ -23,15 +24,25 @@ class CreateAddressBook extends Command {
/** @var IConfig */
private $config;
/** @var ILogger */
private $logger;
/**
* @param IUserManager $userManager
* @param IDBConnection $dbConnection
* @param IConfig $config
* @param ILogger $logger
*/
function __construct(IUserManager $userManager, IDBConnection $dbConnection, IConfig $config) {
function __construct(IUserManager $userManager,
IDBConnection $dbConnection,
IConfig $config,
ILogger $logger
) {
parent::__construct();
$this->userManager = $userManager;
$this->dbConnection = $dbConnection;
$this->config = $config;
$this->logger = $logger;
}
protected function configure() {

View File

@ -0,0 +1,219 @@
<?php
/**
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Björn Schießle <schiessle@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\DAV\CardDAV;
use OCP\Constants;
use OCP\IAddressBook;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property\Text;
use Sabre\VObject\Reader;
use Sabre\VObject\UUIDUtil;
class AddressBookImpl implements IAddressBook {
/** @var CardDavBackend */
private $backend;
/** @var array */
private $addressBookInfo;
/** @var AddressBook */
private $addressBook;
/**
* AddressBookImpl constructor.
*
* @param AddressBook $addressBook
* @param array $addressBookInfo
* @param CardDavBackend $backend
*/
public function __construct(
AddressBook $addressBook,
array $addressBookInfo,
CardDavBackend $backend) {
$this->addressBook = $addressBook;
$this->addressBookInfo = $addressBookInfo;
$this->backend = $backend;
}
/**
* @return string defining the technical unique key
* @since 5.0.0
*/
public function getKey() {
return $this->addressBookInfo['id'];
}
/**
* In comparison to getKey() this function returns a human readable (maybe translated) name
*
* @return mixed
* @since 5.0.0
*/
public function getDisplayName() {
return $this->addressBookInfo['{DAV:}displayname'];
}
/**
* @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!
* @return array an array of contacts which are arrays of key-value-pairs
* @since 5.0.0
*/
public function search($pattern, $searchProperties, $options) {
$result = $this->backend->search($this->getKey(), $pattern, $searchProperties);
$vCards = [];
foreach ($result as $cardData) {
$vCards[] = $this->vCard2Array($this->readCard($cardData));
}
return $vCards;
}
/**
* @param array $properties this array if key-value-pairs defines a contact
* @return array an array representing the contact just created or updated
* @since 5.0.0
*/
public function createOrUpdate($properties) {
$update = false;
if (!isset($properties['UID'])) { // create a new contact
$uid = $this->createUid();
$uri = $uid . '.vcf';
$vCard = $this->createEmptyVCard($uid);
} else { // update existing contact
$uid = $properties['UID'];
$uri = $uid . '.vcf';
$vCardData = $this->backend->getCard($this->getKey(), $uri);
$vCard = $this->readCard($vCardData['carddata']);
$update = true;
}
foreach ($properties as $key => $value) {
$vCard->$key = $vCard->createProperty($key, $value);
}
if ($update) {
$this->backend->updateCard($this->getKey(), $uri, $vCard->serialize());
} else {
$this->backend->createCard($this->getKey(), $uri, $vCard->serialize());
}
return $this->vCard2Array($vCard);
}
/**
* @return mixed
* @since 5.0.0
*/
public function getPermissions() {
$permissions = $this->addressBook->getACL();
$result = 0;
foreach ($permissions as $permission) {
switch($permission['privilege']) {
case '{DAV:}read':
$result |= Constants::PERMISSION_READ;
break;
case '{DAV:}write':
$result |= Constants::PERMISSION_CREATE;
$result |= Constants::PERMISSION_UPDATE;
break;
case '{DAV:}all':
$result |= Constants::PERMISSION_ALL;
break;
}
}
return $result;
}
/**
* @param object $id the unique identifier to a contact
* @return bool successful or not
* @since 5.0.0
*/
public function delete($id) {
$uri = $this->backend->getCardUri($id);
return $this->backend->deleteCard($this->addressBookInfo['id'], $uri);
}
/**
* read vCard data into a vCard object
*
* @param string $cardData
* @return VCard
*/
protected function readCard($cardData) {
return Reader::read($cardData);
}
/**
* create UID for contact
*
* @return string
*/
protected function createUid() {
do {
$uid = $this->getUid();
} while (!empty($this->backend->getContact($uid . '.vcf')));
return $uid;
}
/**
* getUid is only there for testing, use createUid instead
*/
protected function getUid() {
return UUIDUtil::getUUID();
}
/**
* create empty vcard
*
* @param string $uid
* @return VCard
*/
protected function createEmptyVCard($uid) {
$vCard = new VCard();
$vCard->add(new Text($vCard, 'UID', $uid));
return $vCard;
}
/**
* create array with all vCard properties
*
* @param VCard $vCard
* @return array
*/
protected function vCard2Array(VCard $vCard) {
$result = [];
foreach ($vCard->children as $property) {
$result[$property->name] = $property->getValue();
}
return $result;
}
}

View File

@ -23,19 +23,48 @@
namespace OCA\DAV\CardDAV;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\IDBConnection;
use OCP\ILogger;
use Sabre\CardDAV\Backend\BackendInterface;
use Sabre\CardDAV\Backend\SyncSupport;
use Sabre\CardDAV\Plugin;
use Sabre\DAV\Exception\BadRequest;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Reader;
class CardDavBackend implements BackendInterface, SyncSupport {
/** @var Principal */
private $principalBackend;
public function __construct(\OCP\IDBConnection $db, Principal $principalBackend) {
/** @var ILogger */
private $logger;
/** @var string */
private $dbCardsTable = 'cards';
/** @var string */
private $dbCardsPropertiesTable = 'cards_properties';
/** @var IDBConnection */
private $db;
/** @var array properties to index */
public static $indexProperties = array(
'BDAY', 'UID', 'N', 'FN', 'TITLE', 'ROLE', 'NOTE', 'NICKNAME',
'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO', 'CLOUD');
/**
* CardDavBackend constructor.
*
* @param IDBConnection $db
* @param Principal $principalBackend
* @param ILogger $logger
*/
public function __construct(IDBConnection $db, Principal $principalBackend, ILogger $logger) {
$this->db = $db;
$this->principalBackend = $principalBackend;
$this->logger = $logger;
}
/**
@ -263,6 +292,11 @@ class CardDavBackend implements BackendInterface, SyncSupport {
->where($query->expr()->eq('resourceid', $query->createNamedParameter($addressBookId)))
->andWhere($query->expr()->eq('type', $query->createNamedParameter('addressbook')))
->execute();
$query->delete($this->dbCardsPropertiesTable)
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
->execute();
}
/**
@ -398,7 +432,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$query = $this->db->getQueryBuilder();
$query->insert('cards')
->values([
'carddata' => $query->createNamedParameter($cardData),
'carddata' => $query->createNamedParameter($cardData, \PDO::PARAM_LOB),
'uri' => $query->createNamedParameter($cardUri),
'lastmodified' => $query->createNamedParameter(time()),
'addressbookid' => $query->createNamedParameter($addressBookId),
@ -408,6 +442,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
->execute();
$this->addChange($addressBookId, $cardUri, 1);
$this->updateProperties($addressBookId, $cardUri, $cardData);
return '"' . $etag . '"';
}
@ -451,6 +486,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
->execute();
$this->addChange($addressBookId, $cardUri, 2);
$this->updateProperties($addressBookId, $cardUri, $cardData);
return '"' . $etag . '"';
}
@ -463,6 +499,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
* @return bool
*/
function deleteCard($addressBookId, $cardUri) {
$cardId = $this->getCardId($cardUri);
$query = $this->db->getQueryBuilder();
$ret = $query->delete('cards')
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
@ -471,7 +508,12 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$this->addChange($addressBookId, $cardUri, 3);
return $ret === 1;
if ($ret === 1) {
$this->purgeProperties($addressBookId, $cardId);
return true;
}
return false;
}
/**
@ -637,6 +679,87 @@ class CardDavBackend implements BackendInterface, SyncSupport {
}
}
/**
* search contact
*
* @param int $addressBookId
* @param string $pattern which should match within the $searchProperties
* @param array $searchProperties defines the properties within the query pattern should match
* @return array an array of contacts which are arrays of key-value-pairs
*/
public function search($addressBookId, $pattern, $searchProperties) {
$query = $this->db->getQueryBuilder();
$query2 = $this->db->getQueryBuilder();
$query2->selectDistinct('cp.cardid')->from($this->dbCardsPropertiesTable, 'cp');
foreach ($searchProperties as $property) {
$query2->orWhere(
$query2->expr()->andX(
$query2->expr()->eq('cp.name', $query->createNamedParameter($property)),
$query2->expr()->like('cp.value', $query->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%'))
)
);
}
$query2->andWhere($query2->expr()->eq('cp.addressbookid', $query->createNamedParameter($addressBookId)));
$query->select('c.carddata')->from($this->dbCardsTable, 'c')
->where($query->expr()->in('c.id', $query->createFunction($query2->getSQL())));
$result = $query->execute();
$cards = $result->fetchAll();
$result->closeCursor();
return array_map(function($array) {return $this->readBlob($array['carddata']);}, $cards);
}
/**
* get URI from a given contact
*
* @param int $id
* @return string
*/
public function getCardUri($id) {
$query = $this->db->getQueryBuilder();
$query->select('uri')->from($this->dbCardsTable)
->where($query->expr()->eq('id', $query->createParameter('id')))
->setParameter('id', $id);
$result = $query->execute();
$uri = $result->fetch();
$result->closeCursor();
if (!isset($uri['uri'])) {
throw new \InvalidArgumentException('Card does not exists: ' . $id);
}
return $uri['uri'];
}
/**
* return contact with the given URI
*
* @param string $uri
* @returns array
*/
public function getContact($uri) {
$result = [];
$query = $this->db->getQueryBuilder();
$query->select('*')->from($this->dbCardsTable)
->where($query->expr()->eq('uri', $query->createParameter('uri')))
->setParameter('uri', $uri);
$queryResult = $query->execute();
$contact = $queryResult->fetch();
$queryResult->closeCursor();
if (is_array($contact)) {
$result = $contact;
}
return $result;
}
/**
* @param string $addressBookUri
* @param string $element
@ -658,7 +781,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
}
// remove the share if it already exists
$this->unshare($addressBookUri, $element);
$this->unshare($addressBookUri, $element['href']);
$query = $this->db->getQueryBuilder();
$query->insert('dav_shares')
@ -677,8 +800,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
* @param string $element
*/
private function unshare($addressBookUri, $element) {
$user = $element['href'];
$parts = explode(':', $user, 2);
$parts = explode(':', $element, 2);
if ($parts[0] !== 'principal') {
return;
}
@ -734,4 +856,93 @@ class CardDavBackend implements BackendInterface, SyncSupport {
return $shares;
}
/**
* update properties table
*
* @param int $addressBookId
* @param string $cardUri
* @param string $vCardSerialized
*/
protected function updateProperties($addressBookId, $cardUri, $vCardSerialized) {
$cardId = $this->getCardId($cardUri);
$vCard = $this->readCard($vCardSerialized);
$this->purgeProperties($addressBookId, $cardId);
$query = $this->db->getQueryBuilder();
$query->insert($this->dbCardsPropertiesTable)
->values(
[
'addressbookid' => $query->createNamedParameter($addressBookId),
'cardid' => $query->createNamedParameter($cardId),
'name' => $query->createParameter('name'),
'value' => $query->createParameter('value'),
'preferred' => $query->createParameter('preferred')
]
);
foreach ($vCard->children as $property) {
if(!in_array($property->name, self::$indexProperties)) {
continue;
}
$preferred = 0;
foreach($property->parameters as $parameter) {
if ($parameter->name == 'TYPE' && strtoupper($parameter->getValue()) == 'PREF') {
$preferred = 1;
break;
}
}
$query->setParameter('name', $property->name);
$query->setParameter('value', substr($property->getValue(), 0, 254));
$query->setParameter('preferred', $preferred);
$query->execute();
}
}
/**
* read vCard data into a vCard object
*
* @param string $cardData
* @return VCard
*/
protected function readCard($cardData) {
return Reader::read($cardData);
}
/**
* delete all properties from a given card
*
* @param int $addressBookId
* @param int $cardId
*/
protected function purgeProperties($addressBookId, $cardId) {
$query = $this->db->getQueryBuilder();
$query->delete($this->dbCardsPropertiesTable)
->where($query->expr()->eq('cardid', $query->createNamedParameter($cardId)))
->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)));
$query->execute();
}
/**
* get ID from a given contact
*
* @param string $uri
* @return int
*/
protected function getCardId($uri) {
$query = $this->db->getQueryBuilder();
$query->select('id')->from($this->dbCardsTable)
->where($query->expr()->eq('uri', $query->createNamedParameter($uri)));
$result = $query->execute();
$cardIds = $result->fetch();
$result->closeCursor();
if (!isset($cardIds['id'])) {
throw new \InvalidArgumentException('Card does not exists: ' . $uri);
}
return (int)$cardIds['id'];
}
}

View File

@ -9,11 +9,24 @@ use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
use Sabre\DAV\XMLUtil;
use Sabre\DAVACL\IACL;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
class Plugin extends ServerPlugin {
/** @var Auth */
private $auth;
/** @var IRequest */
private $request;
/**
* Plugin constructor.
*
* @param Auth $authBackEnd
* @param IRequest $request
*/
public function __construct(Auth $authBackEnd, IRequest $request) {
$this->auth = $authBackEnd;
$this->request = $request;
@ -68,6 +81,7 @@ class Plugin extends ServerPlugin {
function initialize(Server $server) {
$this->server = $server;
$server->resourceTypeMapping['OCA\\DAV\CardDAV\\ISharedAddressbook'] = '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}shared';
$this->server->xml->elementMap['{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}share'] = 'OCA\\DAV\\CardDAV\\Sharing\\Xml\\ShareRequest';
$this->server->on('method:POST', [$this, 'httpPost']);
}
@ -109,9 +123,7 @@ class Plugin extends ServerPlugin {
// re-populated the request body with the existing data.
$request->setBody($requestBody);
$dom = XMLUtil::loadDOMDocument($requestBody);
$documentType = XMLUtil::toClarkNotation($dom->firstChild);
$message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType);
switch ($documentType) {
@ -124,19 +136,18 @@ class Plugin extends ServerPlugin {
return;
}
$this->server->transactionType = 'post-calendar-share';
$this->server->transactionType = 'post-oc-addressbook-share';
// Getting ACL info
$acl = $this->server->getPlugin('acl');
// If there's no ACL support, we allow everything
if ($acl) {
/** @var \Sabre\DAVACL\Plugin $acl */
$acl->checkPrivileges($path, '{DAV:}write');
}
$mutations = $this->parseShareRequest($dom);
$node->updateShares($mutations[0], $mutations[1]);
$node->updateShares($message->set, $message->remove);
$response->setStatus(200);
// Adding this because sending a response body may cause issues,
@ -148,59 +159,6 @@ class Plugin extends ServerPlugin {
}
}
/**
* Parses the 'share' POST request.
*
* This method returns an array, containing two arrays.
* The first array is a list of new sharees. Every element is a struct
* containing a:
* * href element. (usually a mailto: address)
* * commonName element (often a first and lastname, but can also be
* false)
* * readOnly (true or false)
* * summary (A description of the share, can also be false)
*
* The second array is a list of sharees that are to be removed. This is
* just a simple array with 'hrefs'.
*
* @param \DOMDocument $dom
* @return array
*/
function parseShareRequest(\DOMDocument $dom) {
$xpath = new \DOMXPath($dom);
$xpath->registerNamespace('cs', \Sabre\CardDAV\Plugin::NS_CARDDAV);
$xpath->registerNamespace('d', 'urn:DAV');
$set = [];
$elems = $xpath->query('cs:set');
for ($i = 0; $i < $elems->length; $i++) {
$xset = $elems->item($i);
$set[] = [
'href' => $xpath->evaluate('string(d:href)', $xset),
'commonName' => $xpath->evaluate('string(cs:common-name)', $xset),
'summary' => $xpath->evaluate('string(cs:summary)', $xset),
'readOnly' => $xpath->evaluate('boolean(cs:read)', $xset) !== false
];
}
$remove = [];
$elems = $xpath->query('cs:remove');
for ($i = 0; $i < $elems->length; $i++) {
$xremove = $elems->item($i);
$remove[] = $xpath->evaluate('string(d:href)', $xremove);
}
return [$set, $remove];
}
private function protectAgainstCSRF() {
$user = $this->auth->getCurrentUser();
if ($this->auth->isDavAuthenticated($user)) {

View File

@ -0,0 +1,65 @@
<?php
namespace OCA\DAV\CardDAV\Sharing\Xml;
use Sabre\Xml\Reader;
use Sabre\Xml\XmlDeserializable;
class ShareRequest implements XmlDeserializable {
public $set = [];
public $remove = [];
/**
* Constructor
*
* @param array $set
* @param array $remove
*/
function __construct(array $set, array $remove) {
$this->set = $set;
$this->remove = $remove;
}
static function xmlDeserialize(Reader $reader) {
$elems = $reader->parseInnerTree([
'{' . \Sabre\CardDAV\Plugin::NS_CARDDAV. '}set' => 'Sabre\\Xml\\Element\\KeyValue',
'{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}remove' => 'Sabre\\Xml\\Element\\KeyValue',
]);
$set = [];
$remove = [];
foreach ($elems as $elem) {
switch ($elem['name']) {
case '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}set' :
$sharee = $elem['value'];
$sumElem = '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}summary';
$commonName = '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}common-name';
$set[] = [
'href' => $sharee['{DAV:}href'],
'commonName' => isset($sharee[$commonName]) ? $sharee[$commonName] : null,
'summary' => isset($sharee[$sumElem]) ? $sharee[$sumElem] : null,
'readOnly' => !array_key_exists('{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}read-write', $sharee),
];
break;
case '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}remove' :
$remove[] = $elem['value']['{DAV:}href'];
break;
}
}
return new self($set, $remove);
}
}

View File

@ -160,7 +160,7 @@ class Auth extends AbstractBasic {
return [true, $this->principalPrefix . $user];
}
if (!$this->userSession->isLoggedIn() && $request->getHeader('X-Requested-With') === 'XMLHttpRequest') {
if (!$this->userSession->isLoggedIn() && in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With')))) {
// do not re-authenticate over ajax, use dummy auth name to prevent browser popup
$response->addHeader('WWW-Authenticate','DummyBasic realm="' . $this->realm . '"');
$response->setStatus(401);

View File

@ -329,7 +329,7 @@ class File extends Node implements IFile {
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') {
return $mimeType;
}
return \OC_Helper::getSecureMimeType($mimeType);
return \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType);
}
/**

View File

@ -31,6 +31,7 @@ use \Sabre\DAV\PropFind;
use \Sabre\DAV\PropPatch;
use \Sabre\HTTP\RequestInterface;
use \Sabre\HTTP\ResponseInterface;
use OCP\Files\StorageNotAvailableException;
class FilesPlugin extends \Sabre\DAV\ServerPlugin {
@ -225,9 +226,13 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
$propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) {
/** @var $node \OCA\DAV\Connector\Sabre\File */
$directDownloadUrl = $node->getDirectDownload();
if (isset($directDownloadUrl['url'])) {
return $directDownloadUrl['url'];
try {
$directDownloadUrl = $node->getDirectDownload();
if (isset($directDownloadUrl['url'])) {
return $directDownloadUrl['url'];
}
} catch (StorageNotAvailableException $e) {
return false;
}
return false;
});

View File

@ -190,7 +190,7 @@ class ObjectTree extends \Sabre\DAV\Tree {
$targetNodeExists = $this->nodeExists($destinationPath);
$sourceNode = $this->getNodeForPath($sourcePath);
if ($sourceNode instanceof \Sabre\DAV\ICollection && $targetNodeExists) {
throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode . ', target exists');
throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists');
}
list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($sourcePath);
list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destinationPath);

View File

@ -41,11 +41,11 @@ class RootCollection extends SimpleCollection {
\OC::$server->getSystemTagObjectMapper()
);
$usersCardDavBackend = new CardDavBackend($db, $principalBackend);
$usersCardDavBackend = new CardDavBackend($db, $principalBackend, \OC::$server->getLogger());
$usersAddressBookRoot = new AddressBookRoot($principalBackend, $usersCardDavBackend, 'principals/users');
$usersAddressBookRoot->disableListing = $disableListing;
$systemCardDavBackend = new CardDavBackend($db, $principalBackend);
$systemCardDavBackend = new CardDavBackend($db, $principalBackend, \OC::$server->getLogger());
$systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, 'principals/system');
$systemAddressBookRoot->disableListing = $disableListing;

View File

@ -46,14 +46,25 @@ class SystemTagsByIdCollection implements ICollection {
$this->tagManager = $tagManager;
}
/**
* @param string $name
* @param resource|string $data Initial payload
* @throws Forbidden
*/
function createFile($name, $data = null) {
throw new Forbidden('Cannot create tags by id');
}
/**
* @param string $name
*/
function createDirectory($name) {
throw new Forbidden('Permission denied to create collections');
}
/**
* @param string $name
*/
function getChild($name) {
try {
$tags = $this->tagManager->getTagsByIds([$name]);
@ -72,6 +83,9 @@ class SystemTagsByIdCollection implements ICollection {
}, $tags);
}
/**
* @param string $name
*/
function childExists($name) {
try {
$this->tagManager->getTagsByIds([$name]);

View File

@ -61,14 +61,25 @@ class SystemTagsObjectTypeCollection implements ICollection {
$this->objectType = $objectType;
}
/**
* @param string $name
* @param resource|string $data Initial payload
* @throws Forbidden
*/
function createFile($name, $data = null) {
throw new Forbidden('Permission denied to create nodes');
}
/**
* @param string $name
*/
function createDirectory($name) {
throw new Forbidden('Permission denied to create collections');
}
/**
* @param string $objectId
*/
function getChild($objectId) {
return new SystemTagsObjectMappingCollection(
$objectId,
@ -83,6 +94,9 @@ class SystemTagsObjectTypeCollection implements ICollection {
throw new MethodNotAllowed();
}
/**
* @param string $name
*/
function childExists($name) {
return true;
}
@ -95,6 +109,9 @@ class SystemTagsObjectTypeCollection implements ICollection {
return $this->objectType;
}
/**
* @param string $name
*/
function setName($name) {
throw new Forbidden('Permission denied to rename this collection');
}

View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
if [ ! -f CalDAVTester/run.py ]; then
cd "$SCRIPTPATH"
git clone https://github.com/DeepDiver1975/CalDAVTester.git
cd "$SCRIPTPATH/CalDAVTester"
python run.py -s
cd "$SCRIPTPATH"
fi
# create test user
cd "$SCRIPTPATH/../../../../../"
OC_PASS=user01 php occ user:add --password-from-env user01
php occ dav:create-calendar user01 calendar
OC_PASS=user02 php occ user:add --password-from-env user02
php occ dav:create-calendar user02 calendar
cd "$SCRIPTPATH/../../../../../"

View File

@ -0,0 +1,18 @@
#!/usr/bin/env bash
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
# start the server
php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../../.." &
sleep 30
# run the tests
cd "$SCRIPTPATH/CalDAVTester"
PYTHONPATH="$SCRIPTPATH/pycalendar/src" python testcaldav.py --print-details-onfail -s "$SCRIPTPATH/../caldavtest/config/serverinfo.xml" -o cdt.txt \
"$SCRIPTPATH/../caldavtest/tests/CalDAV/current-user-principal.xml"
RESULT=$?
tail "$/../../../../../data-autotest/owncloud.log"
exit $RESULT

View File

@ -0,0 +1,151 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE caldavtest SYSTEM "caldavtest.dtd">
<!--
Copyright (c) 2006-2015 Apple Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<caldavtest>
<description>Test DAV:current-user-principal support</description>
<require-feature>
<feature>caldav</feature>
<feature>current-user-principal</feature>
</require-feature>
<start/>
<test-suite name='Check for the property on /'>
<require-feature>
<feature>own-root</feature>
</require-feature>
<test name='1'>
<description>Check for authenticated property on /</description>
<request>
<method>PROPFIND</method>
<ruri>$root:</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/current-user-principal/1.xml</filepath>
</data>
<verify>
<callback>propfindItems</callback>
<arg>
<name>okprops</name>
<value><![CDATA[{DAV:}current-user-principal$<href xmlns="DAV:">$principaluri1:</href>]]></value>
</arg>
</verify>
</request>
</test>
<test name='3'>
<description>Check for authenticated property on / (user02)</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PROPFIND</method>
<ruri>$root:</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/current-user-principal/1.xml</filepath>
</data>
<verify>
<callback>propfindItems</callback>
<arg>
<name>okprops</name>
<value><![CDATA[{DAV:}current-user-principal$<href xmlns="DAV:">$principaluri2:</href>]]></value>
</arg>
</verify>
</request>
</test>
</test-suite>
<test-suite name='Check for the property on /principals/'>
<test name='1'>
<description>Check for authenticated property on /</description>
<request>
<method>PROPFIND</method>
<ruri>$principalcollection:</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/current-user-principal/1.xml</filepath>
</data>
<verify>
<callback>propfindItems</callback>
<arg>
<name>okprops</name>
<value><![CDATA[{DAV:}current-user-principal$<href xmlns="DAV:">$principaluri1:</href>]]></value>
</arg>
</verify>
</request>
</test>
<test name='2'>
<description>Check for unauthenticated property on /</description>
<request auth="no">
<method>PROPFIND</method>
<ruri>$principals_users:</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/current-user-principal/1.xml</filepath>
</data>
<verify>
<callback>statusCode</callback>
<arg>
<name>status</name>
<value>401</value>
</arg>
</verify>
</request>
</test>
<test name='3'>
<description>Check for authenticated property on / (user02)</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PROPFIND</method>
<ruri>$principalcollection:</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/current-user-principal/1.xml</filepath>
</data>
<verify>
<callback>propfindItems</callback>
<arg>
<name>okprops</name>
<value><![CDATA[{DAV:}current-user-principal$<href xmlns="DAV:">$principaluri2:</href>]]></value>
</arg>
</verify>
</request>
</test>
</test-suite>
<end/>
</caldavtest>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
if [ ! -f CalDAVTester/run.py ]; then
cd "$SCRIPTPATH"
git clone https://github.com/DeepDiver1975/CalDAVTester.git
cd "$SCRIPTPATH/CalDAVTester"
python run.py -s
cd "$SCRIPTPATH"
fi
# create test user
cd "$SCRIPTPATH/../../../../../"
OC_PASS=user01 php occ user:add --password-from-env user01
php occ dav:create-addressbook user01 addressbook
OC_PASS=user02 php occ user:add --password-from-env user02
php occ dav:create-addressbook user02 addressbook
cd "$SCRIPTPATH/../../../../../"

View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
# start the server
php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../../.." &
sleep 30
# run the tests
cd "$SCRIPTPATH/CalDAVTester"
PYTHONPATH="$SCRIPTPATH/pycalendar/src" python testcaldav.py --print-details-onfail -s "$SCRIPTPATH/../caldavtest/config/serverinfo.xml" -o cdt.txt \
"$SCRIPTPATH/../caldavtest/tests/CardDAV/current-user-principal.xml" \
"$SCRIPTPATH/../caldavtest/tests/CardDAV/sync-report.xml"
RESULT=$?
tail "$/../../../../../data-autotest/owncloud.log"
exit $RESULT

View File

@ -1,35 +0,0 @@
#!/usr/bin/env bash
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
# start the server
php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../.." &
if [ ! -f CalDAVTester/run.py ]; then
cd "$SCRIPTPATH"
git clone https://github.com/DeepDiver1975/CalDAVTester.git
cd "$SCRIPTPATH/CalDAVTester"
python run.py -s
cd "$SCRIPTPATH"
fi
# create test user
cd "$SCRIPTPATH/../../../../"
OC_PASS=user01 php occ user:add --password-from-env user01
php occ dav:create-addressbook user01 addressbook
OC_PASS=user02 php occ user:add --password-from-env user02
php occ dav:create-addressbook user02 addressbook
cd "$SCRIPTPATH/../../../../"
# run the tests
cd "$SCRIPTPATH/CalDAVTester"
PYTHONPATH="$SCRIPTPATH/pycalendar/src" python testcaldav.py --print-details-onfail -s "$SCRIPTPATH/caldavtest/config/serverinfo.xml" -o cdt.txt \
"$SCRIPTPATH/caldavtest/tests/CardDAV/current-user-principal.xml" \
"$SCRIPTPATH/caldavtest/tests/CardDAV/sync-report.xml"
RESULT=$?
tail "$SCRIPTPATH/../../../../data-autotest/owncloud.log"
exit $RESULT

View File

@ -1,11 +1,4 @@
#!/usr/bin/env bash
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
# start the server
php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../.." &
# compile litmus
if [ ! -f /tmp/litmus/litmus-0.13.tar.gz ]; then
@ -17,7 +10,3 @@ if [ ! -f /tmp/litmus/litmus-0.13.tar.gz ]; then
./configure
make
fi
# run the tests
cd /tmp/litmus/litmus-0.13
make URL=http://127.0.0.1:8888/remote.php/webdav CREDS="admin admin" TESTS="basic copymove props locks" check

View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
# start the server
php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../../.." &
sleep 30
# run the tests
cd /tmp/litmus/litmus-0.13
make URL=http://127.0.0.1:8888/remote.php/webdav CREDS="admin admin" TESTS="basic copymove props locks" check

View File

@ -1,11 +1,4 @@
#!/usr/bin/env bash
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
# start the server
php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../.." &
# compile litmus
if [ ! -f /tmp/litmus/litmus-0.13.tar.gz ]; then
@ -17,7 +10,3 @@ if [ ! -f /tmp/litmus/litmus-0.13.tar.gz ]; then
./configure
make
fi
# run the tests
cd /tmp/litmus/litmus-0.13
make URL=http://127.0.0.1:8888/remote.php/dav/files/admin CREDS="admin admin" TESTS="basic copymove props locks" check

View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
# start the server
php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../../.." &
sleep 30
# run the tests
cd /tmp/litmus/litmus-0.13
make URL=http://127.0.0.1:8888/remote.php/dav/files/admin CREDS="admin admin" TESTS="basic copymove props locks" check

View File

@ -0,0 +1,287 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\DAV\Tests\Unit\CardDAV;
use OCA\DAV\CardDAV\AddressBook;
use OCA\DAV\CardDAV\AddressBookImpl;
use OCA\DAV\CardDAV\CardDavBackend;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property\Text;
use Test\TestCase;
class AddressBookImplTest extends TestCase {
/** @var AddressBookImpl */
private $addressBookImpl;
/** @var array */
private $addressBookInfo;
/** @var AddressBook | \PHPUnit_Framework_MockObject_MockObject */
private $addressBook;
/** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject */
private $backend;
/** @var VCard | \PHPUnit_Framework_MockObject_MockObject */
private $vCard;
public function setUp() {
parent::setUp();
$this->addressBookInfo = [
'id' => 42,
'{DAV:}displayname' => 'display name'
];
$this->addressBook = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBook')
->disableOriginalConstructor()->getMock();
$this->backend = $this->getMockBuilder('\OCA\DAV\CardDAV\CardDavBackend')
->disableOriginalConstructor()->getMock();
$this->vCard = $this->getMock('Sabre\VObject\Component\VCard');
$this->addressBookImpl = new AddressBookImpl(
$this->addressBook,
$this->addressBookInfo,
$this->backend
);
}
public function testGetKey() {
$this->assertSame($this->addressBookInfo['id'],
$this->addressBookImpl->getKey());
}
public function testGetDisplayName() {
$this->assertSame($this->addressBookInfo['{DAV:}displayname'],
$this->addressBookImpl->getDisplayName());
}
public function testSearch() {
/** @var \PHPUnit_Framework_MockObject_MockObject | AddressBookImpl $addressBookImpl */
$addressBookImpl = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBookImpl')
->setConstructorArgs(
[
$this->addressBook,
$this->addressBookInfo,
$this->backend
]
)
->setMethods(['vCard2Array', 'readCard'])
->getMock();
$pattern = 'pattern';
$searchProperties = 'properties';
$this->backend->expects($this->once())->method('search')
->with($this->addressBookInfo['id'], $pattern, $searchProperties)
->willReturn(
[
'cardData1',
'cardData2'
]
);
$addressBookImpl->expects($this->exactly(2))->method('readCard')
->willReturn($this->vCard);
$addressBookImpl->expects($this->exactly(2))->method('vCard2Array')
->with($this->vCard)->willReturn('vCard');
$result = $addressBookImpl->search($pattern, $searchProperties, []);
$this->assertTrue((is_array($result)));
$this->assertSame(2, count($result));
}
/**
* @dataProvider dataTestCreate
*
* @param array $properties
*/
public function testCreate($properties) {
$uid = 'uid';
/** @var \PHPUnit_Framework_MockObject_MockObject | AddressBookImpl $addressBookImpl */
$addressBookImpl = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBookImpl')
->setConstructorArgs(
[
$this->addressBook,
$this->addressBookInfo,
$this->backend
]
)
->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard'])
->getMock();
$addressBookImpl->expects($this->once())->method('createUid')
->willReturn($uid);
$addressBookImpl->expects($this->once())->method('createEmptyVCard')
->with($uid)->willReturn($this->vCard);
$this->vCard->expects($this->exactly(count($properties)))
->method('createProperty');
$this->backend->expects($this->once())->method('createCard');
$this->backend->expects($this->never())->method('updateCard');
$this->backend->expects($this->never())->method('getCard');
$addressBookImpl->expects($this->once())->method('vCard2Array')
->with($this->vCard)->willReturn(true);
$this->assertTrue($addressBookImpl->createOrUpdate($properties));
}
public function dataTestCreate() {
return [
[[]],
[['FN' => 'John Doe']]
];
}
public function testUpdate() {
$uid = 'uid';
$properties = ['UID' => $uid, 'FN' => 'John Doe'];
/** @var \PHPUnit_Framework_MockObject_MockObject | AddressBookImpl $addressBookImpl */
$addressBookImpl = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBookImpl')
->setConstructorArgs(
[
$this->addressBook,
$this->addressBookInfo,
$this->backend
]
)
->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard', 'readCard'])
->getMock();
$addressBookImpl->expects($this->never())->method('createUid');
$addressBookImpl->expects($this->never())->method('createEmptyVCard');
$this->backend->expects($this->once())->method('getCard')
->with($this->addressBookInfo['id'], $uid . '.vcf')
->willReturn(['carddata' => 'data']);
$addressBookImpl->expects($this->once())->method('readCard')
->with('data')->willReturn($this->vCard);
$this->vCard->expects($this->exactly(count($properties)))
->method('createProperty');
$this->backend->expects($this->never())->method('createCard');
$this->backend->expects($this->once())->method('updateCard');
$addressBookImpl->expects($this->once())->method('vCard2Array')
->with($this->vCard)->willReturn(true);
$this->assertTrue($addressBookImpl->createOrUpdate($properties));
}
/**
* @dataProvider dataTestGetPermissions
*
* @param array $permissions
* @param int $expected
*/
public function testGetPermissions($permissions, $expected) {
$this->addressBook->expects($this->once())->method('getACL')
->willReturn($permissions);
$this->assertSame($expected,
$this->addressBookImpl->getPermissions()
);
}
public function dataTestGetPermissions() {
return [
[[], 0],
[[['privilege' => '{DAV:}read']], 1],
[[['privilege' => '{DAV:}write']], 6],
[[['privilege' => '{DAV:}all']], 31],
[[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 7],
[[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}all']], 31],
[[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}write']], 31],
[[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write'],['privilege' => '{DAV:}all']], 31],
[[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 31],
];
}
public function testDelete() {
$cardId = 1;
$cardUri = 'cardUri';
$this->backend->expects($this->once())->method('getCardUri')
->with($cardId)->willReturn($cardUri);
$this->backend->expects($this->once())->method('deleteCard')
->with($this->addressBookInfo['id'], $cardUri)
->willReturn(true);
$this->assertTrue($this->addressBookImpl->delete($cardId));
}
public function testReadCard() {
$vCard = new VCard();
$vCard->add(new Text($vCard, 'UID', 'uid'));
$vCardSerialized = $vCard->serialize();
$result = $this->invokePrivate($this->addressBookImpl, 'readCard', [$vCardSerialized]);
$resultSerialized = $result->serialize();
$this->assertSame($vCardSerialized, $resultSerialized);
}
public function testCreateUid() {
/** @var \PHPUnit_Framework_MockObject_MockObject | AddressBookImpl $addressBookImpl */
$addressBookImpl = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBookImpl')
->setConstructorArgs(
[
$this->addressBook,
$this->addressBookInfo,
$this->backend
]
)
->setMethods(['getUid'])
->getMock();
$addressBookImpl->expects($this->at(0))->method('getUid')->willReturn('uid0');
$addressBookImpl->expects($this->at(1))->method('getUid')->willReturn('uid1');
// simulate that 'uid0' already exists, so the second uid will be returned
$this->backend->expects($this->exactly(2))->method('getContact')
->willReturnCallback(
function($uid) {
return ($uid === 'uid0.vcf');
}
);
$this->assertSame('uid1',
$this->invokePrivate($addressBookImpl, 'createUid', [])
);
}
public function testCreateEmptyVCard() {
$uid = 'uid';
$expectedVCard = new VCard();
$expectedVCard->add(new Text($expectedVCard, 'UID', $uid));
$expectedVCardSerialized = $expectedVCard->serialize();
$result = $this->invokePrivate($this->addressBookImpl, 'createEmptyVCard', [$uid]);
$resultSerialized = $result->serialize();
$this->assertSame($expectedVCardSerialized, $resultSerialized);
}
}

View File

@ -20,8 +20,14 @@
*/
namespace OCA\DAV\Tests\Unit\CardDAV;
use InvalidArgumentException;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\IDBConnection;
use OCP\ILogger;
use Sabre\DAV\PropPatch;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property\Text;
use Test\TestCase;
/**
@ -36,22 +42,46 @@ class CardDavBackendTest extends TestCase {
/** @var CardDavBackend */
private $backend;
/** @var Principal | \PHPUnit_Framework_MockObject_MockObject */
private $principal;
/** @var ILogger | \PHPUnit_Framework_MockObject_MockObject */
private $logger;
/** @var IDBConnection */
private $db;
/** @var string */
private $dbCardsTable = 'cards';
/** @var string */
private $dbCardsPropertiesTable = 'cards_properties';
const UNIT_TEST_USER = 'carddav-unit-test';
public function setUp() {
parent::setUp();
$principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal')
$this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal')
->disableOriginalConstructor()
->setMethods(['getPrincipalByPath'])
->getMock();
$principal->method('getPrincipalByPath')
$this->principal->method('getPrincipalByPath')
->willReturn([
'uri' => 'principals/best-friend'
]);
$this->logger = $this->getMock('\OCP\ILogger');
$this->db = \OC::$server->getDatabaseConnection();
$this->backend = new CardDavBackend($this->db, $this->principal, $this->logger);
// start every test with a empty cards_properties and cards table
$query = $this->db->getQueryBuilder();
$query->delete('cards_properties')->execute();
$query = $this->db->getQueryBuilder();
$query->delete('cards')->execute();
$db = \OC::$server->getDatabaseConnection();
$this->backend = new CardDavBackend($db, $principal);
$this->tearDown();
}
@ -96,23 +126,32 @@ class CardDavBackendTest extends TestCase {
}
public function testCardOperations() {
/** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $backend */
$backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')
->setConstructorArgs([$this->db, $this->principal, $this->logger])
->setMethods(['updateProperties', 'purgeProperties'])->getMock();
// create a new address book
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
$books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
$backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
$books = $backend->getAddressBooksForUser(self::UNIT_TEST_USER);
$this->assertEquals(1, count($books));
$bookId = $books[0]['id'];
// create a card
$uri = $this->getUniqueID('card');
$this->backend->createCard($bookId, $uri, '');
// updateProperties is expected twice, once for createCard and once for updateCard
$backend->expects($this->at(0))->method('updateProperties')->with($bookId, $uri, '');
$backend->expects($this->at(1))->method('updateProperties')->with($bookId, $uri, '***');
// create a card
$backend->createCard($bookId, $uri, '');
// get all the cards
$cards = $this->backend->getCards($bookId);
$cards = $backend->getCards($bookId);
$this->assertEquals(1, count($cards));
$this->assertEquals('', $cards[0]['carddata']);
// get the cards
$card = $this->backend->getCard($bookId, $uri);
$card = $backend->getCard($bookId, $uri);
$this->assertNotNull($card);
$this->assertArrayHasKey('id', $card);
$this->assertArrayHasKey('uri', $card);
@ -122,17 +161,23 @@ class CardDavBackendTest extends TestCase {
$this->assertEquals('', $card['carddata']);
// update the card
$this->backend->updateCard($bookId, $uri, '***');
$card = $this->backend->getCard($bookId, $uri);
$backend->updateCard($bookId, $uri, '***');
$card = $backend->getCard($bookId, $uri);
$this->assertEquals('***', $card['carddata']);
// delete the card
$this->backend->deleteCard($bookId, $uri);
$cards = $this->backend->getCards($bookId);
$backend->expects($this->once())->method('purgeProperties')->with($bookId, $card['id']);
$backend->deleteCard($bookId, $uri);
$cards = $backend->getCards($bookId);
$this->assertEquals(0, count($cards));
}
public function testMultiCard() {
$this->backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')
->setConstructorArgs([$this->db, $this->principal, $this->logger])
->setMethods(['updateProperties'])->getMock();
// create a new address book
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
$books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
@ -175,6 +220,11 @@ class CardDavBackendTest extends TestCase {
}
public function testSyncSupport() {
$this->backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')
->setConstructorArgs([$this->db, $this->principal, $this->logger])
->setMethods(['updateProperties'])->getMock();
// create a new address book
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
$books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
@ -213,7 +263,7 @@ class CardDavBackendTest extends TestCase {
$books = $this->backend->getAddressBooksForUser('principals/best-friend');
$this->assertEquals(1, count($books));
$this->backend->updateShares('Example', [], [['href' => 'principal:principals/best-friend']]);
$this->backend->updateShares('Example', [], ['principal:principals/best-friend']);
$shares = $this->backend->getShares('Example');
$this->assertEquals(0, count($shares));
@ -221,4 +271,273 @@ class CardDavBackendTest extends TestCase {
$books = $this->backend->getAddressBooksForUser('principals/best-friend');
$this->assertEquals(0, count($books));
}
public function testUpdateProperties() {
$bookId = 42;
$cardUri = 'card-uri';
$cardId = 2;
$backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')
->setConstructorArgs([$this->db, $this->principal, $this->logger])
->setMethods(['getCardId'])->getMock();
$backend->expects($this->any())->method('getCardId')->willReturn($cardId);
// add properties for new vCard
$vCard = new VCard();
$vCard->add(new Text($vCard, 'UID', $cardUri));
$vCard->add(new Text($vCard, 'FN', 'John Doe'));
$this->invokePrivate($backend, 'updateProperties', [$bookId, $cardUri, $vCard->serialize()]);
$query = $this->db->getQueryBuilder();
$result = $query->select('*')->from('cards_properties')->execute()->fetchAll();
$this->assertSame(2, count($result));
$this->assertSame('UID', $result[0]['name']);
$this->assertSame($cardUri, $result[0]['value']);
$this->assertSame($bookId, (int)$result[0]['addressbookid']);
$this->assertSame($cardId, (int)$result[0]['cardid']);
$this->assertSame('FN', $result[1]['name']);
$this->assertSame('John Doe', $result[1]['value']);
$this->assertSame($bookId, (int)$result[1]['addressbookid']);
$this->assertSame($cardId, (int)$result[1]['cardid']);
// update properties for existing vCard
$vCard = new VCard();
$vCard->add(new Text($vCard, 'FN', 'John Doe'));
$this->invokePrivate($backend, 'updateProperties', [$bookId, $cardUri, $vCard->serialize()]);
$query = $this->db->getQueryBuilder();
$result = $query->select('*')->from('cards_properties')->execute()->fetchAll();
$this->assertSame(1, count($result));
$this->assertSame('FN', $result[0]['name']);
$this->assertSame('John Doe', $result[0]['value']);
$this->assertSame($bookId, (int)$result[0]['addressbookid']);
$this->assertSame($cardId, (int)$result[0]['cardid']);
}
public function testPurgeProperties() {
$query = $this->db->getQueryBuilder();
$query->insert('cards_properties')
->values(
[
'addressbookid' => $query->createNamedParameter(1),
'cardid' => $query->createNamedParameter(1),
'name' => $query->createNamedParameter('name1'),
'value' => $query->createNamedParameter('value1'),
'preferred' => $query->createNamedParameter(0)
]
);
$query->execute();
$query = $this->db->getQueryBuilder();
$query->insert('cards_properties')
->values(
[
'addressbookid' => $query->createNamedParameter(1),
'cardid' => $query->createNamedParameter(2),
'name' => $query->createNamedParameter('name2'),
'value' => $query->createNamedParameter('value2'),
'preferred' => $query->createNamedParameter(0)
]
);
$query->execute();
$this->invokePrivate($this->backend, 'purgeProperties', [1, 1]);
$query = $this->db->getQueryBuilder();
$result = $query->select('*')->from('cards_properties')->execute()->fetchAll();
$this->assertSame(1, count($result));
$this->assertSame(1 ,(int)$result[0]['addressbookid']);
$this->assertSame(2 ,(int)$result[0]['cardid']);
}
public function testGetCardId() {
$query = $this->db->getQueryBuilder();
$query->insert('cards')
->values(
[
'addressbookid' => $query->createNamedParameter(1),
'carddata' => $query->createNamedParameter(''),
'uri' => $query->createNamedParameter('uri'),
'lastmodified' => $query->createNamedParameter(4738743),
'etag' => $query->createNamedParameter('etag'),
'size' => $query->createNamedParameter(120)
]
);
$query->execute();
$id = $query->getLastInsertId();
$this->assertSame($id,
$this->invokePrivate($this->backend, 'getCardId', ['uri']));
}
/**
* @expectedException InvalidArgumentException
*/
public function testGetCardIdFailed() {
$this->invokePrivate($this->backend, 'getCardId', ['uri']);
}
/**
* @dataProvider dataTestSearch
*
* @param string $pattern
* @param array $expected
*/
public function testSearch($pattern, $properties, $expected) {
/** @var VCard $vCards */
$vCards = [];
$vCards[0] = new VCard();
$vCards[0]->add(new Text($vCards[0], 'UID', 'uid'));
$vCards[0]->add(new Text($vCards[0], 'FN', 'John Doe'));
$vCards[0]->add(new Text($vCards[0], 'CLOUD', 'john@owncloud.org'));
$vCards[1] = new VCard();
$vCards[1]->add(new Text($vCards[1], 'UID', 'uid'));
$vCards[1]->add(new Text($vCards[1], 'FN', 'John M. Doe'));
$vCardIds = [];
$query = $this->db->getQueryBuilder();
for($i=0; $i<2; $i++) {
$query->insert($this->dbCardsTable)
->values(
[
'addressbookid' => $query->createNamedParameter(0),
'carddata' => $query->createNamedParameter($vCards[$i]->serialize(), \PDO::PARAM_LOB),
'uri' => $query->createNamedParameter('uri' . $i),
'lastmodified' => $query->createNamedParameter(time()),
'etag' => $query->createNamedParameter('etag' . $i),
'size' => $query->createNamedParameter(120),
]
);
$query->execute();
$vCardIds[] = $query->getLastInsertId();
}
$query->insert($this->dbCardsPropertiesTable)
->values(
[
'addressbookid' => $query->createNamedParameter(0),
'cardid' => $query->createNamedParameter($vCardIds[0]),
'name' => $query->createNamedParameter('FN'),
'value' => $query->createNamedParameter('John Doe'),
'preferred' => $query->createNamedParameter(0)
]
);
$query->execute();
$query->insert($this->dbCardsPropertiesTable)
->values(
[
'addressbookid' => $query->createNamedParameter(0),
'cardid' => $query->createNamedParameter($vCardIds[0]),
'name' => $query->createNamedParameter('CLOUD'),
'value' => $query->createNamedParameter('John@owncloud.org'),
'preferred' => $query->createNamedParameter(0)
]
);
$query->execute();
$query->insert($this->dbCardsPropertiesTable)
->values(
[
'addressbookid' => $query->createNamedParameter(0),
'cardid' => $query->createNamedParameter($vCardIds[1]),
'name' => $query->createNamedParameter('FN'),
'value' => $query->createNamedParameter('John M. Doe'),
'preferred' => $query->createNamedParameter(0)
]
);
$query->execute();
$result = $this->backend->search(0, $pattern, $properties);
// check result
$this->assertSame(count($expected), count($result));
$found = [];
foreach ($result as $r) {
foreach ($expected as $exp) {
if (strpos($r, $exp) > 0) {
$found[$exp] = true;
break;
}
}
}
$this->assertSame(count($expected), count($found));
}
public function dataTestSearch() {
return [
['John', ['FN'], ['John Doe', 'John M. Doe']],
['M. Doe', ['FN'], ['John M. Doe']],
['Do', ['FN'], ['John Doe', 'John M. Doe']],
// check if duplicates are handled correctly
['John', ['FN', 'CLOUD'], ['John Doe', 'John M. Doe']],
];
}
public function testGetCardUri() {
$query = $this->db->getQueryBuilder();
$query->insert($this->dbCardsTable)
->values(
[
'addressbookid' => $query->createNamedParameter(1),
'carddata' => $query->createNamedParameter('carddata', \PDO::PARAM_LOB),
'uri' => $query->createNamedParameter('uri'),
'lastmodified' => $query->createNamedParameter(5489543),
'etag' => $query->createNamedParameter('etag'),
'size' => $query->createNamedParameter(120),
]
);
$query->execute();
$id = $query->getLastInsertId();
$this->assertSame('uri', $this->backend->getCardUri($id));
}
/**
* @expectedException InvalidArgumentException
*/
public function testGetCardUriFailed() {
$this->backend->getCardUri(1);
}
public function testGetContact() {
$query = $this->db->getQueryBuilder();
for($i=0; $i<2; $i++) {
$query->insert($this->dbCardsTable)
->values(
[
'addressbookid' => $query->createNamedParameter($i),
'carddata' => $query->createNamedParameter('carddata' . $i, \PDO::PARAM_LOB),
'uri' => $query->createNamedParameter('uri' . $i),
'lastmodified' => $query->createNamedParameter(5489543),
'etag' => $query->createNamedParameter('etag' . $i),
'size' => $query->createNamedParameter(120),
]
);
$query->execute();
}
$result = $this->backend->getContact('uri0');
$this->assertSame(7, count($result));
$this->assertSame(0, (int)$result['addressbookid']);
$this->assertSame('uri0', $result['uri']);
$this->assertSame(5489543, (int)$result['lastmodified']);
$this->assertSame('etag0', $result['etag']);
$this->assertSame(120, (int)$result['size']);
}
public function testGetContactFail() {
$this->assertEmpty($this->backend->getContact('uri'));
}
}

View File

@ -0,0 +1,81 @@
<?php
/**
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\DAV\Tests\Unit\CardDAV;
use OCA\DAV\CardDAV\Sharing\IShareableAddressBook;
use OCA\DAV\CardDAV\Sharing\Plugin;
use OCA\DAV\Connector\Sabre\Auth;
use OCP\IRequest;
use Sabre\DAV\Server;
use Sabre\DAV\SimpleCollection;
use Sabre\HTTP\Request;
use Sabre\HTTP\Response;
use Test\TestCase;
class PluginTest extends TestCase {
/** @var Plugin */
private $plugin;
/** @var Server */
private $server;
/** @var IShareableAddressBook | \PHPUnit_Framework_MockObject_MockObject */
private $book;
public function setUp() {
parent::setUp();
/** @var Auth | \PHPUnit_Framework_MockObject_MockObject $authBackend */
$authBackend = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Auth')->disableOriginalConstructor()->getMock();
$authBackend->method('isDavAuthenticated')->willReturn(true);
/** @var IRequest $request */
$request = $this->getMockBuilder('OCP\IRequest')->disableOriginalConstructor()->getMock();
$this->plugin = new Plugin($authBackend, $request);
$root = new SimpleCollection('root');
$this->server = new \Sabre\DAV\Server($root);
/** @var SimpleCollection $node */
$this->book = $this->getMockBuilder('OCA\DAV\CardDAV\Sharing\IShareableAddressBook')->disableOriginalConstructor()->getMock();
$this->book->method('getName')->willReturn('addressbook1.vcf');
$root->addChild($this->book);
$this->plugin->initialize($this->server);
}
public function testSharing() {
$this->book->expects($this->once())->method('updateShares')->with([[
'href' => 'principal:principals/admin',
'commonName' => null,
'summary' => null,
'readOnly' => false
]], ['mailto:wilfredo@example.com']);
// setup request
$request = new Request();
$request->addHeader('Content-Type', 'application/xml');
$request->setUrl('addressbook1.vcf');
$request->setBody('<?xml version="1.0" encoding="utf-8" ?><CS:share xmlns:D="DAV:" xmlns:CS="urn:ietf:params:xml:ns:carddav"><CS:set><D:href>principal:principals/admin</D:href><CS:read-write/></CS:set> <CS:remove><D:href>mailto:wilfredo@example.com</D:href></CS:remove></CS:share>');
$response = new Response();
$this->plugin->httpPost($request, $response);
}
}

View File

@ -48,6 +48,14 @@ class File extends \Test\TestCase {
parent::tearDown();
}
private function getMockStorage() {
$storage = $this->getMock('\OCP\Files\Storage');
$storage->expects($this->any())
->method('getId')
->will($this->returnValue('home::someuser'));
return $storage;
}
/**
* @param string $string
*/
@ -149,7 +157,7 @@ class File extends \Test\TestCase {
->method('getRelativePath')
->will($this->returnArgument(0));
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
@ -209,7 +217,7 @@ class File extends \Test\TestCase {
$_SERVER['HTTP_OC_CHUNKED'] = true;
$info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-0', null, null, [
$info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [
'permissions' => \OCP\Constants::PERMISSION_ALL
], null);
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
@ -219,7 +227,7 @@ class File extends \Test\TestCase {
$this->assertNull($file->put('test data one'));
$file->releaseLock(ILockingProvider::LOCK_SHARED);
$info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-1', null, null, [
$info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-1', $this->getMockStorage(), null, [
'permissions' => \OCP\Constants::PERMISSION_ALL
], null);
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
@ -261,7 +269,7 @@ class File extends \Test\TestCase {
$info = new \OC\Files\FileInfo(
$viewRoot . '/' . ltrim($path, '/'),
null,
$this->getMockStorage(),
null,
['permissions' => \OCP\Constants::PERMISSION_ALL],
null
@ -450,7 +458,7 @@ class File extends \Test\TestCase {
$_SERVER['CONTENT_LENGTH'] = 123456;
$_SERVER['REQUEST_METHOD'] = 'PUT';
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
@ -483,7 +491,7 @@ class File extends \Test\TestCase {
// simulate situation where the target file is locked
$view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE);
$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
@ -518,7 +526,7 @@ class File extends \Test\TestCase {
$_SERVER['HTTP_OC_CHUNKED'] = true;
$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-0', null, null, [
$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [
'permissions' => \OCP\Constants::PERMISSION_ALL
], null);
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
@ -526,7 +534,7 @@ class File extends \Test\TestCase {
$this->assertNull($file->put('test data one'));
$file->releaseLock(ILockingProvider::LOCK_SHARED);
$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-1', null, null, [
$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-1', $this->getMockStorage(), null, [
'permissions' => \OCP\Constants::PERMISSION_ALL
], null);
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
@ -555,7 +563,7 @@ class File extends \Test\TestCase {
->method('getRelativePath')
->will($this->returnArgument(0));
$info = new \OC\Files\FileInfo('/*', null, null, array(
$info = new \OC\Files\FileInfo('/*', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
@ -591,7 +599,7 @@ class File extends \Test\TestCase {
->method('getRelativePath')
->will($this->returnArgument(0));
$info = new \OC\Files\FileInfo('/*', null, null, array(
$info = new \OC\Files\FileInfo('/*', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
@ -618,7 +626,7 @@ class File extends \Test\TestCase {
$_SERVER['CONTENT_LENGTH'] = 12345;
$_SERVER['REQUEST_METHOD'] = 'PUT';
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
@ -654,7 +662,7 @@ class File extends \Test\TestCase {
->method('unlink')
->will($this->returnValue(true));
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
@ -672,7 +680,7 @@ class File extends \Test\TestCase {
$view = $this->getMock('\OC\Files\View',
array());
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
'permissions' => 0
), null);
@ -695,7 +703,7 @@ class File extends \Test\TestCase {
->method('unlink')
->will($this->returnValue(false));
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
@ -718,7 +726,7 @@ class File extends \Test\TestCase {
->method('unlink')
->willThrowException(new ForbiddenException('', true));
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
@ -753,7 +761,7 @@ class File extends \Test\TestCase {
$path = 'test-locking.txt';
$info = new \OC\Files\FileInfo(
'/' . $this->user . '/files/' . $path,
null,
$this->getMockStorage(),
null,
['permissions' => \OCP\Constants::PERMISSION_ALL],
null
@ -865,7 +873,7 @@ class File extends \Test\TestCase {
->method('fopen')
->will($this->returnValue(false));
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);
@ -883,7 +891,7 @@ class File extends \Test\TestCase {
->method('fopen')
->willThrowException(new ForbiddenException('', true));
$info = new \OC\Files\FileInfo('/test.txt', null, null, array(
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array(
'permissions' => \OCP\Constants::PERMISSION_ALL
), null);

View File

@ -2,6 +2,8 @@
namespace OCA\DAV\Tests\Unit\Connector\Sabre;
use OCP\Files\StorageNotAvailableException;
/**
* Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
@ -143,6 +145,29 @@ class FilesPlugin extends \Test\TestCase {
$this->assertEquals(array(self::SIZE_PROPERTYNAME), $propFind->get404Properties());
}
public function testGetPropertiesStorageNotAvailable() {
$node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
$propFind = new \Sabre\DAV\PropFind(
'/dummyPath',
array(
self::DOWNLOADURL_PROPERTYNAME,
),
0
);
$node->expects($this->once())
->method('getDirectDownload')
->will($this->throwException(new StorageNotAvailableException()));
$this->plugin->handleGetProperties(
$propFind,
$node
);
$this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME));
}
public function testGetPublicPermissions() {
$this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view, true);
$this->plugin->initialize($this->server);

View File

@ -294,4 +294,45 @@ class ObjectTree extends \Test\TestCase {
$this->assertInstanceOf('\Sabre\DAV\INode', $tree->getNodeForPath($path));
}
/**
* @expectedException \Sabre\DAV\Exception\Forbidden
* @expectedExceptionMessage Could not copy directory nameOfSourceNode, target exists
*/
public function testFailingMove() {
$source = 'a/b';
$destination = 'b/b';
$updatables = array('a' => true, 'a/b' => true, 'b' => true, 'b/b' => false);
$deletables = array('a/b' => true);
$view = new TestDoubleFileView($updatables, $deletables);
$info = new FileInfo('', null, null, array(), null);
$rootDir = new \OCA\DAV\Connector\Sabre\Directory($view, $info);
$objectTree = $this->getMock('\OCA\DAV\Connector\Sabre\ObjectTree',
array('nodeExists', 'getNodeForPath'),
array($rootDir, $view));
$sourceNode = $this->getMockBuilder('\Sabre\DAV\ICollection')
->disableOriginalConstructor()
->getMock();
$sourceNode->expects($this->once())
->method('getName')
->will($this->returnValue('nameOfSourceNode'));
$objectTree->expects($this->once())
->method('nodeExists')
->with($this->identicalTo($destination))
->will($this->returnValue(true));
$objectTree->expects($this->once())
->method('getNodeForPath')
->with($this->identicalTo($source))
->will($this->returnValue($sourceNode));
/** @var $objectTree \OCA\DAV\Connector\Sabre\ObjectTree */
$mountManager = \OC\Files\Filesystem::getMountManager();
$objectTree->init($rootDir, $view, $mountManager);
$objectTree->move($source, $destination);
}
}

View File

@ -9,12 +9,8 @@
namespace OCA\DAV\Tests\Unit\SystemTag;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\Exception\MethodNotAllowed;
use Sabre\DAV\Exception\Conflict;
use OC\SystemTag\SystemTag;
use OCP\SystemTag\TagNotFoundException;
use OCP\SystemTag\TagAlreadyExistsException;
class SystemTagMappingNode extends SystemTagNode {

View File

@ -11,7 +11,6 @@ namespace OCA\DAV\Tests\Unit\SystemTag;
use OC\SystemTag\SystemTag;
use OCP\SystemTag\TagNotFoundException;
use OCP\SystemTag\TagAlreadyExistsException;
class SystemTagsByIdCollection extends \Test\TestCase {

View File

@ -11,7 +11,6 @@ namespace OCA\DAV\Tests\Unit\SystemTag;
use OC\SystemTag\SystemTag;
use OCP\SystemTag\TagNotFoundException;
use OCP\SystemTag\TagAlreadyExistsException;
class SystemTagsObjectMappingCollection extends \Test\TestCase {

View File

@ -32,6 +32,7 @@ OC.L10N.register(
"The share will expire on %s." : "El objeto dejará de ser compartido el %s.",
"Cheers!" : "¡Saludos!",
"Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"ownCloud basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Hola,\n<br><br>\nEl administrador ha habilitado el cifrado del lado servidor. Sus archivos serán cifrados usando como contraseña: <strong>%s</strong>\n<br><br>\nPor favor, identifíquese en la interfaz web, vaya a la sección 'Modulo básico de cifrado' de sus opciones personales y actualice su contraseña tecleando esta contraseña en el campo 'contraseña antigua' e introduciendo la nueva en su correspondiente campo.<br><br>",
"Encrypt the home storage" : "Encriptar el almacenamiento personal",
"Enable recovery key" : "Activa la clave de recuperación",
"Disable recovery key" : "Desactiva la clave de recuperación",
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "La clave de recuperación es una clave de cifrado extra que se usa para cifrar ficheros. Permite la recuperación de los ficheros de un usuario si él o ella olvida su contraseña.",

View File

@ -30,6 +30,7 @@
"The share will expire on %s." : "El objeto dejará de ser compartido el %s.",
"Cheers!" : "¡Saludos!",
"Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"ownCloud basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Hola,\n<br><br>\nEl administrador ha habilitado el cifrado del lado servidor. Sus archivos serán cifrados usando como contraseña: <strong>%s</strong>\n<br><br>\nPor favor, identifíquese en la interfaz web, vaya a la sección 'Modulo básico de cifrado' de sus opciones personales y actualice su contraseña tecleando esta contraseña en el campo 'contraseña antigua' e introduciendo la nueva en su correspondiente campo.<br><br>",
"Encrypt the home storage" : "Encriptar el almacenamiento personal",
"Enable recovery key" : "Activa la clave de recuperación",
"Disable recovery key" : "Desactiva la clave de recuperación",
"The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "La clave de recuperación es una clave de cifrado extra que se usa para cifrar ficheros. Permite la recuperación de los ficheros de un usuario si él o ella olvida su contraseña.",

View File

@ -34,6 +34,7 @@ OC.L10N.register(
"New recovery key password" : "Uus taastevõtme parool",
"Repeat new recovery key password" : "Korda uut taastevõtme parooli",
"Change Password" : "Muuda parooli",
"ownCloud basic encryption module" : "ownCloud peamine küpteerimismoodul",
"Your private key password no longer matches your log-in password." : "Sinu provaatvõtme parool ei kattu enam sinu sisselogimise parooliga.",
"Set your old private key password to your current log-in password:" : "Pane oma vana privaatvõtme parooliks oma praegune sisselogimise parool.",
" If you don't remember your old password you can ask your administrator to recover your files." : "Kui sa ei mäleta oma vana parooli, siis palu oma süsteemihalduril taastada ligipääs failidele.",

View File

@ -32,6 +32,7 @@
"New recovery key password" : "Uus taastevõtme parool",
"Repeat new recovery key password" : "Korda uut taastevõtme parooli",
"Change Password" : "Muuda parooli",
"ownCloud basic encryption module" : "ownCloud peamine küpteerimismoodul",
"Your private key password no longer matches your log-in password." : "Sinu provaatvõtme parool ei kattu enam sinu sisselogimise parooliga.",
"Set your old private key password to your current log-in password:" : "Pane oma vana privaatvõtme parooliks oma praegune sisselogimise parool.",
" If you don't remember your old password you can ask your administrator to recover your files." : "Kui sa ei mäleta oma vana parooli, siis palu oma süsteemihalduril taastada ligipääs failidele.",

View File

@ -30,7 +30,7 @@ OC.L10N.register(
"Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Não é possível ler este arquivo, provavelmente este é um arquivo compartilhado. Por favor, pergunte o dono do arquivo para recompartilhar o arquivo com você.",
"Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password '%s'.\n\nPlease login to the web interface, go to the section 'ownCloud basic encryption module' of your personal settings and update your encryption password by entering this password into the 'old log-in password' field and your current login-password.\n\n" : "Olá,\n\nO administrador habilitou criptografia-lado-servidor. Os seus arquivos foram criptografados usando a senha '%s'.\n\nPor favor faça o login para a interface da Web, vá para a seção 'ownCloud módulo de criptografia básico' das suas definições pessoais e atualize sua senha de criptografia, inserindo esta senha no campo 'senha antiga de log-in' e sua atual senha-de-login.\n\n",
"The share will expire on %s." : "O compartilhamento irá expirar em %s.",
"Cheers!" : "Saúde!",
"Cheers!" : "Saudações!",
"Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"ownCloud basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Olá,<br><br>o administrador habilitou criptografia-lado-servidor. Os seus arquivos foram criptografados usando a senha <strong>%s</strong>.<br><br>Por favor faça o login para a interface da Web, vá para a seção 'ownCloud módulo de criptografia básico' das suas definições pessoais e atualize sua senha de criptografia, inserindo esta senha no campo 'senha antiga de log-in' e sua atual senha-de-login..<br><br>",
"Encrypt the home storage" : "Criptografar a pasta de armazenamento home",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Ativar essa opção de criptografia para todos os arquivos armazenados no armazenamento principal, caso contrário, apenas arquivos no armazenamento externo serão criptografados",

View File

@ -28,7 +28,7 @@
"Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Não é possível ler este arquivo, provavelmente este é um arquivo compartilhado. Por favor, pergunte o dono do arquivo para recompartilhar o arquivo com você.",
"Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password '%s'.\n\nPlease login to the web interface, go to the section 'ownCloud basic encryption module' of your personal settings and update your encryption password by entering this password into the 'old log-in password' field and your current login-password.\n\n" : "Olá,\n\nO administrador habilitou criptografia-lado-servidor. Os seus arquivos foram criptografados usando a senha '%s'.\n\nPor favor faça o login para a interface da Web, vá para a seção 'ownCloud módulo de criptografia básico' das suas definições pessoais e atualize sua senha de criptografia, inserindo esta senha no campo 'senha antiga de log-in' e sua atual senha-de-login.\n\n",
"The share will expire on %s." : "O compartilhamento irá expirar em %s.",
"Cheers!" : "Saúde!",
"Cheers!" : "Saudações!",
"Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"ownCloud basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Olá,<br><br>o administrador habilitou criptografia-lado-servidor. Os seus arquivos foram criptografados usando a senha <strong>%s</strong>.<br><br>Por favor faça o login para a interface da Web, vá para a seção 'ownCloud módulo de criptografia básico' das suas definições pessoais e atualize sua senha de criptografia, inserindo esta senha no campo 'senha antiga de log-in' e sua atual senha-de-login..<br><br>",
"Encrypt the home storage" : "Criptografar a pasta de armazenamento home",
"Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Ativar essa opção de criptografia para todos os arquivos armazenados no armazenamento principal, caso contrário, apenas arquivos no armazenamento externo serão criptografados",

View File

@ -30,7 +30,6 @@ use OC\Encryption\Exceptions\DecryptionFailedException;
use OC\Encryption\Exceptions\EncryptionFailedException;
use OCA\Encryption\Exceptions\MultiKeyDecryptException;
use OCA\Encryption\Exceptions\MultiKeyEncryptException;
use OCA\Encryption\Vendor\PBKDF2Fallback;
use OCP\Encryption\Exceptions\GenericEncryptionException;
use OCP\IConfig;
use OCP\ILogger;
@ -293,28 +292,14 @@ class Crypt {
$salt = hash('sha256', $uid . $instanceId . $instanceSecret, true);
$keySize = $this->getKeySize($cipher);
if (function_exists('hash_pbkdf2')) {
$hash = hash_pbkdf2(
'sha256',
$password,
$salt,
100000,
$keySize,
true
);
} else {
// fallback to 3rdparty lib for PHP <= 5.4.
// FIXME: Can be removed as soon as support for PHP 5.4 was dropped
$fallback = new PBKDF2Fallback();
$hash = $fallback->pbkdf2(
'sha256',
$password,
$salt,
100000,
$keySize,
true
);
}
$hash = hash_pbkdf2(
'sha256',
$password,
$salt,
100000,
$keySize,
true
);
return $hash;
}

View File

@ -43,15 +43,18 @@ class MigrationTest extends \Test\TestCase {
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
\OC_User::createUser(self::TEST_ENCRYPTION_MIGRATION_USER1, 'foo');
\OC_User::createUser(self::TEST_ENCRYPTION_MIGRATION_USER2, 'foo');
\OC_User::createUser(self::TEST_ENCRYPTION_MIGRATION_USER3, 'foo');
\OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER1, 'foo');
\OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER2, 'foo');
\OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER3, 'foo');
}
public static function tearDownAfterClass() {
\OC_User::deleteUser(self::TEST_ENCRYPTION_MIGRATION_USER1);
\OC_User::deleteUser(self::TEST_ENCRYPTION_MIGRATION_USER2);
\OC_User::deleteUser(self::TEST_ENCRYPTION_MIGRATION_USER3);
$user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER1);
if ($user !== null) { $user->delete(); }
$user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER2);
if ($user !== null) { $user->delete(); }
$user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER3);
if ($user !== null) { $user->delete(); }
parent::tearDownAfterClass();
}

View File

@ -1,87 +0,0 @@
<?php
/* Note; This class can be removed as soon as we drop PHP 5.4 support.
*
*
* Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
* Copyright (c) 2013, Taylor Hornby
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OCA\Encryption\Vendor;
class PBKDF2Fallback {
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
public function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) {
$algorithm = strtolower($algorithm);
if (!in_array($algorithm, hash_algos(), true))
trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
if ($count <= 0 || $key_length <= 0)
trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
if (function_exists("hash_pbkdf2")) {
// The output length is in NIBBLES (4-bits) if $raw_output is false!
if (!$raw_output) {
$key_length = $key_length * 2;
}
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for ($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if ($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
}

View File

@ -26,6 +26,7 @@ use OCA\Federation\DbHandler;
use OCA\Federation\TrustedServers;
use OCP\AppFramework\Http;
use OCP\BackgroundJob\IJobList;
use OCP\ILogger;
use OCP\IRequest;
use OCP\Security\ISecureRandom;
use OCP\Security\StringUtils;
@ -54,6 +55,9 @@ class OCSAuthAPI {
/** @var DbHandler */
private $dbHandler;
/** @var ILogger */
private $logger;
/**
* OCSAuthAPI constructor.
*
@ -62,19 +66,22 @@ class OCSAuthAPI {
* @param IJobList $jobList
* @param TrustedServers $trustedServers
* @param DbHandler $dbHandler
* @param ILogger $logger
*/
public function __construct(
IRequest $request,
ISecureRandom $secureRandom,
IJobList $jobList,
TrustedServers $trustedServers,
DbHandler $dbHandler
DbHandler $dbHandler,
ILogger $logger
) {
$this->request = $request;
$this->secureRandom = $secureRandom;
$this->jobList = $jobList;
$this->trustedServers = $trustedServers;
$this->dbHandler = $dbHandler;
$this->logger = $logger;
}
/**
@ -88,6 +95,7 @@ class OCSAuthAPI {
$token = $this->request->getParam('token');
if ($this->trustedServers->isTrustedServer($url) === false) {
$this->logger->log(\OCP\Util::ERROR, 'remote server not trusted (' . $url . ') while requesting shared secret');
return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
}
@ -95,6 +103,7 @@ class OCSAuthAPI {
// token wins
$localToken = $this->dbHandler->getToken($url);
if (strcmp($localToken, $token) > 0) {
$this->logger->log(\OCP\Util::ERROR, 'remote server (' . $url . ') presented lower token');
return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
}
@ -120,10 +129,13 @@ class OCSAuthAPI {
$url = $this->request->getParam('url');
$token = $this->request->getParam('token');
if (
$this->trustedServers->isTrustedServer($url) === false
|| $this->isValidToken($url, $token) === false
) {
if ($this->trustedServers->isTrustedServer($url) === false) {
$this->logger->log(\OCP\Util::ERROR, 'remote server not trusted (' . $url . ') while getting shared secret');
return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
}
if ($this->isValidToken($url, $token) === false) {
$this->logger->log(\OCP\Util::ERROR, 'remote server (' . $url . ') didn\'t send a valid token (got ' . $token . ') while getting shared secret');
return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
}
@ -139,7 +151,7 @@ class OCSAuthAPI {
protected function isValidToken($url, $token) {
$storedToken = $this->dbHandler->getToken($url);
return StringUtils::equals($storedToken, $token);
return hash_equals($storedToken, $token);
}
}

View File

@ -108,7 +108,8 @@ class Application extends \OCP\AppFramework\App {
$server->getSecureRandom(),
$server->getJobList(),
$container->query('TrustedServers'),
$container->query('DbHandler')
$container->query('DbHandler'),
$server->getLogger()
);

View File

@ -91,7 +91,7 @@ class GetSharedSecret extends QueuedJob{
$this->trustedServers = new TrustedServers(
$this->dbHandler,
\OC::$server->getHTTPClientService(),
\OC::$server->getLogger(),
$this->logger,
$this->jobList,
\OC::$server->getSecureRandom(),
\OC::$server->getConfig()
@ -148,6 +148,7 @@ class GetSharedSecret extends QueuedJob{
} catch (ClientException $e) {
$status = $e->getCode();
$this->logger->logException($e);
}
// if we received a unexpected response we try again later

View File

@ -60,6 +60,9 @@ class RequestSharedSecret extends QueuedJob {
private $endPoint = '/ocs/v2.php/apps/federation/api/v1/request-shared-secret?format=json';
/** @var ILogger */
private $logger;
/**
* RequestSharedSecret constructor.
*
@ -80,13 +83,14 @@ class RequestSharedSecret extends QueuedJob {
$this->jobList = $jobList ? $jobList : \OC::$server->getJobList();
$this->urlGenerator = $urlGenerator ? $urlGenerator : \OC::$server->getURLGenerator();
$this->dbHandler = $dbHandler ? $dbHandler : new DbHandler(\OC::$server->getDatabaseConnection(), \OC::$server->getL10N('federation'));
$this->logger = \OC::$server->getLogger();
if ($trustedServers) {
$this->trustedServers = $trustedServers;
} else {
$this->trustedServers = new TrustedServers(
$this->dbHandler,
\OC::$server->getHTTPClientService(),
\OC::$server->getLogger(),
$this->logger,
$this->jobList,
\OC::$server->getSecureRandom(),
\OC::$server->getConfig()
@ -142,6 +146,7 @@ class RequestSharedSecret extends QueuedJob {
} catch (ClientException $e) {
$status = $e->getCode();
$this->logger->logException($e);
}
// if we received a unexpected response we try again later

View File

@ -28,6 +28,7 @@ use OCA\Federation\API\OCSAuthAPI;
use OCA\Federation\DbHandler;
use OCA\Federation\TrustedServers;
use OCP\AppFramework\Http;
use OCP\ILogger;
use OCP\IRequest;
use OCP\Security\ISecureRandom;
use Test\TestCase;
@ -49,6 +50,9 @@ class OCSAuthAPITest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject | DbHandler */
private $dbHandler;
/** @var \PHPUnit_Framework_MockObject_MockObject | ILogger */
private $logger;
/** @var OCSAuthApi */
private $ocsAuthApi;
@ -63,13 +67,16 @@ class OCSAuthAPITest extends TestCase {
->disableOriginalConstructor()->getMock();
$this->jobList = $this->getMockBuilder('OC\BackgroundJob\JobList')
->disableOriginalConstructor()->getMock();
$this->logger = $this->getMockBuilder('OCP\ILogger')
->disableOriginalConstructor()->getMock();
$this->ocsAuthApi = new OCSAuthAPI(
$this->request,
$this->secureRandom,
$this->jobList,
$this->trustedServers,
$this->dbHandler
$this->dbHandler,
$this->logger
);
}
@ -136,7 +143,8 @@ class OCSAuthAPITest extends TestCase {
$this->secureRandom,
$this->jobList,
$this->trustedServers,
$this->dbHandler
$this->dbHandler,
$this->logger
]
)->setMethods(['isValidToken'])->getMock();

View File

@ -33,7 +33,7 @@ $htaccessWorking=(getenv('htaccessWorking')=='true');
$upload_max_filesize = OC::$server->getIniWrapper()->getBytes('upload_max_filesize');
$post_max_size = OC::$server->getIniWrapper()->getBytes('post_max_size');
$maxUploadFilesize = OCP\Util::humanFileSize(min($upload_max_filesize, $post_max_size));
if($_POST && OC_Util::isCallRegistered()) {
if($_POST && \OC::$server->getRequest()->passesCSRFCheck()) {
if(isset($_POST['maxUploadSize'])) {
if(($setMaxSize = OC_Files::setUploadLimit(OCP\Util::computerFileSize($_POST['maxUploadSize']))) !== false) {
$maxUploadFilesize = OCP\Util::humanFileSize($setMaxSize);

View File

@ -20,5 +20,5 @@
*
*/
$application->add(new OCA\Files\Command\Scan(OC_User::getManager()));
$application->add(new OCA\Files\Command\Scan(\OC::$server->getUserManager()));
$application->add(new OCA\Files\Command\DeleteOrphanedFiles(\OC::$server->getDatabaseConnection()));

View File

@ -32,6 +32,7 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\Table;
class Scan extends Command {
@ -39,6 +40,15 @@ class Scan extends Command {
* @var \OC\User\Manager $userManager
*/
private $userManager;
/** @var float */
protected $execTime = 0;
/** @var int */
protected $foldersCounter = 0;
/** @var int */
protected $filesCounter = 0;
/** @var bool */
protected $interrupted = false;
public function __construct(\OC\User\Manager $userManager) {
$this->userManager = $userManager;
@ -64,7 +74,13 @@ class Scan extends Command {
'quiet',
'q',
InputOption::VALUE_NONE,
'suppress output'
'suppress any output'
)
->addOption(
'verbose',
'-v|vv|vvv',
InputOption::VALUE_NONE,
'verbose the output'
)
->addOption(
'all',
@ -74,19 +90,31 @@ class Scan extends Command {
);
}
protected function scanFiles($user, $path, $quiet, OutputInterface $output) {
protected function scanFiles($user, $path, $verbose, OutputInterface $output) {
$scanner = new \OC\Files\Utils\Scanner($user, \OC::$server->getDatabaseConnection(), \OC::$server->getLogger());
if (!$quiet) {
# printout and count
if ($verbose) {
$scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) {
$output->writeln("Scanning file <info>$path</info>");
$this->filesCounter += 1;
});
$scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) {
$output->writeln("Scanning folder <info>$path</info>");
$this->foldersCounter += 1;
});
$scanner->listen('\OC\Files\Utils\Scanner', 'StorageNotAvailable', function (StorageNotAvailableException $e) use ($output) {
$output->writeln("Error while scanning, storage not available (" . $e->getMessage() . ")");
});
# count only
} else {
$scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) {
$this->filesCounter += 1;
});
$scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) {
$this->foldersCounter += 1;
});
}
try {
$scanner->scan($path);
} catch (ForbiddenException $e) {
@ -95,6 +123,7 @@ class Scan extends Command {
}
}
protected function execute(InputInterface $input, OutputInterface $output) {
$inputPath = $input->getOption('path');
if ($inputPath) {
@ -106,24 +135,173 @@ class Scan extends Command {
} else {
$users = $input->getArgument('user_id');
}
$quiet = $input->getOption('quiet');
if (count($users) === 0) {
$output->writeln("<error>Please specify the user id to scan, \"--all\" to scan for all users or \"--path=...\"</error>");
return;
}
# no messaging level option means: no full printout but statistics
# $quiet means no print at all
# $verbose means full printout including statistics
# -q -v full stat
# 0 0 no yes
# 0 1 yes yes
# 1 -- no no (quiet overrules verbose)
$verbose = $input->getOption('verbose');
$quiet = $input->getOption('quiet');
# restrict the verbosity level to VERBOSITY_VERBOSE
if ($output->getVerbosity()>OutputInterface::VERBOSITY_VERBOSE) {
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
}
if ($quiet) {
$verbose = false;
}
$this->initTools();
foreach ($users as $user) {
if (is_object($user)) {
$user = $user->getUID();
}
$path = $inputPath ? $inputPath : '/' . $user;
if ($this->userManager->userExists($user)) {
$this->scanFiles($user, $path, $quiet, $output);
# full: printout data if $verbose was set
$this->scanFiles($user, $path, $verbose, $output);
} else {
$output->writeln("<error>Unknown user $user</error>");
}
}
# stat: printout statistics if $quiet was not set
if (!$quiet) {
$this->presentStats($output);
}
}
/**
* Checks if the command was interrupted by ctrl-c
*/
protected function checkForInterruption($output) {
if ($this->hasBeenInterrupted()) {
$this->presentResults($output);
exit;
}
}
/**
* Initialises some useful tools for the Command
*/
protected function initTools() {
// Start the timer
$this->execTime = -microtime(true);
// Convert PHP errors to exceptions
set_error_handler([$this, 'exceptionErrorHandler'], E_ALL);
// Collect interrupts and notify the running command
pcntl_signal(SIGTERM, [$this, 'cancelOperation']);
pcntl_signal(SIGINT, [$this, 'cancelOperation']);
}
/**
* Changes the status of the command to "interrupted"
*
* Gives a chance to the command to properly terminate what it's doing
*/
private function cancelOperation() {
$this->interrupted = true;
}
/**
* Processes PHP errors as exceptions in order to be able to keep track of problems
*
* @see https://secure.php.net/manual/en/function.set-error-handler.php
*
* @param int $severity the level of the error raised
* @param string $message
* @param string $file the filename that the error was raised in
* @param int $line the line number the error was raised
*
* @throws \ErrorException
*/
public function exceptionErrorHandler($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
// This error code is not included in error_reporting
return;
}
throw new \ErrorException($message, 0, $severity, $file, $line);
}
/**
* @return bool
*/
protected function hasBeenInterrupted() {
$cancelled = false;
pcntl_signal_dispatch();
if ($this->interrupted) {
$cancelled = true;
}
return $cancelled;
}
/**
* @param OutputInterface $output
*/
protected function presentStats(OutputInterface $output) {
// Stop the timer
$this->execTime += microtime(true);
$output->writeln("");
$headers = [
'Folders', 'Files', 'Elapsed time'
];
$this->showSummary($headers, null, $output);
}
/**
* Shows a summary of operations
*
* @param string[] $headers
* @param string[] $rows
* @param OutputInterface $output
*/
protected function showSummary($headers, $rows, OutputInterface $output) {
$niceDate = $this->formatExecTime();
if (!$rows) {
$rows = [
$this->foldersCounter,
$this->filesCounter,
$niceDate,
];
}
$table = new Table($output);
$table
->setHeaders($headers)
->setRows([$rows]);
$table->render();
}
/**
* Formats microtime into a human readable format
*
* @return string
*/
protected function formatExecTime() {
list($secs, $tens) = explode('.', sprintf("%.1f", ($this->execTime)));
$niceDate = date('H:i:s', $secs) . '.' . $tens;
return $niceDate;
}
}

View File

@ -39,7 +39,7 @@ if(!\OC\Files\Filesystem::file_exists($filename)) {
exit;
}
$ftype=\OC_Helper::getSecureMimeType(\OC\Files\Filesystem::getMimeType( $filename ));
$ftype=\OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType( $filename ));
header('Content-Type:'.$ftype);
OCP\Response::setContentDispositionHeader(basename($filename), 'attachment');

View File

@ -617,7 +617,16 @@
this.registerAction({
name: 'Delete',
displayName: t('files', 'Delete'),
displayName: function(context) {
var mountType = context.$file.attr('data-mounttype');
var deleteTitle = t('files', 'Delete');
if (mountType === 'external-root') {
deleteTitle = t('files', 'Disconnect storage');
} else if (mountType === 'shared-root') {
deleteTitle = t('files', 'Unshare');
}
return deleteTitle;
},
mime: 'all',
order: 1000,
// permission is READ because we show a hint instead if there is no permission
@ -668,8 +677,9 @@
* @typedef {Object} OCA.Files.FileAction
*
* @property {String} name identifier of the action
* @property {String} displayName display name of the action, defaults
* to the name given in name property
* @property {(String|OCA.Files.FileActions~displayNameFunction)} displayName
* display name string for the action, or function that returns the display name.
* Defaults to the name given in name property
* @property {String} mime mime type
* @property {int} permissions permissions
* @property {(Function|String)} icon icon path to the icon or function
@ -702,6 +712,16 @@
* @return {Object} jQuery link object
*/
/**
* Display name function for actions.
* The function returns the display name of the action using
* the given context information..
*
* @callback OCA.Files.FileActions~displayNameFunction
* @param {OCA.Files.FileActionContext} context action context
* @return {String} display name
*/
/**
* Action handler function for file actions
*

View File

@ -81,6 +81,7 @@
* Renders the menu with the currently set items
*/
render: function() {
var self = this;
var fileActions = this._context.fileActions;
var actions = fileActions.getActions(
fileActions.getCurrentMimeType(),
@ -100,6 +101,13 @@
(!defaultAction || actionSpec.name !== defaultAction.name)
);
});
items = _.map(items, function(item) {
if (_.isFunction(item.displayName)) {
item = _.extend({}, item);
item.displayName = item.displayName(self._context);
}
return item;
});
items = items.sort(function(actionA, actionB) {
var orderA = actionA.order || 0;
var orderB = actionB.order || 0;

View File

@ -990,13 +990,16 @@
}
if (fileData.mountType) {
// FIXME: HACK: detect shared-root
if (fileData.mountType === 'shared' && this.dirInfo.mountType !== 'shared') {
// if parent folder isn't share, assume the displayed folder is a share root
fileData.mountType = 'shared-root';
} else if (fileData.mountType === 'external' && this.dirInfo.mountType !== 'external') {
// if parent folder isn't external, assume the displayed folder is the external storage root
fileData.mountType = 'external-root';
// dirInfo (parent) only exist for the "real" file list
if (this.dirInfo.id) {
// FIXME: HACK: detect shared-root
if (fileData.mountType === 'shared' && this.dirInfo.mountType !== 'shared' && this.dirInfo.mountType !== 'shared-root') {
// if parent folder isn't share, assume the displayed folder is a share root
fileData.mountType = 'shared-root';
} else if (fileData.mountType === 'external' && this.dirInfo.mountType !== 'external' && this.dirInfo.mountType !== 'external-root') {
// if parent folder isn't external, assume the displayed folder is the external storage root
fileData.mountType = 'external-root';
}
}
tr.attr('data-mounttype', fileData.mountType);
}
@ -1065,7 +1068,7 @@
nameSpan.tooltip({placement: 'right'});
}
// dirs can show the number of uploaded files
if (mime !== 'httpd/unix-directory') {
if (mime === 'httpd/unix-directory') {
linkElem.append($('<span></span>').attr({
'class': 'uploadtext',
'currentUploads': 0
@ -1380,7 +1383,7 @@
* Returns list of webdav properties to request
*/
_getWebdavProperties: function() {
return this.filesClient.getPropfindProperties();
return [].concat(this.filesClient.getPropfindProperties());
},
/**
@ -2069,7 +2072,7 @@
*/
showFileBusyState: function(files, state) {
var self = this;
if (!_.isArray(files)) {
if (!_.isArray(files) && !files.is) {
files = [files];
}
@ -2077,10 +2080,13 @@
state = true;
}
_.each(files, function($tr) {
_.each(files, function(fileName) {
// jquery element already ?
if (!$tr.is) {
$tr = self.findFileEl($tr);
var $tr;
if (_.isString(fileName)) {
$tr = self.findFileEl(fileName);
} else {
$tr = $(fileName);
}
var $thumbEl = $tr.find('.thumbnail');
@ -2465,6 +2471,7 @@
}
});
fileUploadStart.on('fileuploadadd', function(e, data) {
console.log('XXXXXXX');
OC.Upload.log('filelist handle fileuploadadd', e, data);
//finish delete if we are uploading a deleted file
@ -2484,8 +2491,7 @@
var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads);
if (currentUploads === 1) {
var img = OC.imagePath('core', 'loading.gif');
data.context.find('.thumbnail').css('background-image', 'url(' + img + ')');
self.showFileBusyState(uploadText.closest('tr'), true);
uploadText.text(translatedText);
uploadText.show();
} else {
@ -2523,8 +2529,7 @@
uploadText.attr('currentUploads', currentUploads);
var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads);
if (currentUploads === 0) {
var img = OC.imagePath('core', 'filetypes/folder');
data.context.find('.thumbnail').css('background-image', 'url(' + img + ')');
self.showFileBusyState(uploadText.closest('tr'), false);
uploadText.text(translatedText);
uploadText.hide();
} else {
@ -2601,18 +2606,15 @@
}
}
});
fileUploadStart.on('fileuploadstop', function(e, data) {
OC.Upload.log('filelist handle fileuploadstop', e, data);
fileUploadStart.on('fileuploadstop', function() {
OC.Upload.log('filelist handle fileuploadstop');
//cleanup uploading to a dir
var uploadText = self.$fileList.find('tr .uploadtext');
self.showFileBusyState(uploadText.closest('tr'), false);
uploadText.fadeOut();
uploadText.attr('currentUploads', 0);
//if user pressed cancel hide upload chrome
if (data.errorThrown === 'abort') {
//cleanup uploading to a dir
var uploadText = $('tr .uploadtext');
var img = OC.imagePath('core', 'filetypes/folder');
uploadText.parents('td.filename').find('.thumbnail').css('background-image', 'url(' + img + ')');
uploadText.fadeOut();
uploadText.attr('currentUploads', 0);
}
self.updateStorageStatistics();
});
fileUploadStart.on('fileuploadfail', function(e, data) {
@ -2621,9 +2623,8 @@
//if user pressed cancel hide upload chrome
if (data.errorThrown === 'abort') {
//cleanup uploading to a dir
var uploadText = $('tr .uploadtext');
var img = OC.imagePath('core', 'filetypes/folder');
uploadText.parents('td.filename').find('.thumbnail').css('background-image', 'url(' + img + ')');
var uploadText = self.$fileList.find('tr .uploadtext');
self.showFileBusyState(uploadText.closest('tr'), false);
uploadText.fadeOut();
uploadText.attr('currentUploads', 0);
}

View File

@ -34,6 +34,7 @@ OC.L10N.register(
"Download" : "تحميل",
"Rename" : "إعادة التسمية",
"Delete" : "حذف ",
"Unshare" : "إلغاء المشاركة",
"Details" : "تفاصيل",
"Select" : "اختار",
"Pending" : "قيد الانتظار",

View File

@ -32,6 +32,7 @@
"Download" : "تحميل",
"Rename" : "إعادة التسمية",
"Delete" : "حذف ",
"Unshare" : "إلغاء المشاركة",
"Details" : "تفاصيل",
"Select" : "اختار",
"Pending" : "قيد الانتظار",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Descargar",
"Rename" : "Renomar",
"Delete" : "Desaniciar",
"Disconnect storage" : "Desconeutar almacenamientu",
"Unshare" : "Dexar de compartir",
"Details" : "Detalles",
"Select" : "Esbillar",
"Pending" : "Pendiente",

View File

@ -32,6 +32,8 @@
"Download" : "Descargar",
"Rename" : "Renomar",
"Delete" : "Desaniciar",
"Disconnect storage" : "Desconeutar almacenamientu",
"Unshare" : "Dexar de compartir",
"Details" : "Detalles",
"Select" : "Esbillar",
"Pending" : "Pendiente",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Yüklə",
"Rename" : "Adı dəyiş",
"Delete" : "Sil",
"Disconnect storage" : "Daşıyıcını ayır",
"Unshare" : "Paylaşımı durdur",
"Details" : "Detallar",
"Select" : "Seç",
"Pending" : "Gözləmə",

View File

@ -32,6 +32,8 @@
"Download" : "Yüklə",
"Rename" : "Adı dəyiş",
"Delete" : "Sil",
"Disconnect storage" : "Daşıyıcını ayır",
"Unshare" : "Paylaşımı durdur",
"Details" : "Detallar",
"Select" : "Seç",
"Pending" : "Gözləmə",

View File

@ -26,6 +26,7 @@ OC.L10N.register(
"Download" : "ডাউনলোড",
"Rename" : "পূনঃনামকরণ",
"Delete" : "মুছে",
"Unshare" : "ভাগাভাগি বাতিল ",
"Details" : "বিস্তারিত",
"Pending" : "মুলতুবি",
"Name" : "রাম",

View File

@ -24,6 +24,7 @@
"Download" : "ডাউনলোড",
"Rename" : "পূনঃনামকরণ",
"Delete" : "মুছে",
"Unshare" : "ভাগাভাগি বাতিল ",
"Details" : "বিস্তারিত",
"Pending" : "মুলতুবি",
"Name" : "রাম",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Preuzmi",
"Rename" : "Preimenuj",
"Delete" : "Izbriši",
"Disconnect storage" : "Diskonektuj pohranu",
"Unshare" : "Prestani dijeliti",
"Select" : "Izaberi",
"Pending" : "Na čekanju",
"Unable to determine date" : "Nemoguće odrediti datum",

View File

@ -32,6 +32,8 @@
"Download" : "Preuzmi",
"Rename" : "Preimenuj",
"Delete" : "Izbriši",
"Disconnect storage" : "Diskonektuj pohranu",
"Unshare" : "Prestani dijeliti",
"Select" : "Izaberi",
"Pending" : "Na čekanju",
"Unable to determine date" : "Nemoguće odrediti datum",

View File

@ -5,6 +5,7 @@ OC.L10N.register(
"Files" : "Dateien",
"Download" : "Herunterladen",
"Delete" : "Löschen",
"Unshare" : "Teilung zurücknehmen",
"Details" : "Details",
"New folder" : "Neuer Ordner",
"Upload" : "Hochladen",

View File

@ -3,6 +3,7 @@
"Files" : "Dateien",
"Download" : "Herunterladen",
"Delete" : "Löschen",
"Unshare" : "Teilung zurücknehmen",
"Details" : "Details",
"New folder" : "Neuer Ordner",
"Upload" : "Hochladen",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Herunterladen",
"Rename" : "Umbenennen",
"Delete" : "Löschen",
"Disconnect storage" : "Speicher trennen",
"Unshare" : "Freigabe aufheben",
"Details" : "Details",
"Select" : "Auswählen",
"Pending" : "Ausstehend",

View File

@ -32,6 +32,8 @@
"Download" : "Herunterladen",
"Rename" : "Umbenennen",
"Delete" : "Löschen",
"Disconnect storage" : "Speicher trennen",
"Unshare" : "Freigabe aufheben",
"Details" : "Details",
"Select" : "Auswählen",
"Pending" : "Ausstehend",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Λήψη",
"Rename" : "Μετονομασία",
"Delete" : "Διαγραφή",
"Disconnect storage" : "Αποσυνδεδεμένος αποθηκευτικός χώρος",
"Unshare" : "Διακοπή διαμοιρασμού",
"Details" : "Λεπτομέρειες",
"Select" : "Επιλογή",
"Pending" : "Εκκρεμεί",

View File

@ -32,6 +32,8 @@
"Download" : "Λήψη",
"Rename" : "Μετονομασία",
"Delete" : "Διαγραφή",
"Disconnect storage" : "Αποσυνδεδεμένος αποθηκευτικός χώρος",
"Unshare" : "Διακοπή διαμοιρασμού",
"Details" : "Λεπτομέρειες",
"Select" : "Επιλογή",
"Pending" : "Εκκρεμεί",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Download",
"Rename" : "Rename",
"Delete" : "Delete",
"Disconnect storage" : "Disconnect storage",
"Unshare" : "Unshare",
"Details" : "Details",
"Select" : "Select",
"Pending" : "Pending",

View File

@ -32,6 +32,8 @@
"Download" : "Download",
"Rename" : "Rename",
"Delete" : "Delete",
"Disconnect storage" : "Disconnect storage",
"Unshare" : "Unshare",
"Details" : "Details",
"Select" : "Select",
"Pending" : "Pending",

View File

@ -28,6 +28,7 @@ OC.L10N.register(
"Download" : "Elŝuti",
"Rename" : "Alinomigi",
"Delete" : "Forigi",
"Unshare" : "Malkunhavigi",
"Details" : "Detaloj",
"Select" : "Elekti",
"Pending" : "Traktotaj",

View File

@ -26,6 +26,7 @@
"Download" : "Elŝuti",
"Rename" : "Alinomigi",
"Delete" : "Forigi",
"Unshare" : "Malkunhavigi",
"Details" : "Detaloj",
"Select" : "Elekti",
"Pending" : "Traktotaj",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Descargar",
"Rename" : "Renombrar",
"Delete" : "Eliminar",
"Disconnect storage" : "Desconectar almacenamiento",
"Unshare" : "Dejar de compartir",
"Details" : "Detalles",
"Select" : "Seleccionar",
"Pending" : "Pendiente",
@ -97,6 +99,7 @@ OC.L10N.register(
"Maximum upload size" : "Tamaño máximo de subida",
"max. possible: " : "máx. posible:",
"Save" : "Guardar",
"Missing permissions to edit from here." : "Faltan permisos para poder editar desde aquí.",
"Settings" : "Ajustes",
"WebDAV" : "WebDAV",
"Use this address to <a href=\"%s\" target=\"_blank\">access your Files via WebDAV</a>" : "Use esta URL <a href=\"%s\" target=\"_blank\">para acceder via WebDAV</a>",

View File

@ -32,6 +32,8 @@
"Download" : "Descargar",
"Rename" : "Renombrar",
"Delete" : "Eliminar",
"Disconnect storage" : "Desconectar almacenamiento",
"Unshare" : "Dejar de compartir",
"Details" : "Detalles",
"Select" : "Seleccionar",
"Pending" : "Pendiente",
@ -95,6 +97,7 @@
"Maximum upload size" : "Tamaño máximo de subida",
"max. possible: " : "máx. posible:",
"Save" : "Guardar",
"Missing permissions to edit from here." : "Faltan permisos para poder editar desde aquí.",
"Settings" : "Ajustes",
"WebDAV" : "WebDAV",
"Use this address to <a href=\"%s\" target=\"_blank\">access your Files via WebDAV</a>" : "Use esta URL <a href=\"%s\" target=\"_blank\">para acceder via WebDAV</a>",

View File

@ -28,6 +28,7 @@ OC.L10N.register(
"Download" : "Descargar",
"Rename" : "Renombrar",
"Delete" : "Eliminar",
"Unshare" : "Dejar de compartir",
"Details" : "Detalles",
"Pending" : "Pendiente",
"Name" : "Nombre",

View File

@ -26,6 +26,7 @@
"Download" : "Descargar",
"Rename" : "Renombrar",
"Delete" : "Eliminar",
"Unshare" : "Dejar de compartir",
"Details" : "Detalles",
"Pending" : "Pendiente",
"Name" : "Nombre",

View File

@ -34,12 +34,19 @@ OC.L10N.register(
"Download" : "Lae alla",
"Rename" : "Nimeta ümber",
"Delete" : "Kustuta",
"Disconnect storage" : "Ühenda andmehoidla lahti.",
"Unshare" : "Lõpeta jagamine",
"Details" : "Üksikasjad",
"Select" : "Vali",
"Pending" : "Ootel",
"Unable to determine date" : "Kuupäeva tuvastamine ei õnnestunud",
"This operation is forbidden" : "See toiming on keelatud",
"This directory is unavailable, please check the logs or contact the administrator" : "See kaust pole saadaval. Palun kontrolli logifaile või võta ühendust administraatoriga",
"Could not move \"{file}\"" : "\"{file}\" liigutamine ebaõnnestus",
"{newName} already exists" : "{newName} on juba olemas",
"Could not rename \"{fileName}\"" : "\"{fileName}\" ümbernimetamine ebaõnnestus",
"Could not create file \"{file}\"" : "Faili \"{file}\" loomine ebaõnnestus",
"Error deleting file \"{fileName}\"." : "Tõrge faili \"{fileName}\" kustutamisel.",
"No entries in this folder match '{filter}'" : "Ükski sissekanne ei kattu filtriga '{filter}'",
"Name" : "Nimi",
"Size" : "Suurus",
@ -62,6 +69,7 @@ OC.L10N.register(
"Favorite" : "Lemmik",
"Folder" : "Kaust",
"New folder" : "Uus kaust",
"{newname} already exists" : "{newname} on juba olemas",
"Upload" : "Lae üles",
"An error occurred while trying to update the tags" : "Siltide uuendamisel tekkis tõrge",
"A new file or folder has been <strong>created</strong>" : "Uus fail või kataloog on <strong>loodud</strong>",
@ -96,6 +104,7 @@ OC.L10N.register(
"Currently scanning" : "Praegu skännimisel",
"No favorites" : "Lemmikuid pole",
"Files and folders you mark as favorite will show up here" : "Siin kuvatakse faile ja kaustasid, mille oled märkinud lemmikuteks",
"Text file" : "Tekstifail"
"Text file" : "Tekstifail",
"New text file.txt" : "Uus tekstifail.txt"
},
"nplurals=2; plural=(n != 1);");

View File

@ -32,12 +32,19 @@
"Download" : "Lae alla",
"Rename" : "Nimeta ümber",
"Delete" : "Kustuta",
"Disconnect storage" : "Ühenda andmehoidla lahti.",
"Unshare" : "Lõpeta jagamine",
"Details" : "Üksikasjad",
"Select" : "Vali",
"Pending" : "Ootel",
"Unable to determine date" : "Kuupäeva tuvastamine ei õnnestunud",
"This operation is forbidden" : "See toiming on keelatud",
"This directory is unavailable, please check the logs or contact the administrator" : "See kaust pole saadaval. Palun kontrolli logifaile või võta ühendust administraatoriga",
"Could not move \"{file}\"" : "\"{file}\" liigutamine ebaõnnestus",
"{newName} already exists" : "{newName} on juba olemas",
"Could not rename \"{fileName}\"" : "\"{fileName}\" ümbernimetamine ebaõnnestus",
"Could not create file \"{file}\"" : "Faili \"{file}\" loomine ebaõnnestus",
"Error deleting file \"{fileName}\"." : "Tõrge faili \"{fileName}\" kustutamisel.",
"No entries in this folder match '{filter}'" : "Ükski sissekanne ei kattu filtriga '{filter}'",
"Name" : "Nimi",
"Size" : "Suurus",
@ -60,6 +67,7 @@
"Favorite" : "Lemmik",
"Folder" : "Kaust",
"New folder" : "Uus kaust",
"{newname} already exists" : "{newname} on juba olemas",
"Upload" : "Lae üles",
"An error occurred while trying to update the tags" : "Siltide uuendamisel tekkis tõrge",
"A new file or folder has been <strong>created</strong>" : "Uus fail või kataloog on <strong>loodud</strong>",
@ -94,6 +102,7 @@
"Currently scanning" : "Praegu skännimisel",
"No favorites" : "Lemmikuid pole",
"Files and folders you mark as favorite will show up here" : "Siin kuvatakse faile ja kaustasid, mille oled märkinud lemmikuteks",
"Text file" : "Tekstifail"
"Text file" : "Tekstifail",
"New text file.txt" : "Uus tekstifail.txt"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Deskargatu",
"Rename" : "Berrizendatu",
"Delete" : "Ezabatu",
"Disconnect storage" : "Deskonektatu biltegia",
"Unshare" : "Ez elkarbanatu",
"Details" : "Xehetasunak",
"Select" : "hautatu",
"Pending" : "Zain",

View File

@ -32,6 +32,8 @@
"Download" : "Deskargatu",
"Rename" : "Berrizendatu",
"Delete" : "Ezabatu",
"Disconnect storage" : "Deskonektatu biltegia",
"Unshare" : "Ez elkarbanatu",
"Details" : "Xehetasunak",
"Select" : "hautatu",
"Pending" : "Zain",

View File

@ -34,6 +34,7 @@ OC.L10N.register(
"Download" : "دانلود",
"Rename" : "تغییرنام",
"Delete" : "حذف",
"Unshare" : "لغو اشتراک",
"Details" : "جزئیات",
"Select" : "انتخاب",
"Pending" : "در انتظار",

View File

@ -32,6 +32,7 @@
"Download" : "دانلود",
"Rename" : "تغییرنام",
"Delete" : "حذف",
"Unshare" : "لغو اشتراک",
"Details" : "جزئیات",
"Select" : "انتخاب",
"Pending" : "در انتظار",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Lataa",
"Rename" : "Nimeä uudelleen",
"Delete" : "Poista",
"Disconnect storage" : "Katkaise yhteys tallennustilaan",
"Unshare" : "Peru jakaminen",
"Details" : "Tiedot",
"Select" : "Valitse",
"Pending" : "Odottaa",

View File

@ -32,6 +32,8 @@
"Download" : "Lataa",
"Rename" : "Nimeä uudelleen",
"Delete" : "Poista",
"Disconnect storage" : "Katkaise yhteys tallennustilaan",
"Unshare" : "Peru jakaminen",
"Details" : "Tiedot",
"Select" : "Valitse",
"Pending" : "Odottaa",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Télécharger",
"Rename" : "Renommer",
"Delete" : "Supprimer",
"Disconnect storage" : "Déconnecter ce support de stockage",
"Unshare" : "Ne plus partager",
"Details" : "Détails",
"Select" : "Sélectionner",
"Pending" : "En attente",

View File

@ -32,6 +32,8 @@
"Download" : "Télécharger",
"Rename" : "Renommer",
"Delete" : "Supprimer",
"Disconnect storage" : "Déconnecter ce support de stockage",
"Unshare" : "Ne plus partager",
"Details" : "Détails",
"Select" : "Sélectionner",
"Pending" : "En attente",

View File

@ -10,6 +10,7 @@ OC.L10N.register(
"Close" : "Clauder",
"Download" : "Discargar",
"Delete" : "Deler",
"Unshare" : "Leva compartir",
"Name" : "Nomine",
"Size" : "Dimension",
"Modified" : "Modificate",

View File

@ -8,6 +8,7 @@
"Close" : "Clauder",
"Download" : "Discargar",
"Delete" : "Deler",
"Unshare" : "Leva compartir",
"Name" : "Nomine",
"Size" : "Dimension",
"Modified" : "Modificate",

View File

@ -34,6 +34,8 @@ OC.L10N.register(
"Download" : "Unduh",
"Rename" : "Ubah nama",
"Delete" : "Hapus",
"Disconnect storage" : "Memutuskan penyimpaan",
"Unshare" : "Batalkan berbagi",
"Details" : "Rincian",
"Select" : "Pilih",
"Pending" : "Tertunda",

Some files were not shown because too many files have changed in this diff Show More