diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php
index 2cde8144757cf72826770a1c88585e9f67b65fa9..667be8b98027524f12870d34433603d679df68e1 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 fed0788028faff159f71c9f6cb8337bdc45cf960..ed42cec326af743a2b92f723a217cb43ff79d5fb 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 5eda8df01b9dfa2fbc3aae82f6ccfe5171c36528..cc5b6d5b6f645df06bad1477682e49679937a18f 100644
--- a/apps/files_encryption/tests/hooks.php
+++ b/apps/files_encryption/tests/hooks.php
@@ -335,6 +335,58 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
 		$this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder);
 	}
 
+	/**
+	 * 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 371162cd16f1c649632eff37ff89c5029fe382c3..8c517d4d0ff02dd83ed8a5b7f279563c50c72e28 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 990f1403e8d1b2789f87b5e9144dfe19607d8967..1a584232ba74e72296849ffba02fa6fb766d1c1c 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 2e048416c11c13e56e36d6a3867676994f778320..a9d51b2c58b644fc1ee50f0105075762a6c62ad9 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 aa66faffcbfe0a19a304b4f0aef5f4fc751140db..03432276358cd95b745d7e765e78176b237f5104 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