summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorBjörn Schießle <schiessle@owncloud.com>2014-07-30 17:21:32 +0200
committerBjörn Schießle <schiessle@owncloud.com>2014-07-30 17:21:32 +0200
commitef3c0c508fc18fc0888097f920883cda7c5e1370 (patch)
treec38aaf3bc471d34cc64d4a270a53e2377a6a1ffc /apps
parentdd9347fdd2b1f516ee71d3fb9bbc391541a6d5dc (diff)
parent1a797f90bbf55f9602f26f3853f7459076ff6291 (diff)
downloadnextcloud-server-ef3c0c508fc18fc0888097f920883cda7c5e1370.tar.gz
nextcloud-server-ef3c0c508fc18fc0888097f920883cda7c5e1370.zip
Merge pull request #9897 from owncloud/support_webdav_copy
add support for webdav copy to the encryption and versions app
Diffstat (limited to 'apps')
-rw-r--r--apps/files_encryption/hooks/hooks.php43
-rwxr-xr-xapps/files_encryption/lib/helper.php4
-rw-r--r--apps/files_encryption/tests/hooks.php52
-rw-r--r--apps/files_versions/appinfo/app.php7
-rw-r--r--apps/files_versions/lib/hooks.php30
-rw-r--r--apps/files_versions/lib/versions.php70
-rw-r--r--apps/files_versions/tests/versions.php108
7 files changed, 272 insertions, 42 deletions
diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php
index 2cde8144757..667be8b9802 100644
--- a/apps/files_encryption/hooks/hooks.php
+++ b/apps/files_encryption/hooks/hooks.php
@@ -412,18 +412,44 @@ class Hooks {
'uid' => $ownerOld,
'path' => $pathOld,
'type' => $type,
+ 'operation' => 'rename',
);
+
}
}
/**
- * after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing
- * @param array $params array with oldpath and newpath
+ * mark file as renamed so that we know the original source after the file was renamed
+ * @param array $params with the old path and the new path
+ */
+ public static function preCopy($params) {
+ $user = \OCP\User::getUser();
+ $view = new \OC\Files\View('/');
+ $util = new Util($view, $user);
+ list($ownerOld, $pathOld) = $util->getUidAndFilename($params['oldpath']);
+
+ // we only need to rename the keys if the rename happens on the same mountpoint
+ // otherwise we perform a stream copy, so we get a new set of keys
+ $mp1 = $view->getMountPoint('/' . $user . '/files/' . $params['oldpath']);
+ $mp2 = $view->getMountPoint('/' . $user . '/files/' . $params['newpath']);
+
+ $type = $view->is_dir('/' . $user . '/files/' . $params['oldpath']) ? 'folder' : 'file';
+
+ if ($mp1 === $mp2) {
+ self::$renamedFiles[$params['oldpath']] = array(
+ 'uid' => $ownerOld,
+ 'path' => $pathOld,
+ 'type' => $type,
+ 'operation' => 'copy');
+ }
+ }
+
+ /**
+ * after a file is renamed/copied, rename/copy its keyfile and share-keys also fix the file size and fix also the sharing
*
- * This function is connected to the rename signal of OC_Filesystem and adjust the name and location
- * of the stored versions along the actual file
+ * @param array $params array with oldpath and newpath
*/
- public static function postRename($params) {
+ public static function postRenameOrCopy($params) {
if (\OCP\App::isEnabled('files_encryption') === false) {
return true;
@@ -442,6 +468,7 @@ class Hooks {
$ownerOld = self::$renamedFiles[$params['oldpath']]['uid'];
$pathOld = self::$renamedFiles[$params['oldpath']]['path'];
$type = self::$renamedFiles[$params['oldpath']]['type'];
+ $operation = self::$renamedFiles[$params['oldpath']]['operation'];
unset(self::$renamedFiles[$params['oldpath']]);
} else {
\OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::DEBUG);
@@ -484,17 +511,17 @@ class Hooks {
$matches = Helper::findShareKeys($oldShareKeyPath, $view);
foreach ($matches as $src) {
$dst = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $src));
- $view->rename($src, $dst);
+ $view->$operation($src, $dst);
}
} else {
// handle share-keys folders
- $view->rename($oldShareKeyPath, $newShareKeyPath);
+ $view->$operation($oldShareKeyPath, $newShareKeyPath);
}
// Rename keyfile so it isn't orphaned
if ($view->file_exists($oldKeyfilePath)) {
- $view->rename($oldKeyfilePath, $newKeyfilePath);
+ $view->$operation($oldKeyfilePath, $newKeyfilePath);
}
diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php
index fed0788028f..ed42cec326a 100755
--- a/apps/files_encryption/lib/helper.php
+++ b/apps/files_encryption/lib/helper.php
@@ -62,7 +62,9 @@ class Helper {
public static function registerFilesystemHooks() {
\OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Encryption\Hooks', 'preRename');
- \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename');
+ \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRenameOrCopy');
+ \OCP\Util::connectHook('OC_Filesystem', 'copy', 'OCA\Encryption\Hooks', 'preCopy');
+ \OCP\Util::connectHook('OC_Filesystem', 'post_copy', 'OCA\Encryption\Hooks', 'postRenameOrCopy');
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Encryption\Hooks', 'postDelete');
\OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Encryption\Hooks', 'preDelete');
\OCP\Util::connectHook('OC_Filesystem', 'post_umount', 'OCA\Encryption\Hooks', 'postUmount');
diff --git a/apps/files_encryption/tests/hooks.php b/apps/files_encryption/tests/hooks.php
index 5eda8df01b9..cc5b6d5b6f6 100644
--- a/apps/files_encryption/tests/hooks.php
+++ b/apps/files_encryption/tests/hooks.php
@@ -336,6 +336,58 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
}
/**
+ * test rename operation
+ */
+ function testCopyHook() {
+
+ // save file with content
+ $cryptedFile = file_put_contents('crypt:///' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename, $this->data);
+
+ // test that data was successfully written
+ $this->assertTrue(is_int($cryptedFile));
+
+ // check if keys exists
+ $this->assertTrue($this->rootView->file_exists(
+ '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
+ . $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
+
+ $this->assertTrue($this->rootView->file_exists(
+ '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/'
+ . $this->filename . '.key'));
+
+ // make subfolder and sub-subfolder
+ $this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder);
+ $this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder);
+
+ $this->assertTrue($this->rootView->is_dir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder));
+
+ // copy the file to the sub-subfolder
+ \OC\Files\Filesystem::copy($this->filename, '/' . $this->folder . '/' . $this->folder . '/' . $this->filename);
+
+ $this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename));
+ $this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder . '/' . $this->filename));
+
+ // keys should be copied too
+ $this->assertTrue($this->rootView->file_exists(
+ '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
+ . $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
+ $this->assertTrue($this->rootView->file_exists(
+ '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/'
+ . $this->filename . '.key'));
+
+ $this->assertTrue($this->rootView->file_exists(
+ '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' . $this->folder . '/' . $this->folder . '/'
+ . $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
+ $this->assertTrue($this->rootView->file_exists(
+ '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->folder . '/' . $this->folder . '/'
+ . $this->filename . '.key'));
+
+ // cleanup
+ $this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder);
+ $this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename);
+ }
+
+ /**
* @brief replacing encryption keys during password change should be allowed
* until the user logged in for the first time
*/
diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php
index 371162cd16f..8c517d4d0ff 100644
--- a/apps/files_versions/appinfo/app.php
+++ b/apps/files_versions/appinfo/app.php
@@ -8,9 +8,4 @@ OC::$CLASSPATH['OCA\Files_Versions\Capabilities'] = 'files_versions/lib/capabili
OCP\Util::addscript('files_versions', 'versions');
OCP\Util::addStyle('files_versions', 'versions');
-// Listen to write signals
-OCP\Util::connectHook('OC_Filesystem', 'write', "OCA\Files_Versions\Hooks", "write_hook");
-// Listen to delete and rename signals
-OCP\Util::connectHook('OC_Filesystem', 'post_delete', "OCA\Files_Versions\Hooks", "remove_hook");
-OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Versions\Hooks", "pre_remove_hook");
-OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA\Files_Versions\Hooks", "rename_hook");
+\OCA\Files_Versions\Hooks::connectHooks();
diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php
index 990f1403e8d..1a584232ba7 100644
--- a/apps/files_versions/lib/hooks.php
+++ b/apps/files_versions/lib/hooks.php
@@ -14,6 +14,16 @@ namespace OCA\Files_Versions;
class Hooks {
+ public static function connectHooks() {
+ // Listen to write signals
+ \OCP\Util::connectHook('OC_Filesystem', 'write', "OCA\Files_Versions\Hooks", "write_hook");
+ // Listen to delete and rename signals
+ \OCP\Util::connectHook('OC_Filesystem', 'post_delete', "OCA\Files_Versions\Hooks", "remove_hook");
+ \OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Versions\Hooks", "pre_remove_hook");
+ \OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA\Files_Versions\Hooks", "rename_hook");
+ \OCP\Util::connectHook('OC_Filesystem', 'copy', "OCA\Files_Versions\Hooks", "copy_hook");
+ }
+
/**
* listen to write event.
*/
@@ -69,7 +79,25 @@ class Hooks {
$oldpath = $params['oldpath'];
$newpath = $params['newpath'];
if($oldpath<>'' && $newpath<>'') {
- Storage::rename( $oldpath, $newpath );
+ Storage::renameOrCopy($oldpath, $newpath, 'rename');
+ }
+ }
+ }
+
+ /**
+ * copy versions of copied files
+ * @param array $params array with oldpath and newpath
+ *
+ * This function is connected to the copy signal of OC_Filesystem and copies the
+ * the stored versions to the new location
+ */
+ public static function copy_hook($params) {
+
+ if (\OCP\App::isEnabled('files_versions')) {
+ $oldpath = $params['oldpath'];
+ $newpath = $params['newpath'];
+ if($oldpath<>'' && $newpath<>'') {
+ Storage::renameOrCopy($oldpath, $newpath, 'copy');
}
}
}
diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php
index 2e048416c11..a9d51b2c58b 100644
--- a/apps/files_versions/lib/versions.php
+++ b/apps/files_versions/lib/versions.php
@@ -174,9 +174,12 @@ class Storage {
}
/**
- * rename versions of a file
+ * rename or copy versions of a file
+ * @param string $old_path
+ * @param string $new_path
+ * @param string $operation can be 'copy' or 'rename'
*/
- public static function rename($old_path, $new_path) {
+ public static function renameOrCopy($old_path, $new_path, $operation) {
list($uid, $oldpath) = self::getUidAndFilename($old_path);
list($uidn, $newpath) = self::getUidAndFilename($new_path);
$versions_view = new \OC\Files\View('/'.$uid .'/files_versions');
@@ -188,18 +191,21 @@ class Storage {
return self::store($new_path);
}
- self::expire($newpath);
-
if ( $files_view->is_dir($oldpath) && $versions_view->is_dir($oldpath) ) {
- $versions_view->rename($oldpath, $newpath);
+ $versions_view->$operation($oldpath, $newpath);
} else if ( ($versions = Storage::getVersions($uid, $oldpath)) ) {
// create missing dirs if necessary
self::createMissingDirectories($newpath, new \OC\Files\View('/'. $uidn));
foreach ($versions as $v) {
- $versions_view->rename($oldpath.'.v'.$v['version'], $newpath.'.v'.$v['version']);
+ $versions_view->$operation($oldpath.'.v'.$v['version'], $newpath.'.v'.$v['version']);
}
}
+
+ if (!$files_view->is_dir($newpath)) {
+ self::expire($newpath);
+ }
+
}
/**
@@ -254,34 +260,46 @@ class Storage {
public static function getVersions($uid, $filename, $userFullPath = '') {
$versions = array();
// fetch for old versions
- $view = new \OC\Files\View('/' . $uid . '/' . self::VERSIONS_ROOT);
+ $view = new \OC\Files\View('/' . $uid . '/');
$pathinfo = pathinfo($filename);
+ $versionedFile = $pathinfo['basename'];
- $files = $view->getDirectoryContent($pathinfo['dirname']);
+ $dir = self::VERSIONS_ROOT . '/' . $pathinfo['dirname'];
- $versionedFile = $pathinfo['basename'];
+ $dirContent = false;
+ if ($view->is_dir($dir)) {
+ $dirContent = $view->opendir($dir);
+ }
- foreach ($files as $file) {
- if ($file['type'] === 'file') {
- $pos = strrpos($file['path'], '.v');
- $currentFile = substr($file['name'], 0, strrpos($file['name'], '.v'));
- if ($currentFile === $versionedFile) {
- $version = substr($file['path'], $pos + 2);
- $key = $version . '#' . $filename;
- $versions[$key]['cur'] = 0;
- $versions[$key]['version'] = $version;
- $versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($version);
- if (empty($userFullPath)) {
- $versions[$key]['preview'] = '';
- } else {
- $versions[$key]['preview'] = \OCP\Util::linkToRoute('core_ajax_versions_preview', array('file' => $userFullPath, 'version' => $version));
+ if ($dirContent === false) {
+ return $versions;
+ }
+
+ if (is_resource($dirContent)) {
+ while (($entryName = readdir($dirContent)) !== false) {
+ if (!\OC\Files\Filesystem::isIgnoredDir($entryName)) {
+ $pathparts = pathinfo($entryName);
+ $filename = $pathparts['filename'];
+ if ($filename === $versionedFile) {
+ $pathparts = pathinfo($entryName);
+ $timestamp = substr($pathparts['extension'], 1);
+ $filename = $pathparts['filename'];
+ $key = $timestamp . '#' . $filename;
+ $versions[$key]['version'] = $timestamp;
+ $versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($timestamp);
+ if (empty($userFullPath)) {
+ $versions[$key]['preview'] = '';
+ } else {
+ $versions[$key]['preview'] = \OCP\Util::linkToRoute('core_ajax_versions_preview', array('file' => $userFullPath, 'version' => $timestamp));
+ }
+ $versions[$key]['path'] = $filename;
+ $versions[$key]['name'] = $versionedFile;
+ $versions[$key]['size'] = $view->filesize($dir . '/' . $entryName);
}
- $versions[$key]['path'] = $filename;
- $versions[$key]['name'] = $versionedFile;
- $versions[$key]['size'] = $file['size'];
}
}
+ closedir($dirContent);
}
// sort with newest version first
diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php
index aa66faffcbf..03432276358 100644
--- a/apps/files_versions/tests/versions.php
+++ b/apps/files_versions/tests/versions.php
@@ -20,6 +20,7 @@
*
*/
+require_once __DIR__ . '/../appinfo/app.php';
require_once __DIR__ . '/../lib/versions.php';
/**
@@ -28,6 +29,32 @@ require_once __DIR__ . '/../lib/versions.php';
*/
class Test_Files_Versioning extends \PHPUnit_Framework_TestCase {
+ const TEST_VERSIONS_USER = 'test-versions-user';
+ const USERS_VERSIONS_ROOT = '/test-versions-user/files_versions';
+
+ private $rootView;
+
+ public static function setUpBeforeClass() {
+ // create test user
+ self::loginHelper(self::TEST_VERSIONS_USER, true);
+ }
+
+ public static function tearDownAfterClass() {
+ // cleanup test user
+ \OC_User::deleteUser(self::TEST_VERSIONS_USER);
+ }
+
+ function setUp() {
+ self::loginHelper(self::TEST_VERSIONS_USER);
+ $this->rootView = new \OC\Files\View();
+ if (!$this->rootView->file_exists(self::USERS_VERSIONS_ROOT)) {
+ $this->rootView->mkdir(self::USERS_VERSIONS_ROOT);
+ }
+ }
+
+ function tearDown() {
+ $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT);
+ }
/**
* @medium
@@ -176,6 +203,87 @@ class Test_Files_Versioning extends \PHPUnit_Framework_TestCase {
);
}
+ function testRename() {
+
+ \OC\Files\Filesystem::file_put_contents("test.txt", "test file");
+
+ $t1 = time();
+ // second version is two weeks older, this way we make sure that no
+ // version will be expired
+ $t2 = $t1 - 60 * 60 * 24 * 14;
+
+ // create some versions
+ $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
+ $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
+ $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
+ $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
+
+ $this->rootView->file_put_contents($v1, 'version1');
+ $this->rootView->file_put_contents($v2, 'version2');
+
+ // execute rename hook of versions app
+ \OCA\Files_Versions\Storage::renameOrCopy("test.txt", "test2.txt", 'rename');
+
+ $this->assertFalse($this->rootView->file_exists($v1));
+ $this->assertFalse($this->rootView->file_exists($v2));
+
+ $this->assertTrue($this->rootView->file_exists($v1Renamed));
+ $this->assertTrue($this->rootView->file_exists($v2Renamed));
+
+ //cleanup
+ \OC\Files\Filesystem::unlink('test2.txt');
+ }
+
+ function testCopy() {
+
+ \OC\Files\Filesystem::file_put_contents("test.txt", "test file");
+
+ $t1 = time();
+ // second version is two weeks older, this way we make sure that no
+ // version will be expired
+ $t2 = $t1 - 60 * 60 * 24 * 14;
+
+ // create some versions
+ $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
+ $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
+ $v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
+ $v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
+
+ $this->rootView->file_put_contents($v1, 'version1');
+ $this->rootView->file_put_contents($v2, 'version2');
+
+ // execute copy hook of versions app
+ \OCA\Files_Versions\Storage::renameOrCopy("test.txt", "test2.txt", 'copy');
+
+ $this->assertTrue($this->rootView->file_exists($v1));
+ $this->assertTrue($this->rootView->file_exists($v2));
+
+ $this->assertTrue($this->rootView->file_exists($v1Copied));
+ $this->assertTrue($this->rootView->file_exists($v2Copied));
+
+ //cleanup
+ \OC\Files\Filesystem::unlink('test.txt');
+ \OC\Files\Filesystem::unlink('test2.txt');
+ }
+
+ /**
+ * @param string $user
+ * @param bool $create
+ * @param bool $password
+ */
+ public static function loginHelper($user, $create = false) {
+
+ if ($create) {
+ \OC_User::createUser($user, $user);
+ }
+
+ \OC_Util::tearDownFS();
+ \OC_User::setUserId('');
+ \OC\Files\Filesystem::tearDown();
+ \OC_User::setUserId($user);
+ \OC_Util::setupFS($user);
+ }
+
}
// extend the original class to make it possible to test protected methods