summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
m---------3rdparty0
-rw-r--r--apps/files/ajax/list.php26
-rw-r--r--apps/files/lib/helper.php5
-rw-r--r--apps/files/tests/controller/apicontrollertest.php8
-rw-r--r--apps/files/tests/service/tagservice.php2
-rw-r--r--apps/files_encryption/lib/util.php11
-rw-r--r--apps/files_encryption/tests/hooks.php24
-rw-r--r--apps/files_external/lib/owncloud.php4
-rw-r--r--apps/files_external/lib/smb_oc.php14
-rw-r--r--apps/files_external/tests/owncloudfunctions.php8
-rw-r--r--apps/files_sharing/ajax/external.php2
-rw-r--r--apps/files_sharing/api/server2server.php2
-rw-r--r--apps/files_sharing/lib/controllers/sharecontroller.php48
-rw-r--r--apps/files_sharing/lib/external/manager.php113
-rw-r--r--apps/files_sharing/lib/external/storage.php5
-rw-r--r--apps/files_sharing/lib/readonlycache.php27
-rw-r--r--apps/files_sharing/lib/readonlywrapper.php5
-rw-r--r--apps/files_sharing/publicwebdav.php3
-rw-r--r--apps/files_sharing/tests/external/managertest.php216
-rw-r--r--apps/files_sharing/tests/sharedmount.php12
-rw-r--r--apps/files_trashbin/lib/storage.php35
-rw-r--r--apps/files_trashbin/tests/storage.php178
-rw-r--r--apps/provisioning_api/lib/users.php4
-rw-r--r--apps/provisioning_api/tests/userstest.php25
-rw-r--r--apps/user_ldap/lib/connection.php9
-rw-r--r--apps/user_ldap/lib/ldap.php4
-rw-r--r--apps/user_ldap/lib/user/manager.php15
-rw-r--r--apps/user_ldap/tests/user_ldap.php246
-rw-r--r--apps/user_ldap/user_ldap.php16
-rw-r--r--core/ajax/share.php4
-rw-r--r--core/js/oc-dialogs.js2
-rw-r--r--core/js/share.js4
-rw-r--r--core/templates/login.php6
-rw-r--r--cron.php21
-rw-r--r--lib/base.php51
-rw-r--r--lib/private/connector/sabre/maintenanceplugin.php3
-rw-r--r--lib/private/datetimezone.php16
-rw-r--r--lib/private/db/connection.php2
-rw-r--r--lib/private/db/migrator.php4
-rw-r--r--lib/private/files.php8
-rw-r--r--lib/private/files/cache/cache.php11
-rw-r--r--lib/private/files/cache/storage.php2
-rw-r--r--lib/private/files/filesystem.php62
-rw-r--r--lib/private/files/mount/mountpoint.php4
-rw-r--r--lib/private/files/storage/dav.php113
-rw-r--r--lib/private/files/view.php4
-rw-r--r--lib/private/hook.php3
-rw-r--r--lib/private/httphelper.php9
-rw-r--r--lib/private/mail.php7
-rw-r--r--lib/private/servernotavailableexception.php27
-rw-r--r--lib/private/share/mailnotifications.php24
-rw-r--r--lib/private/share/share.php74
-rw-r--r--lib/private/user/http.php8
-rw-r--r--lib/private/user/nouserexception.php14
-rw-r--r--lib/private/util.php19
-rw-r--r--lib/public/appframework/controller.php1
-rw-r--r--lib/public/idatetimezone.php3
-rw-r--r--ocs/v1.php4
-rw-r--r--public.php2
-rw-r--r--tests/lib/db/migrator.php29
-rw-r--r--tests/lib/files/filesystem.php22
-rw-r--r--tests/lib/files/mount/mountpoint.php4
-rw-r--r--tests/lib/preview.php48
-rw-r--r--tests/lib/share/share.php98
-rw-r--r--tests/lib/testcase.php2
-rw-r--r--tests/lib/util.php19
-rw-r--r--version.php4
67 files changed, 1386 insertions, 419 deletions
diff --git a/3rdparty b/3rdparty
-Subproject 70ed7fe3d530d74f0eb501ce6dd3dbe6fe8d81c
+Subproject 4a43dcef48f684e9cd17f740b6f2c67bc9142a0
diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php
index 4aed79d70f7..f9facd0d7f7 100644
--- a/apps/files/ajax/list.php
+++ b/apps/files/ajax/list.php
@@ -22,10 +22,32 @@ try {
$sortAttribute = isset($_GET['sort']) ? $_GET['sort'] : 'name';
$sortDirection = isset($_GET['sortdirection']) ? ($_GET['sortdirection'] === 'desc') : false;
+ $mimetypeFilters = isset($_GET['mimetypes']) ? json_decode($_GET['mimetypes']) : '';
- // make filelist
+ $files = [];
+ // Clean up duplicates from array
+ if (is_array($mimetypeFilters) && count($mimetypeFilters)) {
+ $mimetypeFilters = array_unique($mimetypeFilters);
+
+ if (!in_array('httpd/unix-directory', $mimetypeFilters)) {
+ // append folder filter to be able to browse folders
+ $mimetypeFilters[] = 'httpd/unix-directory';
+ }
+
+ // create filelist with mimetype filter - as getFiles only supports on
+ // mimetype filter at once we will filter this folder for each
+ // mimetypeFilter
+ foreach ($mimetypeFilters as $mimetypeFilter) {
+ $files = array_merge($files, \OCA\Files\Helper::getFiles($dir, $sortAttribute, $sortDirection, $mimetypeFilter));
+ }
+
+ // sort the files accordingly
+ $files = \OCA\Files\Helper::sortFiles($files, $sortAttribute, $sortDirection);
+ } else {
+ // create file list without mimetype filter
+ $files = \OCA\Files\Helper::getFiles($dir, $sortAttribute, $sortDirection);
+ }
- $files = \OCA\Files\Helper::getFiles($dir, $sortAttribute, $sortDirection);
$files = \OCA\Files\Helper::populateTags($files);
$data['directory'] = $dir;
$data['files'] = \OCA\Files\Helper::formatFileInfos($files);
diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php
index 84b1a0f1662..d92e69674f9 100644
--- a/apps/files/lib/helper.php
+++ b/apps/files/lib/helper.php
@@ -170,10 +170,11 @@ class Helper
* @param string $dir path to the directory
* @param string $sortAttribute attribute to sort on
* @param bool $sortDescending true for descending sort, false otherwise
+ * @param string $mimetypeFilter limit returned content to this mimetype or mimepart
* @return \OCP\Files\FileInfo[] files
*/
- public static function getFiles($dir, $sortAttribute = 'name', $sortDescending = false) {
- $content = \OC\Files\Filesystem::getDirectoryContent($dir);
+ public static function getFiles($dir, $sortAttribute = 'name', $sortDescending = false, $mimetypeFilter = '') {
+ $content = \OC\Files\Filesystem::getDirectoryContent($dir, $mimetypeFilter);
return self::sortFiles($content, $sortAttribute, $sortDescending);
}
diff --git a/apps/files/tests/controller/apicontrollertest.php b/apps/files/tests/controller/apicontrollertest.php
index 35df1b6be28..87c57b6b7db 100644
--- a/apps/files/tests/controller/apicontrollertest.php
+++ b/apps/files/tests/controller/apicontrollertest.php
@@ -87,7 +87,7 @@ class ApiControllerTest extends TestCase {
[
'id' => null,
'parentId' => null,
- 'date' => 'January 1, 1970 at 12:00:55 AM GMT+0',
+ 'date' => \OCP\Util::formatDate(55),
'mtime' => 55000,
'icon' => \OCA\Files\Helper::determineIcon($fileInfo),
'name' => 'root.txt',
@@ -152,7 +152,7 @@ class ApiControllerTest extends TestCase {
[
'id' => null,
'parentId' => null,
- 'date' => 'January 1, 1970 at 12:00:55 AM GMT+0',
+ 'date' => \OCP\Util::formatDate(55),
'mtime' => 55000,
'icon' => \OCA\Files\Helper::determineIcon($fileInfo1),
'name' => 'root.txt',
@@ -171,7 +171,7 @@ class ApiControllerTest extends TestCase {
[
'id' => null,
'parentId' => null,
- 'date' => 'January 1, 1970 at 12:16:39 AM GMT+0',
+ 'date' => \OCP\Util::formatDate(999),
'mtime' => 999000,
'icon' => \OCA\Files\Helper::determineIcon($fileInfo2),
'name' => 'root.txt',
@@ -240,4 +240,4 @@ class ApiControllerTest extends TestCase {
$expected = new DataResponse('My error message', Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $this->apiController->updateFileTags('/path.txt', ['Tag1', 'Tag2']));
}
-} \ No newline at end of file
+}
diff --git a/apps/files/tests/service/tagservice.php b/apps/files/tests/service/tagservice.php
index 158dd77e858..322a940ac74 100644
--- a/apps/files/tests/service/tagservice.php
+++ b/apps/files/tests/service/tagservice.php
@@ -65,7 +65,7 @@ class TagServiceTest extends \Test\TestCase {
->withAnyParameters()
->will($this->returnValue($user));
- $this->root = \OC::$server->getUserFolder();
+ $this->root = \OC::$server->getUserFolder($this->user);
$this->tagger = \OC::$server->getTagManager()->load('files');
$this->tagService = new TagService(
diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php
index 14d0a0bc4b9..106ed3b2f08 100644
--- a/apps/files_encryption/lib/util.php
+++ b/apps/files_encryption/lib/util.php
@@ -24,6 +24,7 @@
*/
namespace OCA\Files_Encryption;
+use OC\User\NoUserException;
/**
* Class for utilities relating to encrypted file storage system
@@ -945,8 +946,14 @@ class Util {
) {
return true;
}
- $util = new Util($this->view, $user);
- return $util->ready();
+ try {
+ $util = new Util($this->view, $user);
+ return $util->ready();
+ } catch (NoUserException $e) {
+ \OCP\Util::writeLog('Encryption library',
+ 'No User object for '.$user, \OCP\Util::DEBUG);
+ return false;
+ }
}
/**
diff --git a/apps/files_encryption/tests/hooks.php b/apps/files_encryption/tests/hooks.php
index 7c60024d637..755573e1dbc 100644
--- a/apps/files_encryption/tests/hooks.php
+++ b/apps/files_encryption/tests/hooks.php
@@ -30,6 +30,7 @@ class Hooks extends TestCase {
const TEST_ENCRYPTION_HOOKS_USER1 = "test-encryption-hooks-user1.dot";
const TEST_ENCRYPTION_HOOKS_USER2 = "test-encryption-hooks-user2.dot";
+ const TEST_ENCRYPTION_HOOKS_USER3 = "test-encryption-hooks-user3.dot";
/** @var \OC\Files\View */
public $user1View; // view on /data/user1/files
@@ -91,6 +92,7 @@ class Hooks extends TestCase {
// cleanup test user
\OC_User::deleteUser(self::TEST_ENCRYPTION_HOOKS_USER1);
\OC_User::deleteUser(self::TEST_ENCRYPTION_HOOKS_USER2);
+ \OC_User::deleteUser(self::TEST_ENCRYPTION_HOOKS_USER3);
parent::tearDownAfterClass();
}
@@ -407,31 +409,35 @@ class Hooks extends TestCase {
$view = new \OC\Files\View();
// set user password for the first time
- \OCA\Files_Encryption\Hooks::postCreateUser(array('uid' => 'newUser', 'password' => 'newUserPassword'));
+ \OC_User::createUser(self::TEST_ENCRYPTION_HOOKS_USER3, 'newUserPassword');
+ \OCA\Files_Encryption\Hooks::postCreateUser(array(
+ 'uid' => self::TEST_ENCRYPTION_HOOKS_USER3,
+ 'password' => 'newUserPassword')
+ );
- $this->assertTrue($view->file_exists(\OCA\Files_Encryption\Keymanager::getPublicKeyPath() . '/newUser.publicKey'));
- $this->assertTrue($view->file_exists('newUser/files_encryption/newUser.privateKey'));
+ $this->assertTrue($view->file_exists(\OCA\Files_Encryption\Keymanager::getPublicKeyPath() . '/'.self::TEST_ENCRYPTION_HOOKS_USER3.'.publicKey'));
+ $this->assertTrue($view->file_exists(self::TEST_ENCRYPTION_HOOKS_USER3.'/files_encryption/'.self::TEST_ENCRYPTION_HOOKS_USER3.'.privateKey'));
// check if we are able to decrypt the private key
- $encryptedKey = \OCA\Files_Encryption\Keymanager::getPrivateKey($view, 'newUser');
+ $encryptedKey = \OCA\Files_Encryption\Keymanager::getPrivateKey($view, self::TEST_ENCRYPTION_HOOKS_USER3);
$privateKey = \OCA\Files_Encryption\Crypt::decryptPrivateKey($encryptedKey, 'newUserPassword');
$this->assertTrue(is_string($privateKey));
// change the password before the user logged-in for the first time,
// we can replace the encryption keys
- \OCA\Files_Encryption\Hooks::setPassphrase(array('uid' => 'newUser', 'password' => 'passwordChanged'));
+ \OCA\Files_Encryption\Hooks::setPassphrase(array('uid' => self::TEST_ENCRYPTION_HOOKS_USER3, 'password' => 'passwordChanged'));
- $encryptedKey = \OCA\Files_Encryption\Keymanager::getPrivateKey($view, 'newUser');
+ $encryptedKey = \OCA\Files_Encryption\Keymanager::getPrivateKey($view, self::TEST_ENCRYPTION_HOOKS_USER3);
$privateKey = \OCA\Files_Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged');
$this->assertTrue(is_string($privateKey));
// now create a files folder to simulate a already used account
- $view->mkdir('/newUser/files');
+ $view->mkdir('/'.self::TEST_ENCRYPTION_HOOKS_USER3.'/files');
// change the password after the user logged in, now the password should not change
- \OCA\Files_Encryption\Hooks::setPassphrase(array('uid' => 'newUser', 'password' => 'passwordChanged2'));
+ \OCA\Files_Encryption\Hooks::setPassphrase(array('uid' => self::TEST_ENCRYPTION_HOOKS_USER3, 'password' => 'passwordChanged2'));
- $encryptedKey = \OCA\Files_Encryption\Keymanager::getPrivateKey($view, 'newUser');
+ $encryptedKey = \OCA\Files_Encryption\Keymanager::getPrivateKey($view, self::TEST_ENCRYPTION_HOOKS_USER3);
$privateKey = \OCA\Files_Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged2');
$this->assertFalse($privateKey);
diff --git a/apps/files_external/lib/owncloud.php b/apps/files_external/lib/owncloud.php
index 04a1e959eb0..7d452e8ff4d 100644
--- a/apps/files_external/lib/owncloud.php
+++ b/apps/files_external/lib/owncloud.php
@@ -37,13 +37,13 @@ class OwnCloud extends \OC\Files\Storage\DAV{
$host = substr($host, 0, $hostSlashPos);
}
- if (substr($contextPath , 1) !== '/'){
+ if (substr($contextPath, -1) !== '/'){
$contextPath .= '/';
}
if (isset($params['root'])){
$root = $params['root'];
- if (substr($root, 1) !== '/'){
+ if (substr($root, 0, 1) !== '/'){
$root = '/' . $root;
}
}
diff --git a/apps/files_external/lib/smb_oc.php b/apps/files_external/lib/smb_oc.php
index a7c93d97fd1..5e9442746f8 100644
--- a/apps/files_external/lib/smb_oc.php
+++ b/apps/files_external/lib/smb_oc.php
@@ -18,13 +18,19 @@ class SMB_OC extends \OC\Files\Storage\SMB {
* @throws \Exception
*/
public function __construct($params) {
- if (isset($params['host']) && \OC::$server->getSession()->exists('smb-credentials')) {
+ if (isset($params['host'])) {
$host=$params['host'];
$this->username_as_share = ($params['username_as_share'] === 'true');
- $params_auth = json_decode(\OC::$server->getCrypto()->decrypt(\OC::$server->getSession()->get('smb-credentials')), true);
- $user = \OC::$server->getSession()->get('loginname');
- $password = $params_auth['password'];
+ $user = 'foo';
+ $password = 'bar';
+ if (\OC::$server->getSession()->exists('smb-credentials')) {
+ $params_auth = json_decode(\OC::$server->getCrypto()->decrypt(\OC::$server->getSession()->get('smb-credentials')), true);
+ $user = \OC::$server->getSession()->get('loginname');
+ $password = $params_auth['password'];
+ } else {
+ // assume we are testing from the admin section
+ }
$root=isset($params['root'])?$params['root']:'/';
$share = '';
diff --git a/apps/files_external/tests/owncloudfunctions.php b/apps/files_external/tests/owncloudfunctions.php
index 8232f30a5e2..ca9a8b231f1 100644
--- a/apps/files_external/tests/owncloudfunctions.php
+++ b/apps/files_external/tests/owncloudfunctions.php
@@ -68,6 +68,14 @@ class OwnCloudFunctions extends \Test\TestCase {
),
'http://testhost/testroot/remote.php/webdav/subdir/',
),
+ array(
+ array(
+ 'host' => 'http://testhost/testroot/',
+ 'root' => '/subdir',
+ 'secure' => false
+ ),
+ 'http://testhost/testroot/remote.php/webdav/subdir/',
+ ),
);
}
diff --git a/apps/files_sharing/ajax/external.php b/apps/files_sharing/ajax/external.php
index 30c1f38801e..153285e11ff 100644
--- a/apps/files_sharing/ajax/external.php
+++ b/apps/files_sharing/ajax/external.php
@@ -38,8 +38,6 @@ $externalManager = new \OCA\Files_Sharing\External\Manager(
\OC::$server->getUserSession()->getUser()->getUID()
);
-$name = OCP\Files::buildNotExistingFileName('/', $name);
-
// check for ssl cert
if (substr($remote, 0, 5) === 'https' and !OC_Util::getUrlContent($remote)) {
\OCP\JSON::error(array('data' => array('message' => $l->t('Invalid or untrusted SSL certificate'))));
diff --git a/apps/files_sharing/api/server2server.php b/apps/files_sharing/api/server2server.php
index f2f7561598f..89a0262481c 100644
--- a/apps/files_sharing/api/server2server.php
+++ b/apps/files_sharing/api/server2server.php
@@ -64,8 +64,6 @@ class Server2Server {
$shareWith
);
- $name = \OCP\Files::buildNotExistingFileName('/', $name);
-
try {
$externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId);
diff --git a/apps/files_sharing/lib/controllers/sharecontroller.php b/apps/files_sharing/lib/controllers/sharecontroller.php
index b224b7dc108..c89e005b95f 100644
--- a/apps/files_sharing/lib/controllers/sharecontroller.php
+++ b/apps/files_sharing/lib/controllers/sharecontroller.php
@@ -225,26 +225,48 @@ class ShareController extends Controller {
}
}
+ $files_list = null;
+ if (!is_null($files)) { // download selected files
+ $files_list = json_decode($files);
+ // in case we get only a single file
+ if ($files_list === null) {
+ $files_list = array($files);
+ }
+ }
+
$originalSharePath = self::getPath($token);
+ // Create the activities
if (isset($originalSharePath) && Filesystem::isReadable($originalSharePath . $path)) {
$originalSharePath = Filesystem::normalizePath($originalSharePath . $path);
- $type = \OC\Files\Filesystem::is_dir($originalSharePath) ? 'folder' : 'file';
- $args = $type === 'folder' ? array('dir' => $originalSharePath) : array('dir' => dirname($originalSharePath), 'scrollto' => basename($originalSharePath));
- $linkToFile = \OCP\Util::linkToAbsolute('files', 'index.php', $args);
- $subject = $type === 'folder' ? Activity::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED : Activity::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
- $this->activityManager->publishActivity(
- 'files_sharing', $subject, array($originalSharePath), '', array(), $originalSharePath,
- $linkToFile, $linkItem['uid_owner'], Activity::TYPE_PUBLIC_LINKS, Activity::PRIORITY_MEDIUM);
- }
+ $isDir = \OC\Files\Filesystem::is_dir($originalSharePath);
+
+ $activities = [];
+ if (!$isDir) {
+ // Single file public share
+ $activities[$originalSharePath] = Activity::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
+ } else if (!empty($files_list)) {
+ // Only some files are downloaded
+ foreach ($files_list as $file) {
+ $filePath = Filesystem::normalizePath($originalSharePath . '/' . $file);
+ $isDir = \OC\Files\Filesystem::is_dir($filePath);
+ $activities[$filePath] = ($isDir) ? Activity::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED : Activity::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
+ }
+ } else {
+ // The folder is downloaded
+ $activities[$originalSharePath] = Activity::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
+ }
- if (!is_null($files)) { // download selected files
- $files_list = json_decode($files);
- // in case we get only a single file
- if ($files_list === NULL) {
- $files_list = array($files);
+ foreach ($activities as $filePath => $subject) {
+ $this->activityManager->publishActivity(
+ 'files_sharing', $subject, array($filePath), '', array(),
+ $filePath, '', $linkItem['uid_owner'], Activity::TYPE_PUBLIC_LINKS, Activity::PRIORITY_MEDIUM
+ );
}
+ }
+ // download selected files
+ if (!is_null($files)) {
// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
// after dispatching the request which results in a "Cannot modify header information" notice.
OC_Files::get($originalSharePath, $files_list, $_SERVER['REQUEST_METHOD'] == 'HEAD');
diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php
index 8985aeb3fce..aef7654d382 100644
--- a/apps/files_sharing/lib/external/manager.php
+++ b/apps/files_sharing/lib/external/manager.php
@@ -9,6 +9,7 @@
namespace OCA\Files_Sharing\External;
use OC\Files\Filesystem;
+use OCP\Files;
class Manager {
const STORAGE = '\OCA\Files_Sharing\External\Storage';
@@ -29,7 +30,7 @@ class Manager {
private $mountManager;
/**
- * @var \OC\Files\Storage\StorageFactory
+ * @var \OCP\Files\Storage\IStorageFactory
*/
private $storageLoader;
@@ -41,12 +42,12 @@ class Manager {
/**
* @param \OCP\IDBConnection $connection
* @param \OC\Files\Mount\Manager $mountManager
- * @param \OC\Files\Storage\StorageFactory $storageLoader
+ * @param \OCP\Files\Storage\IStorageFactory $storageLoader
* @param \OC\HTTPHelper $httpHelper
* @param string $uid
*/
public function __construct(\OCP\IDBConnection $connection, \OC\Files\Mount\Manager $mountManager,
- \OC\Files\Storage\StorageFactory $storageLoader, \OC\HTTPHelper $httpHelper, $uid) {
+ \OCP\Files\Storage\IStorageFactory $storageLoader, \OC\HTTPHelper $httpHelper, $uid) {
$this->connection = $connection;
$this->mountManager = $mountManager;
$this->storageLoader = $storageLoader;
@@ -65,33 +66,64 @@ class Manager {
* @param boolean $accepted
* @param string $user
* @param int $remoteId
- * @return mixed
+ * @return Mount|null
*/
public function addShare($remote, $token, $password, $name, $owner, $accepted=false, $user = null, $remoteId = -1) {
$user = $user ? $user : $this->uid;
$accepted = $accepted ? 1 : 0;
+ $name = Filesystem::normalizePath('/' . $name);
+
+ if (!$accepted) {
+ // To avoid conflicts with the mount point generation later,
+ // we only use a temporary mount point name here. The real
+ // mount point name will be generated when accepting the share,
+ // using the original share item name.
+ $tmpMountPointName = '{{TemporaryMountPointName#' . $name . '}}';
+ $mountPoint = $tmpMountPointName;
+ $hash = md5($tmpMountPointName);
+ $data = [
+ 'remote' => $remote,
+ 'share_token' => $token,
+ 'password' => $password,
+ 'name' => $name,
+ 'owner' => $owner,
+ 'user' => $user,
+ 'mountpoint' => $mountPoint,
+ 'mountpoint_hash' => $hash,
+ 'accepted' => $accepted,
+ 'remote_id' => $remoteId,
+ ];
+
+ $i = 1;
+ while (!$this->connection->insertIfNotExist('*PREFIX*share_external', $data, ['user', 'mountpoint_hash'])) {
+ // The external share already exists for the user
+ $data['mountpoint'] = $tmpMountPointName . '-' . $i;
+ $data['mountpoint_hash'] = md5($data['mountpoint']);
+ $i++;
+ }
+ return null;
+ }
- $mountPoint = Filesystem::normalizePath('/' . $name);
+ $mountPoint = Files::buildNotExistingFileName('/', $name);
+ $mountPoint = Filesystem::normalizePath('/' . $mountPoint);
+ $hash = md5($mountPoint);
$query = $this->connection->prepare('
INSERT INTO `*PREFIX*share_external`
(`remote`, `share_token`, `password`, `name`, `owner`, `user`, `mountpoint`, `mountpoint_hash`, `accepted`, `remote_id`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
');
- $hash = md5($mountPoint);
$query->execute(array($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId));
- if ($accepted) {
- $options = array(
- 'remote' => $remote,
- 'token' => $token,
- 'password' => $password,
- 'mountpoint' => $mountPoint,
- 'owner' => $owner
- );
- return $this->mountShare($options);
- }
+ $options = array(
+ 'remote' => $remote,
+ 'token' => $token,
+ 'password' => $password,
+ 'mountpoint' => $mountPoint,
+ 'owner' => $owner
+ );
+ return $this->mountShare($options);
}
private function setupMounts() {
@@ -124,7 +156,7 @@ class Manager {
*/
private function getShare($id) {
$getShare = $this->connection->prepare('
- SELECT `remote`, `share_token`
+ SELECT `remote`, `remote_id`, `share_token`, `name`
FROM `*PREFIX*share_external`
WHERE `id` = ? AND `user` = ?');
$result = $getShare->execute(array($id, $this->uid));
@@ -142,12 +174,18 @@ class Manager {
$share = $this->getShare($id);
if ($share) {
+ $mountPoint = Files::buildNotExistingFileName('/', $share['name']);
+ $mountPoint = Filesystem::normalizePath('/' . $mountPoint);
+ $hash = md5($mountPoint);
+
$acceptShare = $this->connection->prepare('
UPDATE `*PREFIX*share_external`
- SET `accepted` = ?
+ SET `accepted` = ?,
+ `mountpoint` = ?,
+ `mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
- $acceptShare->execute(array(1, $id, $this->uid));
- $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $id, 'accept');
+ $acceptShare->execute(array(1, $mountPoint, $hash, $id, $this->uid));
+ $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
}
}
@@ -164,7 +202,7 @@ class Manager {
$removeShare = $this->connection->prepare('
DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?');
$removeShare->execute(array($id, $this->uid));
- $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $id, 'decline');
+ $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
}
}
@@ -173,13 +211,13 @@ class Manager {
*
* @param string $remote
* @param string $token
- * @param int $id
+ * @param int $remoteId Share id on the remote host
* @param string $feedback
* @return boolean
*/
- private function sendFeedbackToRemote($remote, $token, $id, $feedback) {
+ private function sendFeedbackToRemote($remote, $token, $remoteId, $feedback) {
- $url = $remote . \OCP\Share::BASE_PATH_TO_SHARE_API . '/' . $id . '/' . $feedback . '?format=' . \OCP\Share::RESPONSE_FORMAT;
+ $url = rtrim($remote, '/') . \OCP\Share::BASE_PATH_TO_SHARE_API . '/' . $remoteId . '/' . $feedback . '?format=' . \OCP\Share::RESPONSE_FORMAT;
$fields = array('token' => $token);
$result = $this->httpHelper->post($url, $fields);
@@ -315,10 +353,29 @@ class Manager {
* @return array list of open server-to-server shares
*/
public function getOpenShares() {
- $openShares = $this->connection->prepare('SELECT * FROM `*PREFIX*share_external` WHERE `accepted` = ? AND `user` = ?');
- $result = $openShares->execute(array(0, $this->uid));
+ return $this->getShares(false);
+ }
+
+ /**
+ * return a list of shares for the user
+ *
+ * @param bool|null $accepted True for accepted only,
+ * false for not accepted,
+ * null for all shares of the user
+ * @return array list of open server-to-server shares
+ */
+ private function getShares($accepted) {
+ $query = 'SELECT * FROM `*PREFIX*share_external` WHERE `user` = ?';
+ $parameters = [$this->uid];
+ if (!is_null($accepted)) {
+ $query .= ' AND `accepted` = ?';
+ $parameters[] = (int) $accepted;
+ }
+ $query .= ' ORDER BY `id` ASC';
- return $result ? $openShares->fetchAll() : array();
+ $shares = $this->connection->prepare($query);
+ $result = $shares->execute($parameters);
+ return $result ? $shares->fetchAll() : [];
}
-} \ No newline at end of file
+}
diff --git a/apps/files_sharing/lib/external/storage.php b/apps/files_sharing/lib/external/storage.php
index 648376e8cb5..bcd93f5de3f 100644
--- a/apps/files_sharing/lib/external/storage.php
+++ b/apps/files_sharing/lib/external/storage.php
@@ -70,7 +70,7 @@ class Storage extends DAV implements ISharedStorage {
'host' => $host,
'root' => $root,
'user' => $options['token'],
- 'password' => $options['password']
+ 'password' => (string)$options['password']
));
}
@@ -237,7 +237,8 @@ class Storage extends DAV implements ISharedStorage {
$errorMessage = curl_error($ch);
curl_close($ch);
if (!empty($errorMessage)) {
- throw new \Exception($errorMessage);
+ \OCP\Util::writeLog('files_sharing', 'Error getting remote share info: ' . $errorMessage, \OCP\Util::ERROR);
+ throw new StorageNotAvailableException($errorMessage);
}
switch ($status) {
diff --git a/apps/files_sharing/lib/readonlycache.php b/apps/files_sharing/lib/readonlycache.php
deleted file mode 100644
index 6dd3b9cf61d..00000000000
--- a/apps/files_sharing/lib/readonlycache.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-/**
- * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-namespace OCA\Files_Sharing;
-
-use OC\Files\Cache\Cache;
-
-class ReadOnlyCache extends Cache {
- public function get($path) {
- $data = parent::get($path);
- $data['permissions'] &= (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_SHARE);
- return $data;
- }
-
- public function getFolderContents($path) {
- $content = parent::getFolderContents($path);
- foreach ($content as &$data) {
- $data['permissions'] &= (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_SHARE);
- }
- return $content;
- }
-}
diff --git a/apps/files_sharing/lib/readonlywrapper.php b/apps/files_sharing/lib/readonlywrapper.php
index 58a5695aff8..72377516fc0 100644
--- a/apps/files_sharing/lib/readonlywrapper.php
+++ b/apps/files_sharing/lib/readonlywrapper.php
@@ -8,7 +8,9 @@
namespace OCA\Files_Sharing;
+use OC\Files\Cache\Wrapper\CachePermissionsMask;
use OC\Files\Storage\Wrapper\Wrapper;
+use OCP\Constants;
class ReadOnlyWrapper extends Wrapper {
public function isUpdatable($path) {
@@ -51,6 +53,7 @@ class ReadOnlyWrapper extends Wrapper {
if (!$storage) {
$storage = $this;
}
- return new ReadOnlyCache($storage);
+ $sourceCache = $this->storage->getCache($path, $storage);
+ return new CachePermissionsMask($sourceCache, Constants::PERMISSION_READ | Constants::PERMISSION_SHARE);
}
}
diff --git a/apps/files_sharing/publicwebdav.php b/apps/files_sharing/publicwebdav.php
index 240891ffef6..f9b47bad565 100644
--- a/apps/files_sharing/publicwebdav.php
+++ b/apps/files_sharing/publicwebdav.php
@@ -40,7 +40,8 @@ $server->addPlugin(new OC_Connector_Sabre_ExceptionLoggerPlugin('webdav'));
// wait with registering these until auth is handled and the filesystem is setup
$server->subscribeEvent('beforeMethod', function () use ($server, $objectTree, $authBackend) {
$share = $authBackend->getShare();
- $owner = $share['uid_owner'];
+ $rootShare = \OCP\Share::resolveReShare($share);
+ $owner = $rootShare['uid_owner'];
$isWritable = $share['permissions'] & (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_CREATE);
$fileId = $share['file_source'];
diff --git a/apps/files_sharing/tests/external/managertest.php b/apps/files_sharing/tests/external/managertest.php
new file mode 100644
index 00000000000..33a6465cf82
--- /dev/null
+++ b/apps/files_sharing/tests/external/managertest.php
@@ -0,0 +1,216 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Joas Schilling
+ * @copyright 2015 Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Sharing\Tests\External;
+
+use OC\Files\Storage\StorageFactory;
+use OCA\Files_Sharing\External\Manager;
+use OCA\Files_Sharing\Tests\TestCase;
+
+class ManagerTest extends TestCase {
+
+ /** @var Manager **/
+ private $manager;
+
+ /** @var \OC\Files\Mount\Manager */
+ private $mountManager;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ private $httpHelper;
+
+ private $uid;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->uid = $this->getUniqueID('user');
+ $this->mountManager = new \OC\Files\Mount\Manager();
+ $this->httpHelper = $httpHelper = $this->getMockBuilder('\OC\HTTPHelper')->disableOriginalConstructor()->getMock();
+ /** @var \OC\HTTPHelper $httpHelper */
+ $this->manager = new Manager(
+ \OC::$server->getDatabaseConnection(),
+ $this->mountManager,
+ new StorageFactory(),
+ $httpHelper,
+ $this->uid
+ );
+ }
+
+ public function testAddShare() {
+
+ $shareData1 = [
+ 'remote' => 'http://localhost',
+ 'token' => 'token1',
+ 'password' => '',
+ 'name' => '/SharedFolder',
+ 'owner' => 'foobar',
+ 'accepted' => false,
+ 'user' => $this->uid,
+ ];
+ $shareData2 = $shareData1;
+ $shareData2['token'] = 'token2';
+ $shareData3 = $shareData1;
+ $shareData3['token'] = 'token3';
+
+ // Add a share for "user"
+ $this->assertSame(null, call_user_func_array([$this->manager, 'addShare'], $shareData1));
+ $openShares = $this->manager->getOpenShares();
+ $this->assertCount(1, $openShares);
+ $this->assertExternalShareEntry($shareData1, $openShares[0], 1, '{{TemporaryMountPointName#' . $shareData1['name'] . '}}');
+
+ \Test_Helper::invokePrivate($this->manager, 'setupMounts');
+ $this->assertNotMount('SharedFolder');
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}');
+
+ // Add a second share for "user" with the same name
+ $this->assertSame(null, call_user_func_array([$this->manager, 'addShare'], $shareData2));
+ $openShares = $this->manager->getOpenShares();
+ $this->assertCount(2, $openShares);
+ $this->assertExternalShareEntry($shareData1, $openShares[0], 1, '{{TemporaryMountPointName#' . $shareData1['name'] . '}}');
+ // New share falls back to "-1" appendix, because the name is already taken
+ $this->assertExternalShareEntry($shareData2, $openShares[1], 2, '{{TemporaryMountPointName#' . $shareData2['name'] . '}}-1');
+
+ \Test_Helper::invokePrivate($this->manager, 'setupMounts');
+ $this->assertNotMount('SharedFolder');
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}');
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}-1');
+
+ $this->httpHelper->expects($this->at(0))
+ ->method('post')
+ ->with($this->stringStartsWith('http://localhost/ocs/v1.php/cloud/shares/' . $openShares[0]['remote_id']), $this->anything());
+
+ // Accept the first share
+ $this->manager->acceptShare($openShares[0]['id']);
+
+ // Check remaining shares - Accepted
+ $acceptedShares = \Test_Helper::invokePrivate($this->manager, 'getShares', [true]);
+ $this->assertCount(1, $acceptedShares);
+ $shareData1['accepted'] = true;
+ $this->assertExternalShareEntry($shareData1, $acceptedShares[0], 1, $shareData1['name']);
+ // Check remaining shares - Open
+ $openShares = $this->manager->getOpenShares();
+ $this->assertCount(1, $openShares);
+ $this->assertExternalShareEntry($shareData2, $openShares[0], 2, '{{TemporaryMountPointName#' . $shareData2['name'] . '}}-1');
+
+ \Test_Helper::invokePrivate($this->manager, 'setupMounts');
+ $this->assertMount($shareData1['name']);
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}');
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}-1');
+
+ // Add another share for "user" with the same name
+ $this->assertSame(null, call_user_func_array([$this->manager, 'addShare'], $shareData3));
+ $openShares = $this->manager->getOpenShares();
+ $this->assertCount(2, $openShares);
+ $this->assertExternalShareEntry($shareData2, $openShares[0], 2, '{{TemporaryMountPointName#' . $shareData2['name'] . '}}-1');
+ // New share falls back to the original name (no "-\d", because the name is not taken)
+ $this->assertExternalShareEntry($shareData3, $openShares[1], 3, '{{TemporaryMountPointName#' . $shareData3['name'] . '}}');
+
+ \Test_Helper::invokePrivate($this->manager, 'setupMounts');
+ $this->assertMount($shareData1['name']);
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}');
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}-1');
+
+ $this->httpHelper->expects($this->at(0))
+ ->method('post')
+ ->with($this->stringStartsWith('http://localhost/ocs/v1.php/cloud/shares/' . $openShares[1]['remote_id'] . '/decline'), $this->anything());
+
+ // Decline the third share
+ $this->manager->declineShare($openShares[1]['id']);
+
+ \Test_Helper::invokePrivate($this->manager, 'setupMounts');
+ $this->assertMount($shareData1['name']);
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}');
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}-1');
+
+ // Check remaining shares - Accepted
+ $acceptedShares = \Test_Helper::invokePrivate($this->manager, 'getShares', [true]);
+ $this->assertCount(1, $acceptedShares);
+ $shareData1['accepted'] = true;
+ $this->assertExternalShareEntry($shareData1, $acceptedShares[0], 1, $shareData1['name']);
+ // Check remaining shares - Open
+ $openShares = $this->manager->getOpenShares();
+ $this->assertCount(1, $openShares);
+ $this->assertExternalShareEntry($shareData2, $openShares[0], 2, '{{TemporaryMountPointName#' . $shareData2['name'] . '}}-1');
+
+ \Test_Helper::invokePrivate($this->manager, 'setupMounts');
+ $this->assertMount($shareData1['name']);
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}');
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}-1');
+
+ $this->httpHelper->expects($this->at(0))
+ ->method('post')
+ ->with($this->stringStartsWith('http://localhost/ocs/v1.php/cloud/shares/' . $openShares[0]['remote_id'] . '/decline'), $this->anything());
+ $this->httpHelper->expects($this->at(1))
+ ->method('post')
+ ->with($this->stringStartsWith('http://localhost/ocs/v1.php/cloud/shares/' . $acceptedShares[0]['remote_id'] . '/decline'), $this->anything());
+
+ $this->manager->removeUserShares($this->uid);
+ $this->assertEmpty(\Test_Helper::invokePrivate($this->manager, 'getShares', [null]), 'Asserting all shares for the user have been deleted');
+
+ $this->mountManager->clear();
+ \Test_Helper::invokePrivate($this->manager, 'setupMounts');
+ $this->assertNotMount($shareData1['name']);
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}');
+ $this->assertNotMount('{{TemporaryMountPointName#' . $shareData1['name'] . '}}-1');
+ }
+
+ /**
+ * @param array $expected
+ * @param array $actual
+ * @param int $share
+ * @param string $mountPoint
+ */
+ protected function assertExternalShareEntry($expected, $actual, $share, $mountPoint) {
+ $this->assertEquals($expected['remote'], $actual['remote'], 'Asserting remote of a share #' . $share);
+ $this->assertEquals($expected['token'], $actual['share_token'], 'Asserting token of a share #' . $share);
+ $this->assertEquals($expected['name'], $actual['name'], 'Asserting name of a share #' . $share);
+ $this->assertEquals($expected['owner'], $actual['owner'], 'Asserting owner of a share #' . $share);
+ $this->assertEquals($expected['accepted'], (int) $actual['accepted'], 'Asserting accept of a share #' . $share);
+ $this->assertEquals($expected['user'], $actual['user'], 'Asserting user of a share #' . $share);
+ $this->assertEquals($mountPoint, $actual['mountpoint'], 'Asserting mountpoint of a share #' . $share);
+ }
+
+ private function assertMount($mountPoint) {
+ $mountPoint = rtrim($mountPoint, '/');
+ $mount = $this->mountManager->find($this->getFullPath($mountPoint));
+ $this->assertInstanceOf('\OCA\Files_Sharing\External\Mount', $mount);
+ $this->assertInstanceOf('\OCP\Files\Mount\IMountPoint', $mount);
+ $this->assertEquals($this->getFullPath($mountPoint), rtrim($mount->getMountPoint(), '/'));
+ $storage = $mount->getStorage();
+ $this->assertInstanceOf('\OCA\Files_Sharing\External\Storage', $storage);
+ }
+
+ private function assertNotMount($mountPoint) {
+ $mountPoint = rtrim($mountPoint, '/');
+ $mount = $this->mountManager->find($this->getFullPath($mountPoint));
+ if ($mount) {
+ $this->assertInstanceOf('\OCP\Files\Mount\IMountPoint', $mount);
+ $this->assertNotEquals($this->getFullPath($mountPoint), rtrim($mount->getMountPoint(), '/'));
+ } else {
+ $this->assertNull($mount);
+ }
+ }
+
+ private function getFullPath($path) {
+ return '/' . $this->uid . '/files' . $path;
+ }
+}
diff --git a/apps/files_sharing/tests/sharedmount.php b/apps/files_sharing/tests/sharedmount.php
index 715c22cf4ae..33fee2bd03e 100644
--- a/apps/files_sharing/tests/sharedmount.php
+++ b/apps/files_sharing/tests/sharedmount.php
@@ -141,14 +141,20 @@ class Test_Files_Sharing_Mount extends OCA\Files_sharing\Tests\TestCase {
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
- \OC\Files\Filesystem::rename($this->filename, "newFileName");
+ \OC\Files\Filesystem::rename($this->filename, $this->filename . '_renamed');
- $this->assertTrue(\OC\Files\Filesystem::file_exists('newFileName'));
+ $this->assertTrue(\OC\Files\Filesystem::file_exists($this->filename . '_renamed'));
$this->assertFalse(\OC\Files\Filesystem::file_exists($this->filename));
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
$this->assertTrue(\OC\Files\Filesystem::file_exists($this->filename));
- $this->assertFalse(\OC\Files\Filesystem::file_exists("newFileName"));
+ $this->assertFalse(\OC\Files\Filesystem::file_exists($this->filename . '_renamed'));
+
+ // rename back to original name
+ self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
+ \OC\Files\Filesystem::rename($this->filename . '_renamed', $this->filename);
+ $this->assertFalse(\OC\Files\Filesystem::file_exists($this->filename . '_renamed'));
+ $this->assertTrue(\OC\Files\Filesystem::file_exists($this->filename));
//cleanup
\OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
diff --git a/apps/files_trashbin/lib/storage.php b/apps/files_trashbin/lib/storage.php
index d15b136924c..bb2d18d5318 100644
--- a/apps/files_trashbin/lib/storage.php
+++ b/apps/files_trashbin/lib/storage.php
@@ -80,11 +80,36 @@ class Storage extends Wrapper {
/**
* Deletes the given file by moving it into the trashbin.
*
- * @param string $path
+ * @param string $path path of file or folder to delete
+ *
+ * @return bool true if the operation succeeded, false otherwise
*/
public function unlink($path) {
+ return $this->doDelete($path, 'unlink');
+ }
+
+ /**
+ * Deletes the given folder by moving it into the trashbin.
+ *
+ * @param string $path path of folder to delete
+ *
+ * @return bool true if the operation succeeded, false otherwise
+ */
+ public function rmdir($path) {
+ return $this->doDelete($path, 'rmdir');
+ }
+
+ /**
+ * Run the delete operation with the given method
+ *
+ * @param string $path path of file or folder to delete
+ * @param string $method either "unlink" or "rmdir"
+ *
+ * @return bool true if the operation succeeded, false otherwise
+ */
+ private function doDelete($path, $method) {
if (self::$disableTrash) {
- return $this->storage->unlink($path);
+ return call_user_func_array([$this->storage, $method], [$path]);
}
$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path);
$result = true;
@@ -97,14 +122,14 @@ class Storage extends Wrapper {
// in cross-storage cases the file will be copied
// but not deleted, so we delete it here
if ($result) {
- $this->storage->unlink($path);
+ call_user_func_array([$this->storage, $method], [$path]);
}
} else {
- $result = $this->storage->unlink($path);
+ $result = call_user_func_array([$this->storage, $method], [$path]);
}
unset($this->deletedFiles[$normalized]);
} else if ($this->storage->file_exists($path)) {
- $result = $this->storage->unlink($path);
+ $result = call_user_func_array([$this->storage, $method], [$path]);
}
return $result;
diff --git a/apps/files_trashbin/tests/storage.php b/apps/files_trashbin/tests/storage.php
index 24a04e68b2a..72103862ed3 100644
--- a/apps/files_trashbin/tests/storage.php
+++ b/apps/files_trashbin/tests/storage.php
@@ -54,6 +54,8 @@ class Storage extends \Test\TestCase {
$this->userView = new \OC\Files\View('/' . $this->user . '/files/');
$this->userView->file_put_contents('test.txt', 'foo');
+ $this->userView->mkdir('folder');
+ $this->userView->file_put_contents('folder/inside.txt', 'bar');
}
protected function tearDown() {
@@ -68,7 +70,7 @@ class Storage extends \Test\TestCase {
/**
* Test that deleting a file puts it into the trashbin.
*/
- public function testSingleStorageDelete() {
+ public function testSingleStorageDeleteFile() {
$this->assertTrue($this->userView->file_exists('test.txt'));
$this->userView->unlink('test.txt');
list($storage,) = $this->userView->resolvePath('test.txt');
@@ -83,12 +85,34 @@ class Storage extends \Test\TestCase {
}
/**
+ * Test that deleting a folder puts it into the trashbin.
+ */
+ public function testSingleStorageDeleteFolder() {
+ $this->assertTrue($this->userView->file_exists('folder/inside.txt'));
+ $this->userView->rmdir('folder');
+ list($storage,) = $this->userView->resolvePath('folder/inside.txt');
+ $storage->getScanner()->scan(''); // make sure we check the storage
+ $this->assertFalse($this->userView->getFileInfo('folder'));
+
+ // check if folder is in trashbin
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
+ $this->assertEquals(1, count($results));
+ $name = $results[0]->getName();
+ $this->assertEquals('folder', substr($name, 0, strrpos($name, '.')));
+
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/' . $name . '/');
+ $this->assertEquals(1, count($results));
+ $name = $results[0]->getName();
+ $this->assertEquals('inside.txt', $name);
+ }
+
+ /**
* Test that deleting a file from another mounted storage properly
* lands in the trashbin. This is a cross-storage situation because
* the trashbin folder is in the root storage while the mounted one
* isn't.
*/
- public function testCrossStorageDelete() {
+ public function testCrossStorageDeleteFile() {
$storage2 = new Temporary(array());
\OC\Files\Filesystem::mount($storage2, array(), $this->user . '/files/substorage');
@@ -109,9 +133,41 @@ class Storage extends \Test\TestCase {
}
/**
+ * Test that deleting a folder from another mounted storage properly
+ * lands in the trashbin. This is a cross-storage situation because
+ * the trashbin folder is in the root storage while the mounted one
+ * isn't.
+ */
+ public function testCrossStorageDeleteFolder() {
+ $storage2 = new Temporary(array());
+ \OC\Files\Filesystem::mount($storage2, array(), $this->user . '/files/substorage');
+
+ $this->userView->mkdir('substorage/folder');
+ $this->userView->file_put_contents('substorage/folder/subfile.txt', 'bar');
+ $storage2->getScanner()->scan('');
+ $this->assertTrue($storage2->file_exists('folder/subfile.txt'));
+ $this->userView->rmdir('substorage/folder');
+
+ $storage2->getScanner()->scan('');
+ $this->assertFalse($this->userView->getFileInfo('substorage/folder'));
+ $this->assertFalse($storage2->file_exists('folder/subfile.txt'));
+
+ // check if folder is in trashbin
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
+ $this->assertEquals(1, count($results));
+ $name = $results[0]->getName();
+ $this->assertEquals('folder', substr($name, 0, strrpos($name, '.')));
+
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/' . $name . '/');
+ $this->assertEquals(1, count($results));
+ $name = $results[0]->getName();
+ $this->assertEquals('subfile.txt', $name);
+ }
+
+ /**
* Test that deleted versions properly land in the trashbin.
*/
- public function testDeleteVersions() {
+ public function testDeleteVersionsOfFile() {
\OCA\Files_Versions\Hooks::connectHooks();
// trigger a version (multiple would not work because of the expire logic)
@@ -130,7 +186,38 @@ class Storage extends \Test\TestCase {
$results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions');
$this->assertEquals(1, count($results));
$name = $results[0]->getName();
- $this->assertEquals('test.txt', substr($name, 0, strlen('test.txt')));
+ $this->assertEquals('test.txt.v', substr($name, 0, strlen('test.txt.v')));
+ }
+
+ /**
+ * Test that deleted versions properly land in the trashbin.
+ */
+ public function testDeleteVersionsOfFolder() {
+ \OCA\Files_Versions\Hooks::connectHooks();
+
+ // trigger a version (multiple would not work because of the expire logic)
+ $this->userView->file_put_contents('folder/inside.txt', 'v1');
+
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/folder/');
+ $this->assertEquals(1, count($results));
+
+ $this->userView->rmdir('folder');
+
+ // rescan trash storage
+ list($rootStorage,) = $this->rootView->resolvePath($this->user . '/files_trashbin');
+ $rootStorage->getScanner()->scan('');
+
+ // check if versions are in trashbin
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions');
+ $this->assertEquals(1, count($results));
+ $name = $results[0]->getName();
+ $this->assertEquals('folder.d', substr($name, 0, strlen('folder.d')));
+
+ // check if versions are in trashbin
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/' . $name . '/');
+ $this->assertEquals(1, count($results));
+ $name = $results[0]->getName();
+ $this->assertEquals('inside.txt.v', substr($name, 0, strlen('inside.txt.v')));
}
/**
@@ -138,7 +225,7 @@ class Storage extends \Test\TestCase {
* storages. This is because rename() between storages would call
* unlink() which should NOT trigger the version deletion logic.
*/
- public function testKeepFileAndVersionsWhenMovingBetweenStorages() {
+ public function testKeepFileAndVersionsWhenMovingFileBetweenStorages() {
\OCA\Files_Versions\Hooks::connectHooks();
$storage2 = new Temporary(array());
@@ -155,7 +242,7 @@ class Storage extends \Test\TestCase {
// move to another storage
$this->userView->rename('test.txt', 'substorage/test.txt');
- $this->userView->file_exists('substorage/test.txt');
+ $this->assertTrue($this->userView->file_exists('substorage/test.txt'));
// rescan trash storage
list($rootStorage,) = $this->rootView->resolvePath($this->user . '/files_trashbin');
@@ -175,9 +262,50 @@ class Storage extends \Test\TestCase {
}
/**
+ * Test that versions are not auto-trashed when moving a file between
+ * storages. This is because rename() between storages would call
+ * unlink() which should NOT trigger the version deletion logic.
+ */
+ public function testKeepFileAndVersionsWhenMovingFolderBetweenStorages() {
+ \OCA\Files_Versions\Hooks::connectHooks();
+
+ $storage2 = new Temporary(array());
+ \OC\Files\Filesystem::mount($storage2, array(), $this->user . '/files/substorage');
+
+ // trigger a version (multiple would not work because of the expire logic)
+ $this->userView->file_put_contents('folder/inside.txt', 'v1');
+
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
+ $this->assertEquals(0, count($results));
+
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/folder/');
+ $this->assertEquals(1, count($results));
+
+ // move to another storage
+ $this->userView->rename('folder', 'substorage/folder');
+ $this->assertTrue($this->userView->file_exists('substorage/folder/inside.txt'));
+
+ // rescan trash storage
+ list($rootStorage,) = $this->rootView->resolvePath($this->user . '/files_trashbin');
+ $rootStorage->getScanner()->scan('');
+
+ // versions were moved too
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_versions/substorage/folder/');
+ $this->assertEquals(1, count($results));
+
+ // check that nothing got trashed by the rename's unlink() call
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files');
+ $this->assertEquals(0, count($results));
+
+ // check that versions were moved and not trashed
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/versions/');
+ $this->assertEquals(0, count($results));
+ }
+
+ /**
* Delete should fail is the source file cant be deleted
*/
- public function testSingleStorageDeleteFail() {
+ public function testSingleStorageDeleteFileFail() {
/**
* @var \OC\Files\Storage\Temporary | \PHPUnit_Framework_MockObject_MockObject $storage
*/
@@ -187,9 +315,6 @@ class Storage extends \Test\TestCase {
->getMock();
$storage->expects($this->any())
- ->method('rename')
- ->will($this->returnValue(false));
- $storage->expects($this->any())
->method('unlink')
->will($this->returnValue(false));
@@ -206,4 +331,37 @@ class Storage extends \Test\TestCase {
$results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
$this->assertEquals(0, count($results));
}
+
+ /**
+ * Delete should fail is the source folder cant be deleted
+ */
+ public function testSingleStorageDeleteFolderFail() {
+ /**
+ * @var \OC\Files\Storage\Temporary | \PHPUnit_Framework_MockObject_MockObject $storage
+ */
+ $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
+ ->setConstructorArgs([[]])
+ ->setMethods(['rename', 'unlink', 'rmdir'])
+ ->getMock();
+
+ $storage->expects($this->any())
+ ->method('rmdir')
+ ->will($this->returnValue(false));
+
+ $cache = $storage->getCache();
+
+ Filesystem::mount($storage, [], '/' . $this->user . '/files');
+ $this->userView->mkdir('folder');
+ $this->userView->file_put_contents('folder/test.txt', 'foo');
+ $this->assertTrue($storage->file_exists('folder/test.txt'));
+ $this->assertFalse($this->userView->rmdir('folder'));
+ $this->assertTrue($storage->file_exists('folder'));
+ $this->assertTrue($storage->file_exists('folder/test.txt'));
+ $this->assertTrue($cache->inCache('folder'));
+ $this->assertTrue($cache->inCache('folder/test.txt'));
+
+ // file should not be in the trashbin
+ $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
+ $this->assertEquals(0, count($results));
+ }
}
diff --git a/apps/provisioning_api/lib/users.php b/apps/provisioning_api/lib/users.php
index 4262dff7a6c..37bfd3b4eda 100644
--- a/apps/provisioning_api/lib/users.php
+++ b/apps/provisioning_api/lib/users.php
@@ -296,6 +296,10 @@ class Users {
if(strtolower($group) == 'admin') {
return new OC_OCS_Result(null, 103, 'Cannot create subadmins for admin group');
}
+ // We cannot be subadmin twice
+ if (OC_Subadmin::isSubAdminOfGroup($user, $group)) {
+ return new OC_OCS_Result(null, 100);
+ }
// Go
if(OC_Subadmin::createSubAdmin($user, $group)) {
return new OC_OCS_Result(null, 100);
diff --git a/apps/provisioning_api/tests/userstest.php b/apps/provisioning_api/tests/userstest.php
index 917d06a8348..82ff26134dc 100644
--- a/apps/provisioning_api/tests/userstest.php
+++ b/apps/provisioning_api/tests/userstest.php
@@ -767,4 +767,29 @@ class UsersTest extends TestCase {
$this->assertFalse($result->succeeded());
$this->assertEquals(101, $result->getStatusCode());
}
+
+ public function testSubAdminOfGroupAlreadySubAdmin() {
+ $user1 = $this->generateUsers();
+ $user2 = $this->generateUsers();
+ \OC_User::setUserId($user1);
+ \OC_Group::addToGroup($user1, 'admin');
+ $group1 = $this->getUniqueID();
+ \OC_Group::createGroup($group1);
+
+ //Make user2 subadmin of group1
+ $_POST['groupid'] = $group1;
+ $result = \OCA\provisioning_api\Users::addSubAdmin([
+ 'userid' => $user2,
+ ]);
+ $this->assertInstanceOf('OC_OCS_Result', $result);
+ $this->assertTrue($result->succeeded());
+
+ //Make user2 subadmin of group1 again
+ $_POST['groupid'] = $group1;
+ $result = \OCA\provisioning_api\Users::addSubAdmin([
+ 'userid' => $user2,
+ ]);
+ $this->assertInstanceOf('OC_OCS_Result', $result);
+ $this->assertTrue($result->succeeded());
+ }
}
diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php
index a9d21ffc8e7..d22adbd563c 100644
--- a/apps/user_ldap/lib/connection.php
+++ b/apps/user_ldap/lib/connection.php
@@ -23,8 +23,10 @@
namespace OCA\user_ldap\lib;
-//magic properties (incomplete)
+use OC\ServerNotAvailableException;
+
/**
+ * magic properties (incomplete)
* responsible for LDAP connections in context with the provided configuration
*
* @property string ldapUserFilter
@@ -46,7 +48,7 @@ class Connection extends LDAPUtility {
//cache handler
protected $cache;
- //settings handler
+ /** @var Configuration settings handler **/
protected $configuration;
protected $doNotValidate = false;
@@ -159,7 +161,8 @@ class Connection extends LDAPUtility {
$this->establishConnection();
}
if(is_null($this->ldapConnectionRes)) {
- \OCP\Util::writeLog('user_ldap', 'Connection could not be established', \OCP\Util::ERROR);
+ \OCP\Util::writeLog('user_ldap', 'No LDAP Connection to server ' . $this->configuration->ldapHost, \OCP\Util::ERROR);
+ throw new ServerNotAvailableException('Connection to LDAP server could not be established');
}
return $this->ldapConnectionRes;
}
diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/ldap.php
index 4dad34f5b1e..9d5cf4fee0a 100644
--- a/apps/user_ldap/lib/ldap.php
+++ b/apps/user_ldap/lib/ldap.php
@@ -23,6 +23,8 @@
namespace OCA\user_ldap\lib;
+use OC\ServerNotAvailableException;
+
class LDAP implements ILDAPWrapper {
protected $curFunc = '';
protected $curArgs = array();
@@ -280,6 +282,8 @@ class LDAP implements ILDAPWrapper {
//for now
} else if ($errorCode === 10) {
//referrals, we switch them off, but then there is AD :)
+ } else if ($errorCode === -1) {
+ throw new ServerNotAvailableException('Lost connection to LDAP server.');
} else {
\OCP\Util::writeLog('user_ldap',
'LDAP error '.$errorMsg.' (' .
diff --git a/apps/user_ldap/lib/user/manager.php b/apps/user_ldap/lib/user/manager.php
index ec50e031281..79b95818276 100644
--- a/apps/user_ldap/lib/user/manager.php
+++ b/apps/user_ldap/lib/user/manager.php
@@ -150,6 +150,11 @@ class Manager {
$this->access->getUserMapper());
}
+ /**
+ * @brief returns a User object by it's ownCloud username
+ * @param string the DN or username of the user
+ * @return \OCA\user_ldap\lib\user\User|\OCA\user_ldap\lib\user\OfflineUser|null
+ */
protected function createInstancyByUserName($id) {
//most likely a uid. Check whether it is a deleted user
if($this->isDeletedUser($id)) {
@@ -159,13 +164,14 @@ class Manager {
if($dn !== false) {
return $this->createAndCache($dn, $id);
}
- throw new \Exception('Could not create User instance');
+ return null;
}
/**
* @brief returns a User object by it's DN or ownCloud username
* @param string the DN or username of the user
* @return \OCA\user_ldap\lib\user\User|\OCA\user_ldap\lib\user\OfflineUser|null
+ * @throws \Exception when connection could not be established
*/
public function get($id) {
$this->checkAccess();
@@ -182,12 +188,7 @@ class Manager {
}
}
- try {
- $user = $this->createInstancyByUserName($id);
- return $user;
- } catch (\Exception $e) {
- return null;
- }
+ return $this->createInstancyByUserName($id);
}
}
diff --git a/apps/user_ldap/tests/user_ldap.php b/apps/user_ldap/tests/user_ldap.php
index 3fa4f2bf0a1..119b0b186d4 100644
--- a/apps/user_ldap/tests/user_ldap.php
+++ b/apps/user_ldap/tests/user_ldap.php
@@ -411,21 +411,53 @@ class Test_User_Ldap_Direct extends \Test\TestCase {
$this->prepareMockForUserExists($access);
$access->expects($this->any())
- ->method('readAttribute')
- ->will($this->returnCallback(function($dn) {
- if($dn === 'dnOfRoland,dc=test') {
- return array();
- }
- return false;
- }));
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($dn) {
+ if($dn === 'dnOfRoland,dc=test') {
+ return array();
+ }
+ return false;
+ }));
//test for existing user
$result = $backend->userExists('gunslinger');
$this->assertTrue($result);
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function testUserExistsForDeleted() {
+ $access = $this->getAccessMock();
+ $backend = new UserLDAP($access, $this->getMock('\OCP\IConfig'));
+ $this->prepareMockForUserExists($access);
+
+ $access->expects($this->any())
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($dn) {
+ if($dn === 'dnOfRoland,dc=test') {
+ return array();
+ }
+ return false;
+ }));
//test for deleted user
$result = $backend->userExists('formerUser');
- $this->assertFalse($result);
+ }
+
+ public function testUserExistsForNeverExisting() {
+ $access = $this->getAccessMock();
+ $backend = new UserLDAP($access, $this->getMock('\OCP\IConfig'));
+ $this->prepareMockForUserExists($access);
+
+ $access->expects($this->any())
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($dn) {
+ if($dn === 'dnOfRoland,dc=test') {
+ return array();
+ }
+ return false;
+ }));
//test for never-existing user
$result = $backend->userExists('mallory');
@@ -439,21 +471,55 @@ class Test_User_Ldap_Direct extends \Test\TestCase {
\OC_User::useBackend($backend);
$access->expects($this->any())
- ->method('readAttribute')
- ->will($this->returnCallback(function($dn) {
- if($dn === 'dnOfRoland,dc=test') {
- return array();
- }
- return false;
- }));
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($dn) {
+ if($dn === 'dnOfRoland,dc=test') {
+ return array();
+ }
+ return false;
+ }));
//test for existing user
$result = \OCP\User::userExists('gunslinger');
$this->assertTrue($result);
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function testUserExistsPublicAPIForDeleted() {
+ $access = $this->getAccessMock();
+ $backend = new UserLDAP($access, $this->getMock('\OCP\IConfig'));
+ $this->prepareMockForUserExists($access);
+ \OC_User::useBackend($backend);
+
+ $access->expects($this->any())
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($dn) {
+ if($dn === 'dnOfRoland,dc=test') {
+ return array();
+ }
+ return false;
+ }));
//test for deleted user
$result = \OCP\User::userExists('formerUser');
- $this->assertFalse($result);
+ }
+
+ public function testUserExistsPublicAPIForNeverExisting() {
+ $access = $this->getAccessMock();
+ $backend = new UserLDAP($access, $this->getMock('\OCP\IConfig'));
+ $this->prepareMockForUserExists($access);
+ \OC_User::useBackend($backend);
+
+ $access->expects($this->any())
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($dn) {
+ if($dn === 'dnOfRoland,dc=test') {
+ return array();
+ }
+ return false;
+ }));
//test for never-existing user
$result = \OCP\User::userExists('mallory');
@@ -469,54 +535,105 @@ class Test_User_Ldap_Direct extends \Test\TestCase {
$this->assertFalse($result);
}
- public function testGetHome() {
+ public function testGetHomeAbsolutePath() {
$access = $this->getAccessMock();
$config = $this->getMock('\OCP\IConfig');
$backend = new UserLDAP($access, $config);
$this->prepareMockForUserExists($access);
$access->connection->expects($this->any())
- ->method('__get')
- ->will($this->returnCallback(function($name) {
- if($name === 'homeFolderNamingRule') {
- return 'attr:testAttribute';
- }
- return null;
- }));
+ ->method('__get')
+ ->will($this->returnCallback(function($name) {
+ if($name === 'homeFolderNamingRule') {
+ return 'attr:testAttribute';
+ }
+ return null;
+ }));
$access->expects($this->any())
- ->method('readAttribute')
- ->will($this->returnCallback(function($dn, $attr) {
- switch ($dn) {
- case 'dnOfRoland,dc=test':
- if($attr === 'testAttribute') {
- return array('/tmp/rolandshome/');
- }
- return array();
- break;
- case 'dnOfLadyOfShadows,dc=test':
- if($attr === 'testAttribute') {
- return array('susannah/');
- }
- return array();
- break;
- default:
- return false;
- }
- }));
-
- $datadir = '/my/data/dir';
- $config->expects($this->once())
- ->method('getSystemValue')
- ->will($this->returnValue($datadir));
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($dn, $attr) {
+ switch ($dn) {
+ case 'dnOfRoland,dc=test':
+ if($attr === 'testAttribute') {
+ return array('/tmp/rolandshome/');
+ }
+ return array();
+ break;
+ default:
+ return false;
+ }
+ }));
//absolut path
$result = $backend->getHome('gunslinger');
$this->assertEquals('/tmp/rolandshome/', $result);
+ }
+
+ public function testGetHomeRelative() {
+ $access = $this->getAccessMock();
+ $config = $this->getMock('\OCP\IConfig');
+ $backend = new UserLDAP($access, $config);
+ $this->prepareMockForUserExists($access);
+ $access->connection->expects($this->any())
+ ->method('__get')
+ ->will($this->returnCallback(function($name) {
+ if($name === 'homeFolderNamingRule') {
+ return 'attr:testAttribute';
+ }
+ return null;
+ }));
+
+ $access->expects($this->any())
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($dn, $attr) {
+ switch ($dn) {
+ case 'dnOfLadyOfShadows,dc=test':
+ if($attr === 'testAttribute') {
+ return array('susannah/');
+ }
+ return array();
+ break;
+ default:
+ return false;
+ }
+ }));
//datadir-relativ path
+ $datadir = '/my/data/dir';
+ $config->expects($this->once())
+ ->method('getSystemValue')
+ ->will($this->returnValue($datadir));
+
$result = $backend->getHome('ladyofshadows');
$this->assertEquals($datadir.'/susannah/', $result);
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function testGetHomeNoPath() {
+ $access = $this->getAccessMock();
+ $backend = new UserLDAP($access, $this->getMock('\OCP\IConfig'));
+ $this->prepareMockForUserExists($access);
+
+ $access->connection->expects($this->any())
+ ->method('__get')
+ ->will($this->returnCallback(function($name) {
+ if($name === 'homeFolderNamingRule') {
+ return 'attr:testAttribute';
+ }
+ return null;
+ }));
+
+ $access->expects($this->any())
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($dn, $attr) {
+ switch ($dn) {
+ default:
+ return false;
+ }
+ }));
//no path at all – triggers OC default behaviour
$result = $backend->getHome('newyorker');
@@ -556,6 +673,12 @@ class Test_User_Ldap_Direct extends \Test\TestCase {
$backend = new UserLDAP($access, $this->getMock('\OCP\IConfig'));
$this->prepareMockForUserExists($access);
+ $access->connection->expects($this->any())
+ ->method('getConnectionResource')
+ ->will($this->returnCallback(function() {
+ return true;
+ }));
+
//with displayName
$result = $backend->getDisplayName('gunslinger');
$this->assertEquals('Roland Deschain', $result);
@@ -567,9 +690,36 @@ class Test_User_Ldap_Direct extends \Test\TestCase {
public function testGetDisplayNamePublicAPI() {
$access = $this->getAccessMock();
+ $access->expects($this->any())
+ ->method('username2dn')
+ ->will($this->returnCallback(function($uid) {
+ switch ($uid) {
+ case 'gunslinger':
+ return 'dnOfRoland,dc=test';
+ break;
+ case 'formerUser':
+ return 'dnOfFormerUser,dc=test';
+ break;
+ case 'newyorker':
+ return 'dnOfNewYorker,dc=test';
+ break;
+ case 'ladyofshadows':
+ return 'dnOfLadyOfShadows,dc=test';
+ break;
+ default:
+ return false;
+ }
+ }));
$this->prepareAccessForGetDisplayName($access);
$backend = new UserLDAP($access, $this->getMock('\OCP\IConfig'));
$this->prepareMockForUserExists($access);
+
+ $access->connection->expects($this->any())
+ ->method('getConnectionResource')
+ ->will($this->returnCallback(function() {
+ return true;
+ }));
+
\OC_User::useBackend($backend);
//with displayName
diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php
index 051e760105b..c65c68d609c 100644
--- a/apps/user_ldap/user_ldap.php
+++ b/apps/user_ldap/user_ldap.php
@@ -188,6 +188,7 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn
* check if a user exists
* @param string $uid the username
* @return boolean
+ * @throws \Exception when connection could not be established
*/
public function userExists($uid) {
if($this->access->connection->isCached('userExists'.$uid)) {
@@ -206,17 +207,12 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn
return true;
}
- try {
- $result = $this->userExistsOnLDAP($user);
- $this->access->connection->writeToCache('userExists'.$uid, $result);
- if($result === true) {
- $user->update();
- }
- return $result;
- } catch (\Exception $e) {
- \OCP\Util::writeLog('user_ldap', $e->getMessage(), \OCP\Util::WARN);
- return false;
+ $result = $this->userExistsOnLDAP($user);
+ $this->access->connection->writeToCache('userExists'.$uid, $result);
+ if($result === true) {
+ $user->update();
}
+ return $result;
}
/**
diff --git a/core/ajax/share.php b/core/ajax/share.php
index 6d0a6a4e3b9..6792753954b 100644
--- a/core/ajax/share.php
+++ b/core/ajax/share.php
@@ -105,7 +105,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
// don't send a mail to the user who shared the file
$recipientList = array_diff($recipientList, array(\OCP\User::getUser()));
- $mailNotification = new OC\Share\MailNotifications();
+ $mailNotification = new OC\Share\MailNotifications(\OCP\User::getUser());
$result = $mailNotification->sendInternalShareMail($recipientList, $itemSource, $itemType);
\OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, $recipient, true);
@@ -137,7 +137,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
$file = $_POST['file'];
$to_address = $_POST['toaddress'];
- $mailNotification = new \OC\Share\MailNotifications();
+ $mailNotification = new \OC\Share\MailNotifications(\OCP\User::getUser());
$expiration = null;
if (isset($_POST['expiration']) && $_POST['expiration'] !== '') {
diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
index 0c046d8ef0e..f3bf4361d76 100644
--- a/core/js/oc-dialogs.js
+++ b/core/js/oc-dialogs.js
@@ -138,7 +138,7 @@ var OCdialogs = {
* @param title dialog title
* @param callback which will be triggered when user presses Choose
* @param multiselect whether it should be possible to select multiple files
- * @param mimetypeFilter mimetype to filter by
+ * @param mimetypeFilter mimetype to filter by - directories will always be included
* @param modal make the dialog modal
*/
filepicker:function(title, callback, multiselect, mimetypeFilter, modal) {
diff --git a/core/js/share.js b/core/js/share.js
index b3533af4824..128ac729e5d 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -473,6 +473,10 @@ OC.Share={
} else {
response();
}
+ }).fail(function(){
+ $('#dropdown').find('.shareWithLoading').addClass('hidden');
+ OC.Notification.show(t('core', 'An error occured. Please try again'));
+ window.setTimeout(OC.Notification.hide, 5000);
});
},
focus: function(event, focused) {
diff --git a/core/templates/login.php b/core/templates/login.php
index f10a8102180..d139f32732c 100644
--- a/core/templates/login.php
+++ b/core/templates/login.php
@@ -24,6 +24,12 @@ script('core', [
<?php p($message); ?><br>
</div>
<?php endforeach; ?>
+ <?php if (isset($_['internalexception']) && ($_['internalexception'])): ?>
+ <div class="warning">
+ <?php p($l->t('An internal error occured.')); ?><br>
+ <small><?php p($l->t('Please try again or contact your administrator.')); ?></small>
+ </div>
+ <?php endif; ?>
<p id="message" class="hidden">
<img class="float-spinner" alt=""
src="<?php p(\OCP\Util::imagePath('core', 'loading-dark.gif'));?>" />
diff --git a/cron.php b/cron.php
index ef68669da34..b8e5dde41f4 100644
--- a/cron.php
+++ b/cron.php
@@ -57,6 +57,11 @@ try {
exit;
}
+ if (\OC::$server->getSystemConfig()->getValue('singleuser', false)) {
+ \OCP\Util::writeLog('cron', 'We are in admin only mode, skipping cron', \OCP\Util::DEBUG);
+ exit;
+ }
+
// load all apps to get all api routes properly setup
OC_App::loadApps();
@@ -90,6 +95,22 @@ try {
}
if (OC::$CLI) {
+ // the cron job must be executed with the right user
+ if (!OC_Util::runningOnWindows()) {
+ if (!function_exists('posix_getuid')) {
+ echo "The posix extensions are required - see http://php.net/manual/en/book.posix.php" . PHP_EOL;
+ exit(0);
+ }
+ $user = posix_getpwuid(posix_getuid());
+ $configUser = posix_getpwuid(fileowner(OC::$SERVERROOT . '/config/config.php'));
+ if ($user['name'] !== $configUser['name']) {
+ echo "Console has to be executed with the same user as the web server is operated" . PHP_EOL;
+ echo "Current user: " . $user['name'] . PHP_EOL;
+ echo "Web server user: " . $configUser['name'] . PHP_EOL;
+ exit(0);
+ }
+ }
+
// Create lock file first
TemporaryCronClass::$lockfile = OC_Config::getValue("datadirectory", OC::$SERVERROOT . '/data') . '/cron.lock';
diff --git a/lib/base.php b/lib/base.php
index 86097438d57..a5ee3612ab7 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -263,27 +263,37 @@ class OC {
header('Retry-After: 120');
// render error page
- $tmpl = new OC_Template('', 'update.user', 'guest');
+ $template = new OC_Template('', 'update.user', 'guest');
OC_Util::addscript('maintenance-check');
- $tmpl->printPage();
+ $template->printPage();
die();
}
}
- public static function checkSingleUserMode() {
+ public static function checkSingleUserMode($lockIfNoUserLoggedIn = false) {
+ if (!\OC::$server->getSystemConfig()->getValue('singleuser', false)) {
+ return;
+ }
$user = OC_User::getUserSession()->getUser();
- $group = OC_Group::getManager()->get('admin');
- if ($user && \OC::$server->getSystemConfig()->getValue('singleuser', false) && !$group->inGroup($user)) {
- // send http status 503
- header('HTTP/1.1 503 Service Temporarily Unavailable');
- header('Status: 503 Service Temporarily Unavailable');
- header('Retry-After: 120');
-
- // render error page
- $tmpl = new OC_Template('', 'singleuser.user', 'guest');
- $tmpl->printPage();
- die();
+ if ($user) {
+ $group = \OC::$server->getGroupManager()->get('admin');
+ if ($group->inGroup($user)) {
+ return;
+ }
+ } else {
+ if(!$lockIfNoUserLoggedIn) {
+ return;
+ }
}
+ // send http status 503
+ header('HTTP/1.1 503 Service Temporarily Unavailable');
+ header('Status: 503 Service Temporarily Unavailable');
+ header('Retry-After: 120');
+
+ // render error page
+ $template = new OC_Template('', 'singleuser.user', 'guest');
+ $template->printPage();
+ die();
}
/**
@@ -489,12 +499,11 @@ class OC {
\OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
\OC::$server->getEventLogger()->start('boot', 'Initialize');
- // set some stuff
- //ob_start();
+ // Don't display errors and log them
error_reporting(E_ALL | E_STRICT);
- if (defined('DEBUG') && DEBUG) {
- ini_set('display_errors', 1);
- }
+ @ini_set('display_errors', 0);
+ @ini_set('log_errors', 1);
+
self::$CLI = (php_sapi_name() == 'cli');
date_default_timezone_set('UTC');
@@ -880,6 +889,10 @@ class OC {
}
} catch (\OC\User\LoginException $e) {
$messages[] = $e->getMessage();
+ } catch (\Exception $ex) {
+ \OCP\Util::logException('handleLogin', $ex);
+ // do not disclose information. show generic error
+ $error[] = 'internalexception';
}
OC_Util::displayLoginPage(array_unique($error), $messages);
diff --git a/lib/private/connector/sabre/maintenanceplugin.php b/lib/private/connector/sabre/maintenanceplugin.php
index 0208f3fb5a6..86fbd32702a 100644
--- a/lib/private/connector/sabre/maintenanceplugin.php
+++ b/lib/private/connector/sabre/maintenanceplugin.php
@@ -45,6 +45,9 @@ class OC_Connector_Sabre_MaintenancePlugin extends \Sabre\DAV\ServerPlugin
* @return bool
*/
public function checkMaintenanceMode() {
+ if (\OC::$server->getSystemConfig()->getValue('singleuser', false)) {
+ throw new \Sabre\DAV\Exception\ServiceUnavailable();
+ }
if (OC_Config::getValue('maintenance', false)) {
throw new \Sabre\DAV\Exception\ServiceUnavailable();
}
diff --git a/lib/private/datetimezone.php b/lib/private/datetimezone.php
index 727ce321dba..fcf1883cb4c 100644
--- a/lib/private/datetimezone.php
+++ b/lib/private/datetimezone.php
@@ -38,13 +38,14 @@ class DateTimeZone implements IDateTimeZone {
/**
* Get the timezone of the current user, based on his session information and config data
*
+ * @param bool|int $timestamp
* @return \DateTimeZone
*/
- public function getTimeZone() {
+ public function getTimeZone($timestamp = false) {
$timeZone = $this->config->getUserValue($this->session->get('user_id'), 'core', 'timezone', null);
if ($timeZone === null) {
if ($this->session->exists('timezone')) {
- return $this->guessTimeZoneFromOffset($this->session->get('timezone'));
+ return $this->guessTimeZoneFromOffset($this->session->get('timezone'), $timestamp);
}
$timeZone = $this->getDefaultTimeZone();
}
@@ -64,9 +65,10 @@ class DateTimeZone implements IDateTimeZone {
* we try to find it manually, before falling back to UTC.
*
* @param mixed $offset
+ * @param bool|int $timestamp
* @return \DateTimeZone
*/
- protected function guessTimeZoneFromOffset($offset) {
+ protected function guessTimeZoneFromOffset($offset, $timestamp) {
try {
// Note: the timeZone name is the inverse to the offset,
// so a positive offset means negative timeZone
@@ -83,7 +85,13 @@ class DateTimeZone implements IDateTimeZone {
// we try to guess one timezone that has the same offset
foreach (\DateTimeZone::listIdentifiers() as $timeZone) {
$dtz = new \DateTimeZone($timeZone);
- $dtOffset = $dtz->getOffset(new \DateTime());
+ $dateTime = new \DateTime();
+
+ if ($timestamp !== false) {
+ $dateTime->setTimestamp($timestamp);
+ }
+
+ $dtOffset = $dtz->getOffset($dateTime);
if ($dtOffset == 3600 * $offset) {
return $dtz;
}
diff --git a/lib/private/db/connection.php b/lib/private/db/connection.php
index 0da3c844f03..5f065027920 100644
--- a/lib/private/db/connection.php
+++ b/lib/private/db/connection.php
@@ -62,6 +62,8 @@ class Connection extends \Doctrine\DBAL\Connection implements IDBConnection {
parent::__construct($params, $driver, $config, $eventManager);
$this->adapter = new $params['adapter']($this);
$this->tablePrefix = $params['tablePrefix'];
+
+ parent::setTransactionIsolation(parent::TRANSACTION_READ_COMMITTED);
}
/**
diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php
index fcf5aae0258..f55b5078c0e 100644
--- a/lib/private/db/migrator.php
+++ b/lib/private/db/migrator.php
@@ -100,7 +100,7 @@ class Migrator {
* @return string
*/
protected function generateTemporaryTableName($name) {
- return 'oc_' . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
+ return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
}
/**
@@ -151,7 +151,7 @@ class Migrator {
$indexName = $index->getName();
} else {
// avoid conflicts in index names
- $indexName = 'oc_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
+ $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
}
$newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary());
}
diff --git a/lib/private/files.php b/lib/private/files.php
index 49917a5b8a0..be08fd37d60 100644
--- a/lib/private/files.php
+++ b/lib/private/files.php
@@ -161,11 +161,12 @@ class OC_Files {
* @param false|string $filename
*/
private static function addSendfileHeader($filename) {
- $filename = \OC\Files\Filesystem::getLocalFile($filename);
if (isset($_SERVER['MOD_X_SENDFILE_ENABLED'])) {
+ $filename = \OC\Files\Filesystem::getLocalFile($filename);
header("X-Sendfile: " . $filename);
}
if (isset($_SERVER['MOD_X_SENDFILE2_ENABLED'])) {
+ $filename = \OC\Files\Filesystem::getLocalFile($filename);
if (isset($_SERVER['HTTP_RANGE']) &&
preg_match("/^bytes=([0-9]+)-([0-9]*)$/", $_SERVER['HTTP_RANGE'], $range)) {
$filelength = filesize($filename);
@@ -181,6 +182,11 @@ class OC_Files {
}
if (isset($_SERVER['MOD_X_ACCEL_REDIRECT_ENABLED'])) {
+ if (isset($_SERVER['MOD_X_ACCEL_REDIRECT_PREFIX'])) {
+ $filename = $_SERVER['MOD_X_ACCEL_REDIRECT_PREFIX'] . \OC\Files\Filesystem::getLocalFile($filename);
+ } else {
+ $filename = \OC::$WEBROOT . '/data' . \OC\Files\Filesystem::getRoot() . $filename;
+ }
header("X-Accel-Redirect: " . $filename);
}
}
diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php
index 1dfa9e1dd10..358e654aada 100644
--- a/lib/private/files/cache/cache.php
+++ b/lib/private/files/cache/cache.php
@@ -261,7 +261,7 @@ class Cache {
$this->update($id, $data);
return $id;
} else {
- throw new \RuntimeException('File entry exists when inserting and does not exist on select... go away');
+ throw new \RuntimeException('File entry could not be inserted with insertIfNotExist() but could also not be selected with getId() in order to perform an update. Please try again.');
}
}
}
@@ -285,10 +285,17 @@ class Cache {
}
list($queryParts, $params) = $this->buildParts($data);
+ // duplicate $params because we need the parts twice in the SQL statement
+ // once for the SET part, once in the WHERE clause
+ $params = array_merge($params, $params);
$params[] = $id;
- $sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? WHERE `fileid` = ?';
+ // don't update if the data we try to set is the same as the one in the record
+ // some databases (Postgres) don't like superfluous updates
+ $sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? ' .
+ 'WHERE (' . implode(' <> ? OR ', $queryParts) . ' <> ? ) AND `fileid` = ? ';
\OC_DB::executeAudited($sql, $params);
+
}
/**
diff --git a/lib/private/files/cache/storage.php b/lib/private/files/cache/storage.php
index 9f2739bbedb..a2f1a9b6b0d 100644
--- a/lib/private/files/cache/storage.php
+++ b/lib/private/files/cache/storage.php
@@ -44,7 +44,7 @@ class Storage {
if ($row = $result->fetchRow()) {
$this->numericId = $row['numeric_id'];
} else {
- throw new \RuntimeException('Storage exists when inserting and does not exist on select... go away');
+ throw new \RuntimeException('Storage could neither be inserted nor be selected from the database');
}
}
}
diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php
index e933782ce2f..707440632f2 100644
--- a/lib/private/files/filesystem.php
+++ b/lib/private/files/filesystem.php
@@ -342,42 +342,42 @@ class Filesystem {
$userObject = \OC_User::getManager()->get($user);
- if (!is_null($userObject)) {
- $homeStorage = \OC_Config::getValue( 'objectstore' );
- if (!empty($homeStorage)) {
- // sanity checks
- if (empty($homeStorage['class'])) {
- \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
- }
- if (!isset($homeStorage['arguments'])) {
- $homeStorage['arguments'] = array();
- }
- // instantiate object store implementation
- $homeStorage['arguments']['objectstore'] = new $homeStorage['class']($homeStorage['arguments']);
- // mount with home object store implementation
- $homeStorage['class'] = '\OC\Files\ObjectStore\HomeObjectStoreStorage';
- } else {
- $homeStorage = array(
- //default home storage configuration:
- 'class' => '\OC\Files\Storage\Home',
- 'arguments' => array()
- );
- }
- $homeStorage['arguments']['user'] = $userObject;
+ if (is_null($userObject)) {
+ \OCP\Util::writeLog('files', ' Backends provided no user object for '.$user, \OCP\Util::ERROR);
+ throw new \OC\User\NoUserException();
+ }
- // check for legacy home id (<= 5.0.12)
- if (\OC\Files\Cache\Storage::exists('local::' . $root . '/')) {
- $homeStorage['arguments']['legacy'] = true;
+ $homeStorage = \OC_Config::getValue( 'objectstore' );
+ if (!empty($homeStorage)) {
+ // sanity checks
+ if (empty($homeStorage['class'])) {
+ \OCP\Util::writeLog('files', 'No class given for objectstore', \OCP\Util::ERROR);
}
-
- self::mount($homeStorage['class'], $homeStorage['arguments'], $user);
-
- $home = \OC\Files\Filesystem::getStorage($user);
+ if (!isset($homeStorage['arguments'])) {
+ $homeStorage['arguments'] = array();
+ }
+ // instantiate object store implementation
+ $homeStorage['arguments']['objectstore'] = new $homeStorage['class']($homeStorage['arguments']);
+ // mount with home object store implementation
+ $homeStorage['class'] = '\OC\Files\ObjectStore\HomeObjectStoreStorage';
+ } else {
+ $homeStorage = array(
+ //default home storage configuration:
+ 'class' => '\OC\Files\Storage\Home',
+ 'arguments' => array()
+ );
}
- else {
- self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user);
+ $homeStorage['arguments']['user'] = $userObject;
+
+ // check for legacy home id (<= 5.0.12)
+ if (\OC\Files\Cache\Storage::exists('local::' . $root . '/')) {
+ $homeStorage['arguments']['legacy'] = true;
}
+ self::mount($homeStorage['class'], $homeStorage['arguments'], $user);
+
+ $home = \OC\Files\Filesystem::getStorage($user);
+
self::mountCacheDir($user);
// Chance to mount for other storages
diff --git a/lib/private/files/mount/mountpoint.php b/lib/private/files/mount/mountpoint.php
index 85edb7cb570..b62dc478015 100644
--- a/lib/private/files/mount/mountpoint.php
+++ b/lib/private/files/mount/mountpoint.php
@@ -95,10 +95,12 @@ class MountPoint implements IMountPoint {
}
/**
+ * Sets the mount point path, relative to data/
+ *
* @param string $mountPoint new mount point
*/
public function setMountPoint($mountPoint) {
- $this->mountPoint = $mountPoint;
+ $this->mountPoint = $this->formatPath($mountPoint);
}
/**
diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php
index 4f7b3ff8940..9734d36f37d 100644
--- a/lib/private/files/storage/dav.php
+++ b/lib/private/files/storage/dav.php
@@ -126,14 +126,10 @@ class DAV extends \OC\Files\Storage\Common {
return opendir('fakedir://' . $id);
} catch (Exception\NotFound $e) {
return false;
- } catch (\Sabre\DAV\Exception $e) {
- $this->convertSabreException($e);
- return false;
} catch (\Exception $e) {
- // TODO: log for now, but in the future need to wrap/rethrow exception
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
- return false;
+ $this->convertException($e);
}
+ return false;
}
public function filetype($path) {
@@ -148,14 +144,10 @@ class DAV extends \OC\Files\Storage\Common {
return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
} catch (Exception\NotFound $e) {
return false;
- } catch (\Sabre\DAV\Exception $e) {
- $this->convertSabreException($e);
- return false;
} catch (\Exception $e) {
- // TODO: log for now, but in the future need to wrap/rethrow exception
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
- return false;
+ $this->convertException($e);
}
+ return false;
}
public function file_exists($path) {
@@ -166,14 +158,10 @@ class DAV extends \OC\Files\Storage\Common {
return true; //no 404 exception
} catch (Exception\NotFound $e) {
return false;
- } catch (\Sabre\DAV\Exception $e) {
- $this->convertSabreException($e);
- return false;
} catch (\Exception $e) {
- // TODO: log for now, but in the future need to wrap/rethrow exception
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
- return false;
+ $this->convertException($e);
}
+ return false;
}
public function unlink($path) {
@@ -197,8 +185,12 @@ class DAV extends \OC\Files\Storage\Common {
curl_setopt($curl, CURLOPT_URL, $this->createBaseUri() . $this->encodePath($path));
curl_setopt($curl, CURLOPT_FILE, $fp);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
- curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ if(defined('CURLOPT_PROTOCOLS')) {
+ curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ }
+ if(defined('CURLOPT_REDIR_PROTOCOLS')) {
+ curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ }
if ($this->secure === true) {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
@@ -285,12 +277,8 @@ class DAV extends \OC\Files\Storage\Common {
$this->client->proppatch($this->encodePath($path), array('{DAV:}lastmodified' => $mtime));
} catch (Exception\NotImplemented $e) {
return false;
- } catch (\Sabre\DAV\Exception $e) {
- $this->convertSabreException($e);
- return false;
} catch (\Exception $e) {
- // TODO: log for now, but in the future need to wrap/rethrow exception
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
+ $this->convertException($e);
return false;
}
} else {
@@ -311,8 +299,12 @@ class DAV extends \OC\Files\Storage\Common {
curl_setopt($curl, CURLOPT_INFILESIZE, filesize($path));
curl_setopt($curl, CURLOPT_PUT, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
- curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ if(defined('CURLOPT_PROTOCOLS')) {
+ curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ }
+ if(defined('CURLOPT_REDIR_PROTOCOLS')) {
+ curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ }
if ($this->secure === true) {
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
@@ -339,14 +331,10 @@ class DAV extends \OC\Files\Storage\Common {
$this->removeCachedFile($path1);
$this->removeCachedFile($path2);
return true;
- } catch (\Sabre\DAV\Exception $e) {
- $this->convertSabreException($e);
- return false;
} catch (\Exception $e) {
- // TODO: log for now, but in the future need to wrap/rethrow exception
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
- return false;
+ $this->convertException($e);
}
+ return false;
}
public function copy($path1, $path2) {
@@ -357,14 +345,10 @@ class DAV extends \OC\Files\Storage\Common {
$this->client->request('COPY', $path1, null, array('Destination' => $path2));
$this->removeCachedFile($path2);
return true;
- } catch (\Sabre\DAV\Exception $e) {
- $this->convertSabreException($e);
- return false;
} catch (\Exception $e) {
- // TODO: log for now, but in the future need to wrap/rethrow exception
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
- return false;
+ $this->convertException($e);
}
+ return false;
}
public function stat($path) {
@@ -378,14 +362,10 @@ class DAV extends \OC\Files\Storage\Common {
);
} catch (Exception\NotFound $e) {
return array();
- } catch (\Sabre\DAV\Exception $e) {
- $this->convertSabreException($e);
- return false;
} catch (\Exception $e) {
- // TODO: log for now, but in the future need to wrap/rethrow exception
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
- return array();
+ $this->convertException($e);
}
+ return array();
}
public function getMimeType($path) {
@@ -407,14 +387,10 @@ class DAV extends \OC\Files\Storage\Common {
}
} catch (Exception\NotFound $e) {
return false;
- } catch (\Sabre\DAV\Exception $e) {
- $this->convertSabreException($e);
- return false;
} catch (\Exception $e) {
- // TODO: log for now, but in the future need to wrap/rethrow exception
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
- return false;
+ $this->convertException($e);
}
+ return false;
}
/**
@@ -455,16 +431,11 @@ class DAV extends \OC\Files\Storage\Common {
return false;
}
- $this->convertSabreException($e);
- return false;
- } catch (\Sabre\DAV\Exception $e) {
- $this->convertSabreException($e);
- return false;
+ $this->convertException($e);
} catch (\Exception $e) {
- // TODO: log for now, but in the future need to wrap/rethrow exception
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
- return false;
+ $this->convertException($e);
}
+ return false;
}
/**
@@ -565,23 +536,26 @@ class DAV extends \OC\Files\Storage\Common {
}
} catch (Exception\NotFound $e) {
return false;
- } catch (Exception $e) {
- $this->convertSabreException($e);
+ } catch (\Exception $e) {
+ $this->convertException($e);
return false;
}
}
/**
- * Convert sabre DAV exception to a storage exception,
- * then throw it
+ * Interpret the given exception and decide whether it is due to an
+ * unavailable storage, invalid storage or other.
+ * This will either throw StorageInvalidException, StorageNotAvailableException
+ * or do nothing.
*
* @param \Sabre\Dav\Exception $e sabre exception
+ *
* @throws StorageInvalidException if the storage is invalid, for example
* when the authentication expired or is invalid
* @throws StorageNotAvailableException if the storage is not available,
* which might be temporary
*/
- private function convertSabreException(\Sabre\Dav\Exception $e) {
+ private function convertException(\Exception $e) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
if ($e instanceof \Sabre\DAV\Exception\NotAuthenticated) {
// either password was changed or was invalid all along
@@ -589,9 +563,18 @@ class DAV extends \OC\Files\Storage\Common {
} else if ($e instanceof \Sabre\DAV\Exception\MethodNotAllowed) {
// ignore exception, false will be returned
return;
+ } else if ($e instanceof \Sabre\Dav\Exception) {
+ throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
+ } else if ($e instanceof \InvalidArgumentException) {
+ // parse error because the server returned HTML instead of XML,
+ // possibly temporarily down
+ throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
+ } else if (($e instanceof StorageNotAvailableException) || ($e instanceof StorageInvalidException)) {
+ // rethrow
+ throw $e;
}
- throw new StorageNotAvailableException(get_class($e).': '.$e->getMessage());
+ // TODO: only log for now, but in the future need to wrap/rethrow exception
}
}
diff --git a/lib/private/files/view.php b/lib/private/files/view.php
index b5ad425a0fa..b58a8c2d639 100644
--- a/lib/private/files/view.php
+++ b/lib/private/files/view.php
@@ -639,7 +639,9 @@ class View {
if (is_resource($dh)) {
while (($file = readdir($dh)) !== false) {
if (!Filesystem::isIgnoredDir($file)) {
- $result = $this->copy($path1 . '/' . $file, $path2 . '/' . $file, $preserveMtime);
+ if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file, $preserveMtime)) {
+ $result = false;
+ }
}
}
}
diff --git a/lib/private/hook.php b/lib/private/hook.php
index c9ca58f779e..d4e82057d96 100644
--- a/lib/private/hook.php
+++ b/lib/private/hook.php
@@ -80,6 +80,9 @@ class OC_Hook{
OC_Log::write('hook',
'error while running hook (' . $i["class"] . '::' . $i["name"] . '): '.$e->getMessage(),
OC_Log::ERROR);
+ if($e instanceof \OC\ServerNotAvailableException) {
+ throw $e;
+ }
}
}
diff --git a/lib/private/httphelper.php b/lib/private/httphelper.php
index 08c35e4ae08..88e9ac76431 100644
--- a/lib/private/httphelper.php
+++ b/lib/private/httphelper.php
@@ -68,9 +68,12 @@ class HTTPHelper {
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
- curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
-
+ if(defined('CURLOPT_PROTOCOLS')) {
+ curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ }
+ if(defined('CURLOPT_REDIR_PROTOCOLS')) {
+ curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ }
curl_setopt($curl, CURLOPT_USERAGENT, self::USER_AGENT);
if ($proxy !== null) {
curl_setopt($curl, CURLOPT_PROXY, $proxy);
diff --git a/lib/private/mail.php b/lib/private/mail.php
index 6b7eec6e080..1a2025de0b2 100644
--- a/lib/private/mail.php
+++ b/lib/private/mail.php
@@ -28,10 +28,11 @@ class OC_Mail {
* @param string $ccaddress
* @param string $ccname
* @param string $bcc
+ * @param string $replyTo
* @throws Exception
*/
public static function send($toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname,
- $html=0, $altbody='', $ccaddress='', $ccname='', $bcc='') {
+ $html=0, $altbody='', $ccaddress='', $ccname='', $bcc='', $replyTo='') {
$SMTPMODE = OC_Config::getValue( 'mail_smtpmode', 'sendmail' );
$SMTPHOST = OC_Config::getValue( 'mail_smtphost', '127.0.0.1' );
@@ -79,7 +80,9 @@ class OC_Mail {
if($ccaddress != '') $mailo->AddCC($ccaddress, $ccname);
if($bcc != '') $mailo->AddBCC($bcc);
- $mailo->AddReplyTo($fromaddress, $fromname);
+ if($replyTo !== '') {
+ $mailo->addReplyTo($replyTo);
+ }
$mailo->WordWrap = 78;
$mailo->IsHTML($html == 1);
diff --git a/lib/private/servernotavailableexception.php b/lib/private/servernotavailableexception.php
new file mode 100644
index 00000000000..5a57917d23a
--- /dev/null
+++ b/lib/private/servernotavailableexception.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ *
+ * @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 OC;
+
+
+class ServerNotAvailableException extends \Exception {
+
+}
diff --git a/lib/private/share/mailnotifications.php b/lib/private/share/mailnotifications.php
index 342d3d5057a..09d21968dfe 100644
--- a/lib/private/share/mailnotifications.php
+++ b/lib/private/share/mailnotifications.php
@@ -37,6 +37,9 @@ class MailNotifications {
*/
private $from;
+ /** @var string Mail address used for reply to */
+ private $replyTo;
+
/**
* @var string
*/
@@ -49,20 +52,16 @@ class MailNotifications {
/**
*
- * @param string $sender user id (if nothing is set we use the currently logged-in user)
+ * @param string $sender user id
*/
- public function __construct($sender = null) {
+ public function __construct($sender) {
$this->l = \OC::$server->getL10N('lib');
$this->senderId = $sender;
$this->from = \OCP\Util::getDefaultEmailAddress('sharing-noreply');
- if ($this->senderId) {
- $this->from = \OCP\Config::getUserValue($this->senderId, 'settings', 'email', $this->from);
- $this->senderDisplayName = \OCP\User::getDisplayName($this->senderId);
- } else {
- $this->senderDisplayName = \OCP\User::getDisplayName();
- }
+ $this->replyTo = \OCP\Config::getUserValue($this->senderId, 'settings', 'email', $this->from);
+ $this->senderDisplayName = \OCP\User::getDisplayName($this->senderId);
}
/**
@@ -105,6 +104,11 @@ class MailNotifications {
$args = array(
'dir' => $filename,
);
+ } else if (strpos($filename, '/')) {
+ $args = array(
+ 'dir' => '/' . dirname($filename),
+ 'scrollto' => basename($filename),
+ );
} else {
$args = array(
'dir' => '/',
@@ -118,7 +122,7 @@ class MailNotifications {
// send it out now
try {
- \OCP\Util::sendMail($to, $recipientDisplayName, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail);
+ \OC_MAIL::send($to, $recipientDisplayName, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail, '', '', '', $this->replyTo);
} catch (\Exception $e) {
\OCP\Util::writeLog('sharing', "Can't send mail to inform the user about an internal share: " . $e->getMessage() , \OCP\Util::ERROR);
$noMail[] = $recipientDisplayName;
@@ -145,7 +149,7 @@ class MailNotifications {
$failed = array();
foreach ($rs as $r) {
try {
- \OCP\Util::sendMail($r, $r, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail);
+ \OC_MAIL::send($r, $r, $subject, $htmlMail, $this->from, $this->senderDisplayName, 1, $alttextMail, '', '', '', $this->replyTo);
} catch (\Exception $e) {
\OCP\Util::writeLog('sharing', "Can't send mail with public link to $r: " . $e->getMessage(), \OCP\Util::ERROR);
$failed[] = $r;
diff --git a/lib/private/share/share.php b/lib/private/share/share.php
index 0069d70190a..e52704be3d8 100644
--- a/lib/private/share/share.php
+++ b/lib/private/share/share.php
@@ -312,18 +312,20 @@ class Share extends \OC\Share\Constants {
*/
public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
$shares = array();
- $fileDependend = false;
+ $fileDependent = false;
if ($itemType === 'file' || $itemType === 'folder') {
- $fileDependend = true;
+ $fileDependent = true;
$column = 'file_source';
- $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` WHERE';
+ $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
+ $where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
+ $where .= ' WHERE';
} else {
$column = 'item_source';
$where = 'WHERE';
}
- $select = self::createSelectStatement(self::FORMAT_NONE, $fileDependend);
+ $select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
$arguments = array($itemSource, $itemType);
@@ -348,6 +350,9 @@ class Share extends \OC\Share\Constants {
$result = \OC_DB::executeAudited($query, $arguments);
while ($row = $result->fetchRow()) {
+ if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
+ continue;
+ }
$shares[] = $row;
}
@@ -1360,10 +1365,11 @@ class Share extends \OC\Share\Constants {
} else {
$root = '';
}
- $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid`';
+ $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
if (!isset($item)) {
- $where .= ' WHERE `file_target` IS NOT NULL';
+ $where .= ' AND `file_target` IS NOT NULL ';
}
+ $where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
$fileDependent = true;
$queryArgs = array();
} else {
@@ -1504,6 +1510,9 @@ class Share extends \OC\Share\Constants {
while ($row = $result->fetchRow()) {
self::transformDBResults($row);
// Filter out duplicate group shares for users with unique targets
+ if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
+ continue;
+ }
if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
$row['share_type'] = self::SHARE_TYPE_GROUP;
$row['unique_name'] = true; // remember that we use a unique name for this user
@@ -2160,7 +2169,9 @@ class Share extends \OC\Share\Constants {
$select = '*';
if ($format == self::FORMAT_STATUSES) {
if ($fileDependent) {
- $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, `share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`';
+ $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
+ . '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
+ . '`*PREFIX*storages`.`id` AS `storage_id`';
} else {
$select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
}
@@ -2169,7 +2180,8 @@ class Share extends \OC\Share\Constants {
if ($fileDependent) {
$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
. ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
- . ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`';
+ . ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
+ . '`*PREFIX*storages`.`id` AS `storage_id`';
} else {
$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
. ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
@@ -2182,9 +2194,11 @@ class Share extends \OC\Share\Constants {
. '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
. '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `unencrypted_size`, `encrypted`, `etag`, `mail_send`';
} else {
- $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,
- `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,
- `file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`, `stime`, `expiration`, `token`, `storage`, `mail_send`';
+ $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
+ . '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
+ . '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
+ . '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
+ . '`*PREFIX*storages`.`id` AS `storage_id`';
}
}
}
@@ -2275,15 +2289,17 @@ class Share extends \OC\Share\Constants {
*
* @param string $url
* @param array $fields post parameters
- * @return bool
+ * @return array
*/
private static function tryHttpPost($url, $fields) {
$protocol = 'https://';
- $success = false;
+ $result = [
+ 'success' => false,
+ 'result' => '',
+ ];
$try = 0;
- while ($success === false && $try < 2) {
+ while ($result['success'] === false && $try < 2) {
$result = \OC::$server->getHTTPHelper()->post($protocol . $url, $fields);
- $success = $result['success'];
$try++;
$protocol = 'http://';
}
@@ -2306,7 +2322,7 @@ class Share extends \OC\Share\Constants {
list($user, $remote) = explode('@', $shareWith, 2);
if ($user && $remote) {
- $url = $remote . self::BASE_PATH_TO_SHARE_API . '?format=' . self::RESPONSE_FORMAT;
+ $url = rtrim($remote, '/') . self::BASE_PATH_TO_SHARE_API . '?format=' . self::RESPONSE_FORMAT;
$local = \OC::$server->getURLGenerator()->getAbsoluteURL('/');
@@ -2339,8 +2355,9 @@ class Share extends \OC\Share\Constants {
* @return bool
*/
private static function sendRemoteUnshare($remote, $id, $token) {
- $url = $remote . self::BASE_PATH_TO_SHARE_API . '/' . $id . '/unshare?format=' . self::RESPONSE_FORMAT;
+ $url = rtrim($remote, '/') . self::BASE_PATH_TO_SHARE_API . '/' . $id . '/unshare?format=' . self::RESPONSE_FORMAT;
$fields = array('token' => $token, 'format' => 'json');
+ $url = self::removeProtocolFromUrl($url);
$result = self::tryHttpPost($url, $fields);
$status = json_decode($result['result'], true);
@@ -2370,4 +2387,27 @@ class Share extends \OC\Share\Constants {
return (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
}
+ /**
+ * Checks whether the given path is reachable for the given owner
+ *
+ * @param string $path path relative to files
+ * @param string $ownerStorageId storage id of the owner
+ *
+ * @return boolean true if file is reachable, false otherwise
+ */
+ private static function isFileReachable($path, $ownerStorageId) {
+ // if outside the home storage, file is always considered reachable
+ if (!(substr($ownerStorageId, 0, 6) === 'home::')) {
+ return true;
+ }
+
+ // if inside the home storage, the file has to be under "/files/"
+ $path = ltrim($path, '/');
+ if (substr($path, 0, 6) === 'files/') {
+ return true;
+ }
+
+ return false;
+ }
+
}
diff --git a/lib/private/user/http.php b/lib/private/user/http.php
index 8375c4e1e22..10c95ed43b9 100644
--- a/lib/private/user/http.php
+++ b/lib/private/user/http.php
@@ -72,8 +72,12 @@ class OC_User_HTTP extends OC_User_Backend implements \OCP\IUserBackend {
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USERPWD, $user.':'.$password);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
- curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ if(defined('CURLOPT_PROTOCOLS')) {
+ curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ }
+ if(defined('CURLOPT_REDIR_PROTOCOLS')) {
+ curl_setopt($curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ }
curl_exec($ch);
diff --git a/lib/private/user/nouserexception.php b/lib/private/user/nouserexception.php
new file mode 100644
index 00000000000..9452362b4e6
--- /dev/null
+++ b/lib/private/user/nouserexception.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * ownCloud
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING-AGPL file.
+ *
+ * @author Jörn Friedrich Dreyer <jfd@owncloud.com>
+ * @copyright Jörn Friedrich Dreyer 2015
+ */
+
+namespace OC\User;
+
+class NoUserException extends \Exception {} \ No newline at end of file
diff --git a/lib/private/util.php b/lib/private/util.php
index 9454d9f9226..6b5905f7b58 100644
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -628,25 +628,6 @@ class OC_Util {
$webServerRestart = true;
}
- /**
- * PHP 5.6 ships with a PHP setting which throws notices by default for a
- * lot of endpoints. Thus we need to ensure that the value is set to -1
- *
- * FIXME: Due to https://github.com/owncloud/core/pull/13593#issuecomment-71178078
- * this check is disabled for HHVM at the moment. This should get re-evaluated
- * at a later point.
- *
- * @link https://github.com/owncloud/core/issues/13592
- */
- if(version_compare(phpversion(), '5.6.0', '>=') &&
- !self::runningOnHhvm() &&
- \OC::$server->getIniWrapper()->getNumeric('always_populate_raw_post_data') !== -1) {
- $errors[] = array(
- 'error' => $l->t('PHP is configured to populate raw post data. Since PHP 5.6 this will lead to PHP throwing notices for perfectly valid code.'),
- 'hint' => $l->t('To fix this issue set <code>always_populate_raw_post_data</code> to <code>-1</code> in your php.ini')
- );
- }
-
if (!self::isAnnotationsWorking()) {
$errors[] = array(
'error' => $l->t('PHP is apparently setup to strip inline doc blocks. This will make several core apps inaccessible.'),
diff --git a/lib/public/appframework/controller.php b/lib/public/appframework/controller.php
index 00981df05ba..19b1294c0bb 100644
--- a/lib/public/appframework/controller.php
+++ b/lib/public/appframework/controller.php
@@ -28,6 +28,7 @@
namespace OCP\AppFramework;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
diff --git a/lib/public/idatetimezone.php b/lib/public/idatetimezone.php
index fb4c89538d5..24dc70a82fe 100644
--- a/lib/public/idatetimezone.php
+++ b/lib/public/idatetimezone.php
@@ -15,7 +15,8 @@ namespace OCP;
interface IDateTimeZone {
/**
+ * @param bool|int $timestamp
* @return \DateTimeZone
*/
- public function getTimeZone();
+ public function getTimeZone($timestamp = false);
}
diff --git a/ocs/v1.php b/ocs/v1.php
index 0a86fb06411..5b73809e6f8 100644
--- a/ocs/v1.php
+++ b/ocs/v1.php
@@ -23,7 +23,9 @@
require_once '../lib/base.php';
-if (\OCP\Util::needUpgrade()) {
+if (\OCP\Util::needUpgrade()
+ || \OC::$server->getSystemConfig()->getValue('maintenance', false)
+ || \OC::$server->getSystemConfig()->getValue('singleuser', false)) {
// since the behavior of apps or remotes are unpredictable during
// an upgrade, return a 503 directly
OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
diff --git a/public.php b/public.php
index c5c227ef460..8a43b3e466a 100644
--- a/public.php
+++ b/public.php
@@ -12,7 +12,7 @@ try {
}
OC::checkMaintenanceMode();
- OC::checkSingleUserMode();
+ OC::checkSingleUserMode(true);
$pathInfo = OC_Request::getPathInfo();
if (!$pathInfo && !isset($_GET['service'])) {
header('HTTP/1.0 404 Not Found');
diff --git a/tests/lib/db/migrator.php b/tests/lib/db/migrator.php
index 54267740480..6bde68c2d20 100644
--- a/tests/lib/db/migrator.php
+++ b/tests/lib/db/migrator.php
@@ -26,11 +26,17 @@ class Migrator extends \Test\TestCase {
*/
private $manager;
+ /**
+ * @var IConfig
+ **/
+ private $config;
+
private $tableName;
protected function setUp() {
parent::setUp();
+ $this->config = \OC::$server->getConfig();
$this->connection = \OC_DB::getConnection();
if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
$this->markTestSkipped('DB migration tests are not supported on OCI');
@@ -39,7 +45,7 @@ class Migrator extends \Test\TestCase {
$this->markTestSkipped('DB migration tests are not supported on MSSQL');
}
$this->manager = new \OC\DB\MDB2SchemaManager($this->connection);
- $this->tableName = strtolower($this->getUniqueID('oc_test_'));
+ $this->tableName = strtolower($this->getUniqueID($this->config->getSystemValue('dbtableprefix', 'oc_') . 'test_'));
}
protected function tearDown() {
@@ -109,6 +115,27 @@ class Migrator extends \Test\TestCase {
$this->assertTrue(true);
}
+ public function testUpgradeDifferentPrefix() {
+ $oldTablePrefix = $this->config->getSystemValue('dbtableprefix', 'oc_');
+
+ $this->config->setSystemValue('dbtableprefix', 'ownc_');
+ $this->tableName = strtolower($this->getUniqueID($this->config->getSystemValue('dbtableprefix') . 'test_'));
+
+ list($startSchema, $endSchema) = $this->getDuplicateKeySchemas();
+ $migrator = $this->manager->getMigrator();
+ $migrator->migrate($startSchema);
+
+ $this->connection->insert($this->tableName, array('id' => 1, 'name' => 'foo'));
+ $this->connection->insert($this->tableName, array('id' => 2, 'name' => 'bar'));
+ $this->connection->insert($this->tableName, array('id' => 3, 'name' => 'qwerty'));
+
+ $migrator->checkMigrate($endSchema);
+ $migrator->migrate($endSchema);
+ $this->assertTrue(true);
+
+ $this->config->setSystemValue('dbtableprefix', $oldTablePrefix);
+ }
+
public function testInsertAfterUpgrade() {
list($startSchema, $endSchema) = $this->getDuplicateKeySchemas();
$migrator = $this->manager->getMigrator();
diff --git a/tests/lib/files/filesystem.php b/tests/lib/files/filesystem.php
index 7bf59315d77..2da2937bb4f 100644
--- a/tests/lib/files/filesystem.php
+++ b/tests/lib/files/filesystem.php
@@ -22,7 +22,12 @@
namespace Test\Files;
+use OC\User\NoUserException;
+
class Filesystem extends \Test\TestCase {
+
+ const TEST_FILESYSTEM_USER1 = "test-filesystem-user1";
+
/**
* @var array tmpDirs
*/
@@ -244,8 +249,14 @@ class Filesystem extends \Test\TestCase {
if (\OC\Files\Filesystem::getView()) {
$user = \OC_User::getUser();
} else {
- $user = $this->getUniqueID();
+ $user = self::TEST_FILESYSTEM_USER1;
+ $backend = new \OC_User_Dummy();
+ \OC_User::useBackend($backend);
+ $backend->createUser($user, $user);
+ $userObj = \OC::$server->getUserManager()->get($user);
+ \OC::$server->getUserSession()->setUser($userObj);
\OC\Files\Filesystem::init($user, '/' . $user . '/files');
+
}
\OC_Hook::clear('OC_Filesystem');
\OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
@@ -267,19 +278,14 @@ class Filesystem extends \Test\TestCase {
}
/**
- * Tests that a local storage mount is used when passed user
- * does not exist.
+ * Tests that an exception is thrown when passed user does not exist.
+ * @expectedException \OC\User\NoUserException
*/
public function testLocalMountWhenUserDoesNotExist() {
$datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data");
$userId = $this->getUniqueID('user_');
\OC\Files\Filesystem::initMountPoints($userId);
-
- $homeMount = \OC\Files\Filesystem::getStorage('/' . $userId . '/');
-
- $this->assertTrue($homeMount->instanceOfStorage('\OC\Files\Storage\Local'));
- $this->assertEquals('local::' . $datadir . '/' . $userId . '/', $homeMount->getId());
}
/**
diff --git a/tests/lib/files/mount/mountpoint.php b/tests/lib/files/mount/mountpoint.php
index 5a9c6de3e0a..29610e6058d 100644
--- a/tests/lib/files/mount/mountpoint.php
+++ b/tests/lib/files/mount/mountpoint.php
@@ -31,6 +31,10 @@ class MountPoint extends \Test\TestCase {
$this->assertEquals($storage, $mountPoint->getStorage());
$this->assertEquals(123, $mountPoint->getStorageId());
+ $this->assertEquals('/mountpoint/', $mountPoint->getMountPoint());
+
+ $mountPoint->setMountPoint('another');
+ $this->assertEquals('/another/', $mountPoint->getMountPoint());
}
public function testInvalidStorage() {
diff --git a/tests/lib/preview.php b/tests/lib/preview.php
index 2a6761403f4..c7a58f84ab4 100644
--- a/tests/lib/preview.php
+++ b/tests/lib/preview.php
@@ -10,10 +10,7 @@ namespace Test;
class Preview extends TestCase {
- /**
- * @var string
- */
- private $user;
+ const TEST_PREVIEW_USER1 = "test-preview-user1";
/**
* @var \OC\Files\View
@@ -30,15 +27,18 @@ class Preview extends TestCase {
// create a new user with his own filesystem view
// this gets called by each test in this test class
- $this->user = $this->getUniqueID();
- \OC_User::setUserId($this->user);
- \OC\Files\Filesystem::init($this->user, '/' . $this->user . '/files');
+ $backend = new \OC_User_Dummy();
+ \OC_User::useBackend($backend);
+ $backend->createUser(self::TEST_PREVIEW_USER1, self::TEST_PREVIEW_USER1);
+ $user = \OC::$server->getUserManager()->get(self::TEST_PREVIEW_USER1);
+ \OC::$server->getUserSession()->setUser($user);
+ \OC\Files\Filesystem::init(self::TEST_PREVIEW_USER1, '/' . self::TEST_PREVIEW_USER1 . '/files');
\OC\Files\Filesystem::mount('OC\Files\Storage\Temporary', array(), '/');
$this->rootView = new \OC\Files\View('');
- $this->rootView->mkdir('/'.$this->user);
- $this->rootView->mkdir('/'.$this->user.'/files');
+ $this->rootView->mkdir('/'.self::TEST_PREVIEW_USER1);
+ $this->rootView->mkdir('/'.self::TEST_PREVIEW_USER1.'/files');
}
protected function tearDown() {
@@ -50,20 +50,20 @@ class Preview extends TestCase {
public function testIsPreviewDeleted() {
- $sampleFile = '/'.$this->user.'/files/test.txt';
+ $sampleFile = '/'.self::TEST_PREVIEW_USER1.'/files/test.txt';
$this->rootView->file_put_contents($sampleFile, 'dummy file data');
$x = 50;
$y = 50;
- $preview = new \OC\Preview($this->user, 'files/', 'test.txt', $x, $y);
+ $preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.txt', $x, $y);
$preview->getPreview();
$fileInfo = $this->rootView->getFileInfo($sampleFile);
$fileId = $fileInfo['fileid'];
- $thumbCacheFile = '/' . $this->user . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $x . '-' . $y . '.png';
+ $thumbCacheFile = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $x . '-' . $y . '.png';
$this->assertEquals($this->rootView->file_exists($thumbCacheFile), true);
@@ -74,20 +74,20 @@ class Preview extends TestCase {
public function testAreAllPreviewsDeleted() {
- $sampleFile = '/'.$this->user.'/files/test.txt';
+ $sampleFile = '/'.self::TEST_PREVIEW_USER1.'/files/test.txt';
$this->rootView->file_put_contents($sampleFile, 'dummy file data');
$x = 50;
$y = 50;
- $preview = new \OC\Preview($this->user, 'files/', 'test.txt', $x, $y);
+ $preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.txt', $x, $y);
$preview->getPreview();
$fileInfo = $this->rootView->getFileInfo($sampleFile);
$fileId = $fileInfo['fileid'];
- $thumbCacheFolder = '/' . $this->user . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/';
+ $thumbCacheFolder = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/';
$this->assertEquals($this->rootView->is_dir($thumbCacheFolder), true);
@@ -104,11 +104,11 @@ class Preview extends TestCase {
\OC_Config::setValue('preview_max_x', $maxX);
\OC_Config::setValue('preview_max_y', $maxY);
- $sampleFile = '/'.$this->user.'/files/test.txt';
+ $sampleFile = '/'.self::TEST_PREVIEW_USER1.'/files/test.txt';
$this->rootView->file_put_contents($sampleFile, 'dummy file data');
- $preview = new \OC\Preview($this->user, 'files/', 'test.txt', 1000, 1000);
+ $preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.txt', 1000, 1000);
$image = $preview->getPreview();
$this->assertEquals($image->width(), $maxX);
@@ -131,9 +131,9 @@ class Preview extends TestCase {
$x = 32;
$y = 32;
- $sample = '/'.$this->user.'/files/test.'.$extension;
+ $sample = '/'.self::TEST_PREVIEW_USER1.'/files/test.'.$extension;
$this->rootView->file_put_contents($sample, $data);
- $preview = new \OC\Preview($this->user, 'files/', 'test.'.$extension, $x, $y);
+ $preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.'.$extension, $x, $y);
$image = $preview->getPreview();
$resource = $image->resource();
@@ -149,7 +149,7 @@ class Preview extends TestCase {
public function testCreationFromCached() {
- $sampleFile = '/'.$this->user.'/files/test.txt';
+ $sampleFile = '/'.self::TEST_PREVIEW_USER1.'/files/test.txt';
$this->rootView->file_put_contents($sampleFile, 'dummy file data');
@@ -157,22 +157,22 @@ class Preview extends TestCase {
$x = 150;
$y = 150;
- $preview = new \OC\Preview($this->user, 'files/', 'test.txt', $x, $y);
+ $preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.txt', $x, $y);
$preview->getPreview();
$fileInfo = $this->rootView->getFileInfo($sampleFile);
$fileId = $fileInfo['fileid'];
- $thumbCacheFile = '/' . $this->user . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $x . '-' . $y . '.png';
+ $thumbCacheFile = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $x . '-' . $y . '.png';
$this->assertEquals($this->rootView->file_exists($thumbCacheFile), true);
// create smaller previews
- $preview = new \OC\Preview($this->user, 'files/', 'test.txt', 50, 50);
+ $preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.txt', 50, 50);
$isCached = $preview->isCached($fileId);
- $this->assertEquals($this->user . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/150-150.png', $isCached);
+ $this->assertEquals(self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/150-150.png', $isCached);
}
/*
diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php
index 42bb82968af..8f3d927be34 100644
--- a/tests/lib/share/share.php
+++ b/tests/lib/share/share.php
@@ -101,9 +101,16 @@ class Test_Share extends \Test\TestCase {
OC_Group::deleteGroup($this->group2);
OC_Group::deleteGroup($this->groupAndUser);
+ $this->logout();
parent::tearDown();
}
+ protected function setHttpHelper($httpHelper) {
+ \OC::$server->registerService('HTTPHelper', function () use ($httpHelper) {
+ return $httpHelper;
+ });
+ }
+
public function testShareInvalidShareType() {
$message = 'Share type foobar is not valid for test.txt';
try {
@@ -388,6 +395,45 @@ class Test_Share extends \Test\TestCase {
$this->assertSame(\OCP\Share::SHARE_TYPE_USER, $share['share_type']);
}
+ public function testGetShareFromOutsideFilesFolder() {
+ OC_User::setUserId($this->user1);
+ $view = new \OC\Files\View('/' . $this->user1 . '/');
+ $view->mkdir('files/test');
+ $view->mkdir('files/test/sub');
+
+ $view->mkdir('files_trashbin');
+ $view->mkdir('files_trashbin/files');
+
+ $fileInfo = $view->getFileInfo('files/test/sub');
+ $fileId = $fileInfo->getId();
+
+ $this->assertTrue(
+ OCP\Share::shareItem('folder', $fileId, OCP\Share::SHARE_TYPE_USER, $this->user2, \OCP\Constants::PERMISSION_READ),
+ 'Failed asserting that user 1 successfully shared "test/sub" with user 2.'
+ );
+
+ $result = OCP\Share::getItemShared('folder', $fileId, Test_Share_Backend::FORMAT_SOURCE);
+ $this->assertNotEmpty($result);
+
+ $result = OCP\Share::getItemSharedWithUser('folder', $fileId, $this->user2);
+ $this->assertNotEmpty($result);
+
+ $result = OCP\Share::getItemsSharedWithUser('folder', $this->user2);
+ $this->assertNotEmpty($result);
+
+ // move to trash (keeps file id)
+ $view->rename('files/test', 'files_trashbin/files/test');
+
+ $result = OCP\Share::getItemShared('folder', $fileId, Test_Share_Backend::FORMAT_SOURCE);
+ $this->assertEmpty($result, 'Share must not be returned for files outside of "files"');
+
+ $result = OCP\Share::getItemSharedWithUser('folder', $fileId, $this->user2);
+ $this->assertEmpty($result, 'Share must not be returned for files outside of "files"');
+
+ $result = OCP\Share::getItemsSharedWithUser('folder', $this->user2);
+ $this->assertEmpty($result, 'Share must not be returned for files outside of "files"');
+ }
+
public function testSetExpireDateInPast() {
OC_User::setUserId($this->user1);
$this->shareUserOneTestFileWithUserTwo();
@@ -975,10 +1021,58 @@ class Test_Share extends \Test\TestCase {
);
}
+ public function dataRemoteShareUrlCalls() {
+ return [
+ ['admin@localhost', 'localhost'],
+ ['admin@https://localhost', 'localhost'],
+ ['admin@http://localhost', 'localhost'],
+ ['admin@localhost/subFolder', 'localhost/subFolder'],
+ ];
+ }
+
+ /**
+ * @dataProvider dataRemoteShareUrlCalls
+ *
+ * @param string $shareWith
+ * @param string $urlHost
+ */
+ public function testRemoteShareUrlCalls($shareWith, $urlHost) {
+ $oldHttpHelper = \OC::$server->query('HTTPHelper');
+ $httpHelperMock = $this->getMockBuilder('OC\HttpHelper')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->setHttpHelper($httpHelperMock);
+
+ $httpHelperMock->expects($this->at(0))
+ ->method('post')
+ ->with($this->stringStartsWith('https://' . $urlHost . '/ocs/v1.php/cloud/shares'), $this->anything())
+ ->willReturn(['success' => false, 'result' => 'Exception']);
+ $httpHelperMock->expects($this->at(1))
+ ->method('post')
+ ->with($this->stringStartsWith('http://' . $urlHost . '/ocs/v1.php/cloud/shares'), $this->anything())
+ ->willReturn(['success' => true, 'result' => json_encode(['ocs' => ['meta' => ['statuscode' => 100]]])]);
+
+ \OCP\Share::shareItem('test', 'test.txt', \OCP\Share::SHARE_TYPE_REMOTE, $shareWith, \OCP\Constants::PERMISSION_READ);
+ $shares = \OCP\Share::getItemShared('test', 'test.txt');
+ $share = array_shift($shares);
+
+ $httpHelperMock->expects($this->at(0))
+ ->method('post')
+ ->with($this->stringStartsWith('https://' . $urlHost . '/ocs/v1.php/cloud/shares/' . $share['id'] . '/unshare'), $this->anything())
+ ->willReturn(['success' => false, 'result' => 'Exception']);
+ $httpHelperMock->expects($this->at(1))
+ ->method('post')
+ ->with($this->stringStartsWith('http://' . $urlHost . '/ocs/v1.php/cloud/shares/' . $share['id'] . '/unshare'), $this->anything())
+ ->willReturn(['success' => true, 'result' => json_encode(['ocs' => ['meta' => ['statuscode' => 100]]])]);
+
+ \OCP\Share::unshare('test', 'test.txt', \OCP\Share::SHARE_TYPE_REMOTE, $shareWith);
+ $this->setHttpHelper($oldHttpHelper);
+ }
+
/**
* @dataProvider dataProviderTestGroupItems
- * @param type $ungrouped
- * @param type $grouped
+ * @param array $ungrouped
+ * @param array $grouped
*/
function testGroupItems($ungrouped, $grouped) {
diff --git a/tests/lib/testcase.php b/tests/lib/testcase.php
index 1ea3aa13547..704912b22a4 100644
--- a/tests/lib/testcase.php
+++ b/tests/lib/testcase.php
@@ -168,5 +168,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase {
static protected function logout() {
\OC_Util::tearDownFS();
\OC_User::setUserId('');
+ // needed for fully logout
+ \OC::$server->getUserSession()->setUser(null);
}
}
diff --git a/tests/lib/util.php b/tests/lib/util.php
index e77aa53b72d..5fc5e87807f 100644
--- a/tests/lib/util.php
+++ b/tests/lib/util.php
@@ -54,24 +54,27 @@ class Test_Util extends \Test\TestCase {
public function formatDateWithTZFromSessionData() {
return array(
- array(3, 'October 13, 2012 at 2:53:25 PM GMT+3'),
- array(15, 'October 13, 2012 at 11:53:25 AM GMT+0'),
- array(-13, 'October 13, 2012 at 11:53:25 AM GMT+0'),
- array(9.5, 'October 13, 2012 at 9:23:25 PM GMT+9:30'),
- array(-4.5, 'October 13, 2012 at 7:23:25 AM GMT-4:30'),
- array(15.5, 'October 13, 2012 at 11:53:25 AM GMT+0'),
+ array(3, 'October 13, 2012 at 2:53:25 PM GMT+3', 'Etc/GMT-3'),
+ array(15, 'October 13, 2012 at 11:53:25 AM GMT+0', 'UTC'),
+ array(-13, 'October 13, 2012 at 11:53:25 AM GMT+0', 'UTC'),
+ array(9.5, 'October 13, 2012 at 9:23:25 PM GMT+9:30', 'Australia/Darwin'),
+ array(-4.5, 'October 13, 2012 at 7:23:25 AM GMT-4:30', 'America/Caracas'),
+ array(15.5, 'October 13, 2012 at 11:53:25 AM GMT+0', 'UTC'),
);
}
/**
* @dataProvider formatDateWithTZFromSessionData
*/
- function testFormatDateWithTZFromSession($offset, $expected) {
+ function testFormatDateWithTZFromSession($offset, $expected, $expectedTimeZone) {
date_default_timezone_set("UTC");
$oldDateTimeFormatter = \OC::$server->query('DateTimeFormatter');
\OC::$server->getSession()->set('timezone', $offset);
- $newDateTimeFormatter = new \OC\DateTimeFormatter(\OC::$server->getDateTimeZone()->getTimeZone(), new \OC_L10N('lib', 'en'));
+
+ $selectedTimeZone = \OC::$server->getDateTimeZone()->getTimeZone(1350129205);
+ $this->assertEquals($expectedTimeZone, $selectedTimeZone->getName());
+ $newDateTimeFormatter = new \OC\DateTimeFormatter($selectedTimeZone, new \OC_L10N('lib', 'en'));
$this->setDateFormatter($newDateTimeFormatter);
$result = OC_Util::formatDate(1350129205, false);
diff --git a/version.php b/version.php
index 8712f7a8593..98e64f14c55 100644
--- a/version.php
+++ b/version.php
@@ -3,10 +3,10 @@
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
// when updating major/minor version number.
-$OC_Version=array(8, 0, 3, 0);
+$OC_Version=array(8, 0, 3, 4);
// The human readable string
-$OC_VersionString='8.0.3 RC1';
+$OC_VersionString='8.0.3';
// The ownCloud channel
$OC_Channel='git';