diff options
Diffstat (limited to 'apps/files_encryption/lib')
-rwxr-xr-x | apps/files_encryption/lib/helper.php | 27 | ||||
-rw-r--r-- | apps/files_encryption/lib/proxy.php | 42 | ||||
-rw-r--r-- | apps/files_encryption/lib/stream.php | 38 | ||||
-rw-r--r-- | apps/files_encryption/lib/util.php | 192 |
4 files changed, 161 insertions, 138 deletions
diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index 17bcac5c585..5dcb05fa196 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -29,6 +29,8 @@ namespace OCA\Encryption; */ class Helper { + private static $tmpFileMapping; // Map tmp files to files in data/user/files + /** * @brief register share related hooks * @@ -59,6 +61,7 @@ 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'); } @@ -274,7 +277,7 @@ class Helper { $split = explode('/', $trimmed); // it is not a file relative to data/user/files - if (count($split) < 2 || $split[1] !== 'files') { + if (count($split) < 2 || ($split[1] !== 'files' && $split[1] !== 'cache')) { return false; } @@ -423,5 +426,27 @@ class Helper { public static function escapeGlobPattern($path) { return preg_replace('/(\*|\?|\[)/', '[$1]', $path); } + + /** + * @brief remember from which file the tmp file (getLocalFile() call) was created + * @param string $tmpFile path of tmp file + * @param string $originalFile path of the original file relative to data/ + */ + public static function addTmpFileToMapper($tmpFile, $originalFile) { + self::$tmpFileMapping[$tmpFile] = $originalFile; + } + + /** + * @brief get the path of the original file + * @param string $tmpFile path of the tmp file + * @return mixed path of the original file or false + */ + public static function getPathFromTmpFile($tmpFile) { + if (isset(self::$tmpFileMapping[$tmpFile])) { + return self::$tmpFileMapping[$tmpFile]; + } + + return false; + } } diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 5ba3bfa784f..4e71ab1dd5d 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -37,6 +37,7 @@ namespace OCA\Encryption; class Proxy extends \OC_FileProxy { private static $blackList = null; //mimetypes blacklisted from encryption + private static $unencryptedSizes = array(); // remember unencrypted size /** * Check if a file requires encryption @@ -114,6 +115,13 @@ class Proxy extends \OC_FileProxy { // get encrypted content $data = $view->file_get_contents($tmpPath); + // store new unenecrypted size so that it can be updated + // in the post proxy + $tmpFileInfo = $view->getFileInfo($tmpPath); + if ( isset($tmpFileInfo['size']) ) { + self::$unencryptedSizes[\OC_Filesystem::normalizePath($path)] = $tmpFileInfo['size']; + } + // remove our temp file $view->deleteAll('/' . \OCP\User::getUser() . '/cache/' . $cacheFolder); @@ -128,6 +136,24 @@ class Proxy extends \OC_FileProxy { } /** + * @brief update file cache with the new unencrypted size after file was written + * @param string $path + * @param mixed $result + * @return mixed + */ + public function postFile_put_contents($path, $result) { + $normalizedPath = \OC_Filesystem::normalizePath($path); + if ( isset(self::$unencryptedSizes[$normalizedPath]) ) { + $view = new \OC_FilesystemView('/'); + $view->putFileInfo($normalizedPath, + array('encrypted' => true, 'encrypted_size' => self::$unencryptedSizes[$normalizedPath])); + unset(self::$unencryptedSizes[$normalizedPath]); + } + + return $result; + } + + /** * @param string $path Path of file from which has been read * @param string $data Data that has been read from file */ @@ -182,8 +208,11 @@ class Proxy extends \OC_FileProxy { */ public function preUnlink($path) { - // let the trashbin handle this - if (\OCP\App::isEnabled('files_trashbin')) { + $relPath = Helper::stripUserFilesPath($path); + + // skip this method if the trash bin is enabled or if we delete a file + // outside of /data/user/files + if (\OCP\App::isEnabled('files_trashbin') || $relPath === false) { return true; } @@ -197,10 +226,7 @@ class Proxy extends \OC_FileProxy { $util = new Util($view, $userId); - // get relative path - $relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path); - - list($owner, $ownerPath) = $util->getUidAndFilename($relativePath); + list($owner, $ownerPath) = $util->getUidAndFilename($relPath); // Delete keyfile & shareKey so it isn't orphaned if (!Keymanager::deleteFileKey($view, $ownerPath)) { @@ -246,8 +272,8 @@ class Proxy extends \OC_FileProxy { // split the path parts $pathParts = explode('/', $path); - // FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted - if (isset($pathParts[2]) && $pathParts[2] === 'cache') { + // don't try to encrypt/decrypt cache chunks or files in the trash bin + if (isset($pathParts[2]) && ($pathParts[2] === 'cache' || $pathParts[2] === 'files_trashbin')) { return $result; } diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 7a37d2200a4..b3bf34ddb82 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -64,6 +64,9 @@ class Stream { private $publicKey; private $encKeyfile; private $newFile; // helper var, we only need to write the keyfile for new files + private $isLocalTmpFile = false; // do we operate on a local tmp file + private $localTmpFile; // path of local tmp file + /** * @var \OC\Files\View */ @@ -91,13 +94,18 @@ class Stream { $this->rootView = new \OC_FilesystemView('/'); } - $this->session = new \OCA\Encryption\Session($this->rootView); $this->privateKey = $this->session->getPrivateKey(); - // rawPath is relative to the data directory - $this->rawPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); + $normalizedPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path)); + if ($originalFile = Helper::getPathFromTmpFile($normalizedPath)) { + $this->rawPath = $originalFile; + $this->isLocalTmpFile = true; + $this->localTmpFile = $normalizedPath; + } else { + $this->rawPath = $normalizedPath; + } $this->userId = Helper::getUser($this->rawPath); @@ -141,10 +149,14 @@ class Stream { \OCA\Encryption\Helper::redirectToErrorPage($this->session); } - $this->size = $this->rootView->filesize($this->rawPath, $mode); + $this->size = $this->rootView->filesize($this->rawPath); } - $this->handle = $this->rootView->fopen($this->rawPath, $mode); + if ($this->isLocalTmpFile) { + $this->handle = fopen($this->localTmpFile, $mode); + } else { + $this->handle = $this->rootView->fopen($this->rawPath, $mode); + } \OC_FileProxy::$enabled = $proxyStatus; @@ -164,14 +176,25 @@ class Stream { } /** + * @brief Returns the current position of the file pointer + * @return int position of the file pointer + */ + public function stream_tell() { + return ftell($this->handle); + } + + /** * @param $offset * @param int $whence + * @return bool true if fseek was successful, otherwise false */ public function stream_seek($offset, $whence = SEEK_SET) { $this->flush(); - fseek($this->handle, $offset, $whence); + // this wrapper needs to return "true" for success. + // the fseek call itself returns 0 on succeess + return !fseek($this->handle, $offset, $whence); } @@ -477,7 +500,7 @@ class Stream { if ($this->privateKey === false) { // cleanup - if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb') { + if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb' && !$this->isLocalTmpFile) { // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; @@ -498,6 +521,7 @@ class Stream { if ( $this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb' && + $this->isLocalTmpFile === false && $this->size > 0 && $this->unencryptedSize > 0 ) { diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index bf7c49504a2..8816d4d649a 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -2,9 +2,10 @@ /** * ownCloud * - * @author Sam Tuke, Frank Karlitschek + * @author Sam Tuke, Frank Karlitschek, Bjoern Schiessle * @copyright 2012 Sam Tuke <samtuke@owncloud.com>, - * Frank Karlitschek <frank@owncloud.org> + * Frank Karlitschek <frank@owncloud.org>, + * Bjoern Schiessle <schiessle@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 @@ -241,11 +242,9 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); - if (isset($row['recovery_enabled'])) { - $recoveryEnabled[] = $row['recovery_enabled']; - } + $row = $result->fetchRow(); + if ($row && isset($row['recovery_enabled'])) { + $recoveryEnabled[] = $row['recovery_enabled']; } } @@ -289,7 +288,7 @@ class Util { $sql = 'UPDATE `*PREFIX*encryption` SET `recovery_enabled` = ? WHERE `uid` = ?'; $args = array( - $enabled, + $enabled ? '1' : '0', $this->userId ); @@ -415,69 +414,42 @@ class Util { } /** - * @brief Fetch the last lines of a file efficiently - * @note Safe to use on large files; does not read entire file to memory - * @note Derivative of http://tekkie.flashbit.net/php/tail-functionality-in-php - */ - public function tail($filename, $numLines) { - - \OC_FileProxy::$enabled = false; - - $text = ''; - $pos = -1; - $handle = $this->view->fopen($filename, 'r'); - - while ($numLines > 0) { - - --$pos; - - if (fseek($handle, $pos, SEEK_END) !== 0) { - - rewind($handle); - $numLines = 0; - - } elseif (fgetc($handle) === "\n") { - - --$numLines; - - } - - $block_size = (-$pos) % 8192; - if ($block_size === 0 || $numLines === 0) { - - $text = fread($handle, ($block_size === 0 ? 8192 : $block_size)) . $text; - - } - } - - fclose($handle); - - \OC_FileProxy::$enabled = true; - - return $text; - } - - /** * @brief Check if a given path identifies an encrypted file * @param string $path * @return boolean */ public function isEncryptedPath($path) { - $relPath = Helper::getPathToRealFile($path); - - if ($relPath === false) { - $relPath = Helper::stripUserFilesPath($path); - } - - $fileKey = Keymanager::getFileKey($this->view, $this, $relPath); + // Disable encryption proxy so data retrieved is in its + // original form + $proxyStatus = \OC_FileProxy::$enabled; + \OC_FileProxy::$enabled = false; - if ($fileKey === false) { - return false; + // we only need 24 byte from the last chunk + $data = ''; + $handle = $this->view->fopen($path, 'r'); + if (is_resource($handle)) { + // suppress fseek warining, we handle the case that fseek doesn't + // work in the else branch + if (@fseek($handle, -24, SEEK_END) === 0) { + $data = fgets($handle); + } else { + // if fseek failed on the storage we create a local copy from the file + // and read this one + fclose($handle); + $localFile = $this->view->getLocalFile($path); + $handle = fopen($localFile, 'r'); + if (is_resource($handle) && fseek($handle, -24, SEEK_END) === 0) { + $data = fgets($handle); + } + } + fclose($handle); } - return true; + // re-enable proxy + \OC_FileProxy::$enabled = $proxyStatus; + return Crypt::isCatfileContent($data); } /** @@ -523,7 +495,20 @@ class Util { $lastChunckPos = ($lastChunkNr * 8192); // seek to end - fseek($stream, $lastChunckPos); + if (@fseek($stream, $lastChunckPos) === -1) { + // storage doesn't support fseek, we need a local copy + fclose($stream); + $localFile = $this->view->getLocalFile($path); + Helper::addTmpFileToMapper($localFile, $path); + $stream = fopen('crypt://' . $localFile, "r"); + if (fseek($stream, $lastChunckPos) === -1) { + // if fseek also fails on the local storage, than + // there is nothing we can do + fclose($stream); + \OCP\Util::writeLog('Encryption library', 'couldn\'t determine size of "' . $path, \OCP\Util::ERROR); + return $result; + } + } // get the content of the last chunk $lastChunkContent = fread($stream, $lastChunkSize); @@ -985,8 +970,8 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); + $row = $result->fetchRow(); + if ($row) { $path = substr($row['path'], strlen('files')); } } @@ -1266,11 +1251,9 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); - if (isset($row['migration_status'])) { - $migrationStatus[] = $row['migration_status']; - } + $row = $result->fetchRow(); + if ($row && isset($row['migration_status'])) { + $migrationStatus[] = $row['migration_status']; } } @@ -1378,59 +1361,32 @@ class Util { } } - /** * @brief go recursively through a dir and collect all files and sub files. * @param string $dir relative to the users files folder * @return array with list of files relative to the users files folder */ public function getAllFiles($dir) { - $result = array(); + $dirList = array($dir); - $content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath( - $this->userFilesDir . '/' . $dir)); - - // handling for re shared folders - $pathSplit = explode('/', $dir); - - foreach ($content as $c) { - - $sharedPart = $pathSplit[sizeof($pathSplit) - 1]; - $targetPathSplit = array_reverse(explode('/', $c['path'])); - - $path = ''; - - // rebuild path - foreach ($targetPathSplit as $pathPart) { - - if ($pathPart !== $sharedPart) { - - $path = '/' . $pathPart . $path; + while ($dirList) { + $dir = array_pop($dirList); + $content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath( + $this->userFilesDir . '/' . $dir)); + foreach ($content as $c) { + $usersPath = isset($c['usersPath']) ? $c['usersPath'] : $c['path']; + if ($c['type'] === 'dir') { + $dirList[] = substr($usersPath, strlen("files")); } else { - - break; - + $result[] = substr($usersPath, strlen("files")); } - } - $path = $dir . $path; - - if ($c['type'] === 'dir') { - - $result = array_merge($result, $this->getAllFiles($path)); - - } else { - - $result[] = $path; - - } } return $result; - } /** @@ -1450,9 +1406,7 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); - } + $row = $result->fetchRow(); } return $row; @@ -1476,9 +1430,7 @@ class Util { if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); - } + $row = $result->fetchRow(); } return $row; @@ -1497,18 +1449,16 @@ class Util { $result = $query->execute(array($id)); - $source = array(); + $source = null; if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $source = $result->fetchRow(); - } + $source = $result->fetchRow(); } $fileOwner = false; - if (isset($source['parent'])) { + if ($source && isset($source['parent'])) { $parent = $source['parent']; @@ -1518,16 +1468,14 @@ class Util { $result = $query->execute(array($parent)); - $item = array(); + $item = null; if (\OCP\DB::isError($result)) { \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); } else { - if ($result->numRows() > 0) { - $item = $result->fetchRow(); - } + $item = $result->fetchRow(); } - if (isset($item['parent'])) { + if ($item && isset($item['parent'])) { $parent = $item['parent']; |