2013-01-18 16:11:29 +04:00
< ? php
2013-01-31 21:04:00 +04:00
/**
* ownCloud - trash bin
*
* @ author Bjoern Schiessle
* @ copyright 2013 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
* 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 />.
*
*/
2013-02-08 03:11:54 +04:00
namespace OCA\Files_Trashbin ;
2013-01-18 16:11:29 +04:00
class Trashbin {
2013-02-15 01:46:28 +04:00
// how long do we keep files in the trash bin if no other value is defined in the config file (unit: days)
const DEFAULT_RETENTION_OBLIGATION = 180 ;
// unit: percentage; 50% of available disk space/quota
const DEFAULTMAXSIZE = 50 ;
2013-02-22 20:21:57 +04:00
2013-01-18 16:11:29 +04:00
/**
* move file to the trash bin
2013-02-22 20:21:57 +04:00
*
2013-01-18 16:11:29 +04:00
* @ param $file_path path to the deleted file / directory relative to the files root directory
*/
public static function move2trash ( $file_path ) {
$user = \OCP\User :: getUser ();
2013-01-31 21:04:00 +04:00
$view = new \OC\Files\View ( '/' . $user );
if ( ! $view -> is_dir ( 'files_trashbin' )) {
$view -> mkdir ( 'files_trashbin' );
2013-04-20 01:21:06 +04:00
$view -> mkdir ( 'files_trashbin/files' );
$view -> mkdir ( 'files_trashbin/versions' );
$view -> mkdir ( 'files_trashbin/keyfiles' );
$view -> mkdir ( 'files_trashbin/share-keys' );
2013-01-31 21:04:00 +04:00
}
$path_parts = pathinfo ( $file_path );
2013-04-19 12:31:42 +04:00
$filename = $path_parts [ 'basename' ];
2013-01-31 21:04:00 +04:00
$location = $path_parts [ 'dirname' ];
$timestamp = time ();
$mime = $view -> getMimeType ( 'files' . $file_path );
if ( $view -> is_dir ( 'files' . $file_path ) ) {
$type = 'dir' ;
} else {
$type = 'file' ;
}
2013-01-29 16:36:33 +04:00
2013-02-25 17:29:31 +04:00
$trashbinSize = self :: getTrashbinSize ( $user );
2013-02-25 14:14:06 +04:00
if ( $trashbinSize === false || $trashbinSize < 0 ) {
2013-02-27 23:29:19 +04:00
$trashbinSize = self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/files_trashbin' ));
2013-02-07 18:16:29 +04:00
}
2013-04-09 17:16:15 +04:00
2013-04-19 12:31:42 +04:00
$sizeOfAddedFiles = self :: copy_recursive ( $file_path , 'files_trashbin/files/' . $filename . '.d' . $timestamp , $view );
2013-04-22 05:37:55 +04:00
2013-04-19 12:31:42 +04:00
if ( $view -> file_exists ( 'files_trashbin/files/' . $filename . '.d' . $timestamp ) ) {
2013-04-09 17:16:15 +04:00
$trashbinSize += $sizeOfAddedFiles ;
2013-03-22 15:47:43 +04:00
$query = \OC_DB :: prepare ( " INSERT INTO `*PREFIX*files_trash` (`id`,`timestamp`,`location`,`type`,`mime`,`user`) VALUES (?,?,?,?,?,?) " );
2013-04-19 12:31:42 +04:00
$result = $query -> execute ( array ( $filename , $timestamp , $location , $type , $mime , $user ));
2013-01-29 16:36:33 +04:00
if ( ! $result ) { // if file couldn't be added to the database than also don't store it in the trash bin.
2013-04-19 12:31:42 +04:00
$view -> deleteAll ( 'files_trashbin/files/' . $filename . '.d' . $timestamp );
2013-01-29 16:36:33 +04:00
\OC_Log :: write ( 'files_trashbin' , 'trash bin database couldn\'t be updated' , \OC_log :: ERROR );
return ;
2013-01-31 21:04:00 +04:00
}
2013-03-01 15:44:43 +04:00
\OCP\Util :: emitHook ( '\OCA\Files_Trashbin\Trashbin' , 'post_moveToTrash' ,
array ( 'filePath' => \OC\Files\Filesystem :: normalizePath ( $file_path ),
2013-04-19 12:31:42 +04:00
'trashPath' => \OC\Files\Filesystem :: normalizePath ( $filename . '.d' . $timestamp )));
$trashbinSize += self :: retainVersions ( $view , $file_path , $filename , $timestamp );
$trashbinSize += self :: retainEncryptionKeys ( $view , $file_path , $filename , $timestamp );
2013-01-29 16:36:33 +04:00
} else {
2013-02-09 20:35:47 +04:00
\OC_Log :: write ( 'files_trashbin' , 'Couldn\'t move ' . $file_path . ' to the trash bin' , \OC_log :: ERROR );
2013-01-18 16:11:29 +04:00
}
2013-04-16 15:51:53 +04:00
$trashbinSize -= self :: expire ( $trashbinSize );
2013-02-21 15:37:13 +04:00
2013-02-25 17:29:31 +04:00
self :: setTrashbinSize ( $user , $trashbinSize );
2013-02-25 14:14:06 +04:00
2013-01-18 16:11:29 +04:00
}
2013-02-22 20:21:57 +04:00
2013-04-20 01:21:06 +04:00
/**
* Move file versions to trash so that they can be restored later
*
* @ param \OC\Files\View $view
* @ param $file_path path to original file
* @ param $filename of deleted file
* @ param $timestamp when the file was deleted
*
* @ return size of stored versions
*/
2013-04-19 12:31:42 +04:00
private static function retainVersions ( $view , $file_path , $filename , $timestamp ) {
$size = 0 ;
2013-04-20 01:21:06 +04:00
if ( \OCP\App :: isEnabled ( 'files_versions' )) {
2013-04-28 01:51:26 +04:00
// disable proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
$user = \OCP\User :: getUser ();
2013-04-19 12:31:42 +04:00
if ( $view -> is_dir ( 'files_versions/' . $file_path )) {
2013-04-28 01:51:26 +04:00
$size += self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/files_versions/' . $file_path ));
$view -> rename ( 'files_versions/' . $file_path , 'files_trashbin/versions/' . $filename . '.d' . $timestamp );
2013-04-19 12:31:42 +04:00
} else if ( $versions = \OCA\Files_Versions\Storage :: getVersions ( $user , $file_path )) {
2013-04-28 01:51:26 +04:00
foreach ( $versions as $v ) {
2013-04-19 12:31:42 +04:00
$size += $view -> filesize ( 'files_versions' . $v [ 'path' ] . '.v' . $v [ 'version' ]);
2013-04-28 01:51:26 +04:00
$view -> rename ( 'files_versions' . $v [ 'path' ] . '.v' . $v [ 'version' ], 'files_trashbin/versions/' . $filename . '.v' . $v [ 'version' ] . '.d' . $timestamp );
2013-04-19 12:31:42 +04:00
}
}
2013-04-28 01:51:26 +04:00
// enable proxy
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-19 12:31:42 +04:00
}
return $size ;
}
2013-04-20 01:21:06 +04:00
/**
* Move encryption keys to trash so that they can be restored later
*
* @ param \OC\Files\View $view
* @ param $file_path path to original file
* @ param $filename of deleted file
* @ param $timestamp when the file was deleted
*
* @ return size of encryption keys
*/
2013-04-19 12:31:42 +04:00
private static function retainEncryptionKeys ( $view , $file_path , $filename , $timestamp ) {
$size = 0 ;
if ( \OCP\App :: isEnabled ( 'files_encryption' )) {
$user = \OCP\User :: getUser ();
2013-04-20 01:21:06 +04:00
// disable proxy to prevent recursive calls
2013-04-27 02:15:46 +04:00
$proxyStatus = \OC_FileProxy :: $enabled ;
2013-04-20 01:21:06 +04:00
\OC_FileProxy :: $enabled = false ;
2013-04-27 02:15:46 +04:00
// retain key files
2013-04-19 12:31:42 +04:00
$keyfile = \OC\Files\Filesystem :: normalizePath ( 'files_encryption/keyfiles/' . $file_path );
2013-04-22 05:37:55 +04:00
2013-04-20 01:21:06 +04:00
if ( $view -> is_dir ( $keyfile ) || $view -> file_exists ( $keyfile . '.key' )) {
$user = \OCP\User :: getUser ();
2013-04-27 02:15:46 +04:00
// move keyfiles
2013-04-20 01:21:06 +04:00
if ( $view -> is_dir ( $keyfile )) {
2013-04-27 02:15:46 +04:00
$size += self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/' . $keyfile ));
2013-04-19 12:31:42 +04:00
$view -> rename ( $keyfile , 'files_trashbin/keyfiles/' . $filename . '.d' . $timestamp );
} else {
$size += $view -> filesize ( $keyfile . '.key' );
2013-04-20 01:21:06 +04:00
$view -> rename ( $keyfile . '.key' , 'files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp );
2013-04-19 12:31:42 +04:00
}
}
2013-04-27 02:15:46 +04:00
// retain share keys
2013-04-19 12:31:42 +04:00
$sharekeys = \OC\Files\Filesystem :: normalizePath ( 'files_encryption/share-keys/' . $file_path );
2013-04-27 02:15:46 +04:00
2013-04-19 12:31:42 +04:00
if ( $view -> is_dir ( $sharekeys )) {
$size += self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/' . $sharekeys ));
2013-04-27 02:15:46 +04:00
$view -> rename ( $sharekeys , 'files_trashbin/share-keys/' . $filename . '.d' . $timestamp );
} else {
// get local path to share-keys
$localShareKeysPath = $view -> getLocalFile ( $sharekeys );
// handle share-keys
$matches = glob ( preg_quote ( $localShareKeysPath ) . '*.shareKey' );
foreach ( $matches as $src ) {
// get source file parts
$pathinfo = pathinfo ( $src );
// we only want to keep the owners key so we can access the private key
$ownerShareKey = $filename . '.' . $user . '.shareKey' ;
// if we found the share-key for the owner, we need to move it to files_trashbin
if ( $pathinfo [ 'basename' ] == $ownerShareKey ) {
// calculate size
$size += $view -> filesize ( $sharekeys . '.' . $user . '.shareKey' );
// move file
$view -> rename ( $sharekeys . '.' . $user . '.shareKey' , 'files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp );
} else {
// calculate size
$size += filesize ( $src );
2013-04-28 01:51:26 +04:00
2013-04-27 02:15:46 +04:00
// don't keep other share-keys
unlink ( $src );
}
}
}
2013-04-20 01:21:06 +04:00
// enable proxy
2013-04-27 02:15:46 +04:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-19 12:31:42 +04:00
}
return $size ;
}
2013-02-22 20:21:57 +04:00
2013-01-18 16:11:29 +04:00
/**
* restore files from trash bin
2013-01-22 15:00:04 +04:00
* @ param $file path to the deleted file
2013-01-18 16:11:29 +04:00
* @ param $filename name of the file
* @ param $timestamp time when the file was deleted
2013-04-20 01:21:06 +04:00
*
* @ return bool
*/
2013-01-22 15:00:04 +04:00
public static function restore ( $file , $filename , $timestamp ) {
2013-04-22 05:37:55 +04:00
$user = \OCP\User :: getUser ();
2013-01-31 21:04:00 +04:00
$view = new \OC\Files\View ( '/' . $user );
2013-01-18 16:11:29 +04:00
2013-02-21 15:37:13 +04:00
$trashbinSize = self :: getTrashbinSize ( $user );
2013-02-25 17:26:59 +04:00
if ( $trashbinSize === false || $trashbinSize < 0 ) {
2013-02-27 23:29:19 +04:00
$trashbinSize = self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/files_trashbin' ));
2013-02-07 18:16:29 +04:00
}
2013-01-22 15:00:04 +04:00
if ( $timestamp ) {
2013-03-22 15:47:43 +04:00
$query = \OC_DB :: prepare ( 'SELECT `location`,`type` FROM `*PREFIX*files_trash`'
2013-03-15 03:03:37 +04:00
. ' WHERE `user`=? AND `id`=? AND `timestamp`=?' );
2013-01-22 15:00:04 +04:00
$result = $query -> execute ( array ( $user , $filename , $timestamp )) -> fetchAll ();
if ( count ( $result ) != 1 ) {
2013-01-30 16:04:32 +04:00
\OC_Log :: write ( 'files_trashbin' , 'trash bin database inconsistent!' , \OC_Log :: ERROR );
2013-01-22 15:00:04 +04:00
return false ;
}
2013-02-22 20:21:57 +04:00
2013-01-31 21:04:00 +04:00
// if location no longer exists, restore file in the root directory
$location = $result [ 0 ][ 'location' ];
2013-02-22 20:21:57 +04:00
if ( $result [ 0 ][ 'location' ] != '/' &&
2013-02-15 01:46:28 +04:00
( ! $view -> is_dir ( 'files' . $result [ 0 ][ 'location' ]) ||
2013-01-31 21:04:00 +04:00
! $view -> isUpdatable ( 'files' . $result [ 0 ][ 'location' ])) ) {
$location = '' ;
2013-01-22 15:00:04 +04:00
}
} else {
2013-02-21 01:17:04 +04:00
$path_parts = pathinfo ( $file );
2013-01-22 15:00:04 +04:00
$result [] = array (
'location' => $path_parts [ 'dirname' ],
2013-02-20 16:34:32 +04:00
'type' => $view -> is_dir ( '/files_trashbin/files/' . $file ) ? 'dir' : 'files' ,
2013-01-22 15:00:04 +04:00
);
$location = '' ;
2013-01-18 16:11:29 +04:00
}
2013-02-27 23:29:19 +04:00
$source = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/files/' . $file );
2013-01-31 21:04:00 +04:00
$target = \OC\Files\Filesystem :: normalizePath ( 'files/' . $location . '/' . $filename );
2013-02-22 20:21:57 +04:00
2013-01-18 17:09:22 +04:00
// we need a extension in case a file/dir with the same name already exists
2013-01-18 16:11:29 +04:00
$ext = self :: getUniqueExtension ( $location , $filename , $view );
2013-01-31 13:50:02 +04:00
$mtime = $view -> filemtime ( $source );
2013-04-20 01:21:06 +04:00
// disable proxy to prevent recursive calls
2013-04-28 01:51:26 +04:00
$proxyStatus = \OC_FileProxy :: $enabled ;
2013-04-20 01:21:06 +04:00
\OC_FileProxy :: $enabled = false ;
// restore file
$restoreResult = $view -> rename ( $source , $target . $ext );
// handle the restore result
if ( $restoreResult ) {
$view -> touch ( $target . $ext , $mtime );
2013-03-01 15:44:43 +04:00
\OCP\Util :: emitHook ( '\OCA\Files_Trashbin\Trashbin' , 'post_restore' ,
array ( 'filePath' => \OC\Files\Filesystem :: normalizePath ( '/' . $location . '/' . $filename . $ext ),
'trashPath' => \OC\Files\Filesystem :: normalizePath ( $file )));
2013-02-07 18:16:29 +04:00
if ( $view -> is_dir ( $target . $ext )) {
2013-02-27 23:29:19 +04:00
$trashbinSize -= self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/' . $target . $ext ));
2013-02-07 18:16:29 +04:00
} else {
$trashbinSize -= $view -> filesize ( $target . $ext );
}
2013-04-19 12:35:32 +04:00
2013-04-22 05:37:55 +04:00
$trashbinSize -= self :: restoreVersions ( $view , $file , $filename , $ext , $location , $timestamp );
2013-04-19 12:35:32 +04:00
$trashbinSize -= self :: restoreEncryptionKeys ( $view , $file , $filename , $ext , $location , $timestamp );
2013-01-22 15:00:04 +04:00
if ( $timestamp ) {
2013-03-22 15:47:43 +04:00
$query = \OC_DB :: prepare ( 'DELETE FROM `*PREFIX*files_trash` WHERE `user`=? AND `id`=? AND `timestamp`=?' );
2013-01-22 15:00:04 +04:00
$query -> execute ( array ( $user , $filename , $timestamp ));
}
2013-01-18 17:09:22 +04:00
2013-02-21 15:37:13 +04:00
self :: setTrashbinSize ( $user , $trashbinSize );
2013-04-22 05:37:55 +04:00
// enable proxy
2013-04-28 01:51:26 +04:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-22 05:37:55 +04:00
2013-01-18 17:09:22 +04:00
return true ;
2013-01-18 16:11:29 +04:00
}
2013-01-18 17:09:22 +04:00
2013-04-22 05:37:55 +04:00
// enable proxy
2013-04-28 01:51:26 +04:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-22 05:37:55 +04:00
2013-01-18 17:09:22 +04:00
return false ;
2013-01-18 16:11:29 +04:00
}
2013-02-22 20:21:57 +04:00
2013-04-20 01:21:06 +04:00
/**
2013-04-19 12:35:32 +04:00
* @ brief restore versions from trash bin
*
2013-04-20 01:21:06 +04:00
* @ param \OC\Files\View $view file view
2013-04-19 12:35:32 +04:00
* @ param $file complete path to file
* @ param $filename name of file
* @ param $ext file extension in case a file with the same $filename already exists
* @ param $location location if file
* @ param $timestamp deleteion time
2013-04-20 01:21:06 +04:00
*
2013-04-19 12:35:32 +04:00
* @ return size of restored versions
*/
private static function restoreVersions ( $view , $file , $filename , $ext , $location , $timestamp ) {
$size = 0 ;
if ( \OCP\App :: isEnabled ( 'files_versions' )) {
2013-04-28 01:51:26 +04:00
// disable proxy to prevent recursive calls
$proxyStatus = \OC_FileProxy :: $enabled ;
\OC_FileProxy :: $enabled = false ;
$user = \OCP\User :: getUser ();
2013-04-19 12:35:32 +04:00
if ( $timestamp ) {
$versionedFile = $filename ;
} else {
$versionedFile = $file ;
}
2013-04-28 01:51:26 +04:00
if ( $view -> is_dir ( '/files_trashbin/versions/' . $file )) {
2013-04-19 12:35:32 +04:00
$size += self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/' . 'files_trashbin/versions/' . $file ));
2013-04-28 01:51:26 +04:00
$view -> rename ( \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/versions/' . $file ), \OC\Files\Filesystem :: normalizePath ( 'files_versions/' . $location . '/' . $filename . $ext ));
2013-04-19 12:35:32 +04:00
} else if ( $versions = self :: getVersionsFromTrash ( $versionedFile , $timestamp )) {
2013-04-28 01:51:26 +04:00
foreach ( $versions as $v ) {
if ( $timestamp ) {
$size += $view -> filesize ( 'files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp );
$view -> rename ( 'files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp , 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v );
2013-04-19 12:35:32 +04:00
} else {
2013-04-28 01:51:26 +04:00
$size += $view -> filesize ( 'files_trashbin/versions/' . $versionedFile . '.v' . $v );
2013-04-19 12:35:32 +04:00
$view -> rename ( 'files_trashbin/versions/' . $versionedFile . '.v' . $v , 'files_versions/' . $location . '/' . $filename . $ext . '.v' . $v );
}
}
}
2013-04-28 01:51:26 +04:00
// enable proxy
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-19 12:35:32 +04:00
}
return $size ;
}
2013-04-20 01:21:06 +04:00
/**
* @ brief restore encryption keys from trash bin
*
* @ param \OC\Files\View $view
* @ param $file complete path to file
* @ param $filename name of file
* @ param $ext file extension in case a file with the same $filename already exists
* @ param $location location if file
* @ param $timestamp deleteion time
*
2013-04-22 05:37:55 +04:00
* @ return size of restored encrypted file
2013-04-20 01:21:06 +04:00
*/
private static function restoreEncryptionKeys ( $view , $file , $filename , $ext , $location , $timestamp ) {
2013-04-19 12:35:32 +04:00
// Take care of encryption keys TODO! Get '.key' in file between file name and delete date (also for permanent delete!)
$size = 0 ;
if ( \OCP\App :: isEnabled ( 'files_encryption' )) {
$user = \OCP\User :: getUser ();
2013-04-20 01:21:06 +04:00
2013-04-22 05:37:55 +04:00
$path_parts = pathinfo ( $file );
$source_location = $path_parts [ 'dirname' ];
if ( $view -> is_dir ( '/files_trashbin/keyfiles/' . $file )) {
if ( $source_location != '.' ) {
$keyfile = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/keyfiles/' . $source_location . '/' . $filename );
2013-04-27 02:15:46 +04:00
$sharekey = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/share-keys/' . $source_location . '/' . $filename );
2013-04-22 05:37:55 +04:00
} else {
$keyfile = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/keyfiles/' . $filename );
2013-04-27 02:15:46 +04:00
$sharekey = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/share-keys/' . $filename );
2013-04-22 05:37:55 +04:00
}
2013-04-20 01:21:06 +04:00
} else {
2013-04-22 05:37:55 +04:00
$keyfile = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/keyfiles/' . $source_location . '/' . $filename . '.key' );
2013-04-20 01:21:06 +04:00
}
2013-04-22 05:37:55 +04:00
if ( $timestamp ) {
$keyfile .= '.d' . $timestamp ;
}
// disable proxy to prevent recursive calls
2013-04-27 02:15:46 +04:00
$proxyStatus = \OC_FileProxy :: $enabled ;
2013-04-22 05:37:55 +04:00
\OC_FileProxy :: $enabled = false ;
if ( $view -> file_exists ( $keyfile )) {
2013-04-27 02:15:46 +04:00
// handle directory
2013-04-22 05:37:55 +04:00
if ( $view -> is_dir ( $keyfile )) {
2013-04-27 02:15:46 +04:00
// handle keyfiles
$size += self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/' . $keyfile ));
2013-04-28 01:51:26 +04:00
$view -> rename ( $keyfile , 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext );
2013-04-27 02:15:46 +04:00
// handle share-keys
if ( $timestamp ) {
$sharekey .= '.d' . $timestamp ;
}
2013-04-28 01:51:26 +04:00
$view -> rename ( $sharekey , 'files_encryption/share-keys/' . $location . '/' . $filename . $ext );
2013-04-27 02:15:46 +04:00
2013-04-19 12:35:32 +04:00
} else {
2013-04-27 02:15:46 +04:00
// handle keyfiles
2013-04-19 12:35:32 +04:00
$size += $view -> filesize ( $keyfile );
2013-04-28 01:51:26 +04:00
$view -> rename ( $keyfile , 'files_encryption/keyfiles/' . $location . '/' . $filename . $ext . '.key' );
2013-04-27 02:15:46 +04:00
// handle share-keys
$ownerShareKey = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/share-keys/' . $source_location . '/' . $filename . '.' . $user . '.shareKey' );
if ( $timestamp ) {
$ownerShareKey .= '.d' . $timestamp ;
}
$size += $view -> filesize ( $ownerShareKey );
// move only owners key
2013-04-28 01:51:26 +04:00
$view -> rename ( $ownerShareKey , 'files_encryption/share-keys/' . $location . '/' . $filename . $ext . '.' . $user . '.shareKey' );
2013-04-27 02:15:46 +04:00
// try to re-share if file is shared
$filesystemView = new \OC_FilesystemView ( '/' );
$session = new \OCA\Encryption\Session ( $filesystemView );
$util = new \OCA\Encryption\Util ( $filesystemView , $user );
// fix the file size
$absolutePath = \OC\Files\Filesystem :: normalizePath ( '/' . $user . '/files/' . $location . '/' . $filename );
$util -> fixFileSize ( $absolutePath );
// get current sharing state
$sharingEnabled = \OCP\Share :: isEnabled ();
// get the final filename
$target = \OC\Files\Filesystem :: normalizePath ( $location . '/' . $filename );
// get users sharing this file
$usersSharing = $util -> getSharingUsersArray ( $sharingEnabled , $target . $ext , $user );
// Attempt to set shareKey
$util -> setSharedFileKeyfiles ( $session , $usersSharing , $target . $ext );
2013-04-19 12:35:32 +04:00
}
}
2013-04-22 05:37:55 +04:00
// enable proxy
2013-04-27 02:15:46 +04:00
\OC_FileProxy :: $enabled = $proxyStatus ;
2013-04-19 12:35:32 +04:00
}
return $size ;
}
2013-02-08 03:11:54 +04:00
/**
2013-04-20 01:21:06 +04:00
* @ brief delete file from trash bin permanently
*
2013-02-06 19:23:22 +04:00
* @ param $filename path to the file
2013-02-08 03:11:54 +04:00
* @ param $timestamp of deletion time
2013-04-20 01:21:06 +04:00
*
2013-02-22 20:21:57 +04:00
* @ return size of deleted files
2013-02-08 03:11:54 +04:00
*/
public static function delete ( $filename , $timestamp = null ) {
$user = \OCP\User :: getUser ();
2013-02-27 23:29:19 +04:00
$view = new \OC\Files\View ( '/' . $user );
2013-02-25 17:29:31 +04:00
$size = 0 ;
2013-02-08 03:11:54 +04:00
2013-02-21 15:37:13 +04:00
$trashbinSize = self :: getTrashbinSize ( $user );
2013-02-25 17:26:59 +04:00
if ( $trashbinSize === false || $trashbinSize < 0 ) {
2013-02-27 23:29:19 +04:00
$trashbinSize = self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/files_trashbin' ));
2013-02-07 18:16:29 +04:00
}
2013-02-22 20:21:57 +04:00
2013-02-08 03:11:54 +04:00
if ( $timestamp ) {
2013-03-22 15:47:43 +04:00
$query = \OC_DB :: prepare ( 'DELETE FROM `*PREFIX*files_trash` WHERE `user`=? AND `id`=? AND `timestamp`=?' );
2013-02-08 03:11:54 +04:00
$query -> execute ( array ( $user , $filename , $timestamp ));
2013-02-06 19:23:22 +04:00
$file = $filename . '.d' . $timestamp ;
} else {
$file = $filename ;
}
2013-02-22 20:21:57 +04:00
2013-02-06 19:23:22 +04:00
if ( \OCP\App :: isEnabled ( 'files_versions' ) ) {
2013-02-20 16:34:32 +04:00
if ( $view -> is_dir ( 'files_trashbin/versions/' . $file )) {
2013-02-27 23:29:19 +04:00
$size += self :: calculateSize ( new \OC\Files\view ( '/' . $user . '/files_trashbin/versions/' . $file ));
2013-02-20 16:34:32 +04:00
$view -> unlink ( 'files_trashbin/versions/' . $file );
2013-02-07 20:37:46 +04:00
} else if ( $versions = self :: getVersionsFromTrash ( $filename , $timestamp ) ) {
2013-02-06 19:23:22 +04:00
foreach ( $versions as $v ) {
if ( $timestamp ) {
2013-02-20 16:34:32 +04:00
$size += $view -> filesize ( '/files_trashbin/versions/' . $filename . '.v' . $v . '.d' . $timestamp );
$view -> unlink ( '/files_trashbin/versions/' . $filename . '.v' . $v . '.d' . $timestamp );
2013-02-06 19:23:22 +04:00
} else {
2013-02-20 16:34:32 +04:00
$size += $view -> filesize ( '/files_trashbin/versions/' . $filename . '.v' . $v );
$view -> unlink ( '/files_trashbin/versions/' . $filename . '.v' . $v );
2013-02-06 19:23:22 +04:00
}
}
}
2013-02-08 03:11:54 +04:00
}
2013-02-20 20:58:04 +04:00
2013-02-21 01:17:04 +04:00
// Take care of encryption keys
2013-02-25 17:29:31 +04:00
$parts = pathinfo ( $file );
if ( $view -> is_dir ( '/files_trashbin/files/' . $file ) ) {
2013-02-27 23:29:19 +04:00
$keyfile = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/keyfiles/' . $filename );
2013-02-25 17:29:31 +04:00
} else {
2013-02-27 23:29:19 +04:00
$keyfile = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/keyfiles/' . $filename . '.key' );
2013-02-25 17:29:31 +04:00
}
if ( $timestamp ) {
$keyfile .= '.d' . $timestamp ;
}
if ( \OCP\App :: isEnabled ( 'files_encryption' ) && $view -> file_exists ( $keyfile ) ) {
if ( $view -> is_dir ( $keyfile ) ) {
2013-02-27 23:29:19 +04:00
$size += self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/' . $keyfile ));
2013-02-25 17:29:31 +04:00
} else {
$size += $view -> filesize ( $keyfile );
}
$view -> unlink ( $keyfile );
}
2013-02-06 19:23:22 +04:00
2013-02-20 16:34:32 +04:00
if ( $view -> is_dir ( '/files_trashbin/files/' . $file )) {
2013-02-27 23:29:19 +04:00
$size += self :: calculateSize ( new \OC\Files\View ( '/' . $user . '/files_trashbin/files/' . $file ));
2013-02-07 18:16:29 +04:00
} else {
2013-02-20 16:34:32 +04:00
$size += $view -> filesize ( '/files_trashbin/files/' . $file );
2013-02-07 18:16:29 +04:00
}
2013-02-20 16:34:32 +04:00
$view -> unlink ( '/files_trashbin/files/' . $file );
2013-02-07 20:37:46 +04:00
$trashbinSize -= $size ;
2013-02-21 15:37:13 +04:00
self :: setTrashbinSize ( $user , $trashbinSize );
2013-02-08 03:11:54 +04:00
2013-02-22 20:21:57 +04:00
return $size ;
2013-02-06 19:23:22 +04:00
}
2013-02-17 02:42:06 +04:00
/**
* check to see whether a file exists in trashbin
2013-02-18 18:49:50 +04:00
* @ param $filename path to the file
* @ param $timestamp of deletion time
2013-02-17 02:42:06 +04:00
* @ return true if file exists , otherwise false
*/
2013-02-18 18:49:50 +04:00
public static function file_exists ( $filename , $timestamp = null ) {
2013-02-17 02:42:06 +04:00
$user = \OCP\User :: getUser ();
2013-02-27 23:29:19 +04:00
$view = new \OC\Files\View ( '/' . $user );
2013-02-18 18:49:50 +04:00
if ( $timestamp ) {
$filename = $filename . '.d' . $timestamp ;
} else {
$filename = $filename ;
}
2013-02-27 23:29:19 +04:00
$target = \OC\Files\Filesystem :: normalizePath ( 'files_trashbin/files/' . $filename );
2013-02-17 02:42:06 +04:00
return $view -> file_exists ( $target );
}
2013-04-11 14:37:52 +04:00
/**
* @ brief deletes used space for trash bin in db if user was deleted
*
* @ param type $uid id of deleted user
* @ return result of db delete operation
*/
public static function deleteUser ( $uid ) {
$query = \OC_DB :: prepare ( 'DELETE FROM `*PREFIX*files_trash` WHERE `user`=?' );
$result = $query -> execute ( array ( $uid ));
if ( $result ) {
$query = \OC_DB :: prepare ( 'DELETE FROM `*PREFIX*files_trashsize` WHERE `user`=?' );
return $query -> execute ( array ( $uid ));
}
return false ;
}
2013-04-16 15:51:53 +04:00
/**
* calculate remaining free space for trash bin
*
* @ param $trashbinSize current size of the trash bin
* @ return available free space for trash bin
*/
private static function calculateFreeSpace ( $trashbinSize ) {
$softQuota = true ;
$user = \OCP\User :: getUser ();
$quota = \OC_Preferences :: getValue ( $user , 'files' , 'quota' );
$view = new \OC\Files\View ( '/' . $user );
if ( $quota === null || $quota === 'default' ) {
$quota = \OC_Appconfig :: getValue ( 'files' , 'default_quota' );
}
if ( $quota === null || $quota === 'none' ) {
$quota = \OC\Files\Filesystem :: free_space ( '/' );
$softQuota = false ;
} else {
$quota = \OCP\Util :: computerFileSize ( $quota );
}
// calculate available space for trash bin
// subtract size of files and current trash bin size from quota
if ( $softQuota ) {
$rootInfo = $view -> getFileInfo ( '/files/' );
$free = $quota - $rootInfo [ 'size' ]; // remaining free space for user
if ( $free > 0 ) {
$availableSpace = ( $free * self :: DEFAULTMAXSIZE / 100 ) - $trashbinSize ; // how much space can be used for versions
} else {
$availableSpace = $free - $trashbinSize ;
}
} else {
$availableSpace = $quota ;
}
return $availableSpace ;
}
2013-01-31 21:04:00 +04:00
/**
* clean up the trash bin
2013-04-16 15:51:53 +04:00
* @ param current size of the trash bin
2013-01-31 21:04:00 +04:00
*/
2013-04-16 15:51:53 +04:00
private static function expire ( $trashbinSize ) {
2013-02-22 20:21:57 +04:00
2013-01-18 18:12:38 +04:00
$user = \OCP\User :: getUser ();
2013-02-27 23:29:19 +04:00
$view = new \OC\Files\View ( '/' . $user );
2013-04-16 15:51:53 +04:00
$availableSpace = self :: calculateFreeSpace ( $trashbinSize );
2013-02-07 18:16:29 +04:00
$size = 0 ;
2013-02-22 20:21:57 +04:00
2013-03-22 15:47:43 +04:00
$query = \OC_DB :: prepare ( 'SELECT `location`,`type`,`id`,`timestamp` FROM `*PREFIX*files_trash` WHERE `user`=?' );
2013-01-18 18:12:38 +04:00
$result = $query -> execute ( array ( $user )) -> fetchAll ();
2013-02-22 20:21:57 +04:00
2013-02-15 01:46:28 +04:00
$retention_obligation = \OC_Config :: getValue ( 'trashbin_retention_obligation' ,
self :: DEFAULT_RETENTION_OBLIGATION );
2013-02-22 20:21:57 +04:00
2013-01-22 16:19:41 +04:00
$limit = time () - ( $retention_obligation * 86400 );
2013-01-18 18:12:38 +04:00
foreach ( $result as $r ) {
$timestamp = $r [ 'timestamp' ];
$filename = $r [ 'id' ];
if ( $r [ 'timestamp' ] < $limit ) {
2013-02-20 21:00:15 +04:00
$size += self :: delete ( $filename , $timestamp );
2013-04-15 12:34:38 +04:00
\OC_Log :: write ( 'files_trashbin' , 'remove "' . $filename . '" fom trash bin because it is older than ' . $retention_obligation , \OC_log :: INFO );
2013-01-18 18:12:38 +04:00
}
}
2013-02-07 20:37:46 +04:00
$availableSpace = $availableSpace + $size ;
2013-02-09 14:07:47 +04:00
// if size limit for trash bin reached, delete oldest files in trash bin
2013-02-07 20:37:46 +04:00
if ( $availableSpace < 0 ) {
2013-03-22 15:47:43 +04:00
$query = \OC_DB :: prepare ( 'SELECT `location`,`type`,`id`,`timestamp` FROM `*PREFIX*files_trash`'
2013-03-15 03:03:37 +04:00
. ' WHERE `user`=? ORDER BY `timestamp` ASC' );
2013-02-07 20:37:46 +04:00
$result = $query -> execute ( array ( $user )) -> fetchAll ();
$length = count ( $result );
$i = 0 ;
while ( $i < $length && $availableSpace < 0 ) {
$tmp = self :: delete ( $result [ $i ][ 'id' ], $result [ $i ][ 'timestamp' ]);
2013-04-15 12:34:38 +04:00
\OC_Log :: write ( 'files_trashbin' , 'remove "' . $result [ $i ][ 'id' ] . '" (' . $tmp . 'B) to meet the limit of trash bin size (50% of available quota)' , \OC_log :: INFO );
2013-02-07 20:37:46 +04:00
$availableSpace += $tmp ;
$size += $tmp ;
$i ++ ;
}
2013-02-22 20:21:57 +04:00
2013-02-25 15:14:51 +04:00
2013-02-07 20:37:46 +04:00
}
2013-02-22 20:21:57 +04:00
return $size ;
2013-01-18 16:11:29 +04:00
}
2013-02-22 20:21:57 +04:00
2013-01-18 16:11:29 +04:00
/**
* recursive copy to copy a whole directory
2013-02-22 20:21:57 +04:00
*
2013-01-18 16:11:29 +04:00
* @ param $source source path , relative to the users files directory
* @ param $destination destination path relative to the users root directoy
* @ param $view file view for the users root directory
*/
2013-01-31 13:50:02 +04:00
private static function copy_recursive ( $source , $destination , $view ) {
2013-02-07 18:16:29 +04:00
$size = 0 ;
2013-01-18 16:11:29 +04:00
if ( $view -> is_dir ( 'files' . $source ) ) {
$view -> mkdir ( $destination );
2013-01-31 13:50:02 +04:00
$view -> touch ( $destination , $view -> filemtime ( 'files' . $source ));
2013-01-18 16:11:29 +04:00
foreach ( \OC_Files :: getDirectoryContent ( $source ) as $i ) {
$pathDir = $source . '/' . $i [ 'name' ];
if ( $view -> is_dir ( 'files' . $pathDir ) ) {
2013-02-07 18:16:29 +04:00
$size += self :: copy_recursive ( $pathDir , $destination . '/' . $i [ 'name' ], $view );
2013-01-18 16:11:29 +04:00
} else {
2013-02-07 18:16:29 +04:00
$size += $view -> filesize ( 'files' . $pathDir );
2013-01-18 16:11:29 +04:00
$view -> copy ( 'files' . $pathDir , $destination . '/' . $i [ 'name' ] );
2013-01-31 13:50:02 +04:00
$view -> touch ( $destination . '/' . $i [ 'name' ], $view -> filemtime ( 'files' . $pathDir ));
2013-01-18 16:11:29 +04:00
}
}
} else {
2013-02-07 18:16:29 +04:00
$size += $view -> filesize ( 'files' . $source );
2013-01-18 16:11:29 +04:00
$view -> copy ( 'files' . $source , $destination );
2013-01-31 13:50:02 +04:00
$view -> touch ( $destination , $view -> filemtime ( 'files' . $source ));
2013-01-18 16:11:29 +04:00
}
2013-02-07 18:16:29 +04:00
return $size ;
2013-01-18 16:11:29 +04:00
}
2013-02-22 20:21:57 +04:00
2013-01-18 16:11:29 +04:00
/**
* find all versions which belong to the file we want to restore
* @ param $filename name of the file which should be restored
* @ param $timestamp timestamp when the file was deleted
*/
private static function getVersionsFromTrash ( $filename , $timestamp ) {
2013-02-27 23:29:19 +04:00
$view = new \OC\Files\View ( '/' . \OCP\User :: getUser () . '/files_trashbin/versions' );
2013-03-04 15:57:32 +04:00
$versionsName = $view -> getLocalFile ( $filename );
2013-01-31 21:04:00 +04:00
$versions = array ();
2013-01-22 15:00:04 +04:00
if ( $timestamp ) {
2013-02-09 20:27:57 +04:00
// fetch for old versions
2013-01-22 15:00:04 +04:00
$matches = glob ( $versionsName . '.v*.d' . $timestamp );
$offset = - strlen ( $timestamp ) - 2 ;
} else {
$matches = glob ( $versionsName . '.v*' );
2013-01-31 21:04:00 +04:00
}
2013-02-22 20:21:57 +04:00
2013-01-18 16:11:29 +04:00
foreach ( $matches as $ma ) {
2013-01-22 15:00:04 +04:00
if ( $timestamp ) {
2013-01-31 21:04:00 +04:00
$parts = explode ( '.v' , substr ( $ma , 0 , $offset ) );
2013-01-22 15:00:04 +04:00
$versions [] = ( end ( $parts ) );
} else {
2013-01-31 21:04:00 +04:00
$parts = explode ( '.v' , $ma );
2013-01-22 15:00:04 +04:00
$versions [] = ( end ( $parts ) );
}
2013-01-18 16:11:29 +04:00
}
return $versions ;
}
2013-02-22 20:21:57 +04:00
2013-01-18 16:11:29 +04:00
/**
* find unique extension for restored file if a file with the same name already exists
* @ param $location where the file should be restored
* @ param $filename name of the file
* @ param $view filesystem view relative to users root directory
* @ return string with unique extension
*/
private static function getUniqueExtension ( $location , $filename , $view ) {
2013-01-31 21:04:00 +04:00
$ext = '' ;
if ( $view -> file_exists ( 'files' . $location . '/' . $filename ) ) {
$tmpext = '.restored' ;
$ext = $tmpext ;
$i = 1 ;
while ( $view -> file_exists ( 'files' . $location . '/' . $filename . $ext ) ) {
$ext = $tmpext . $i ;
$i ++ ;
}
2013-01-18 16:11:29 +04:00
}
return $ext ;
}
2013-02-22 20:21:57 +04:00
/**
2013-02-07 18:16:29 +04:00
* @ brief get the size from a given root folder
2013-02-22 20:21:57 +04:00
* @ param $view file view on the root folder
* @ return size of the folder
*/
private static function calculateSize ( $view ) {
$root = \OCP\Config :: getSystemValue ( 'datadirectory' ) . $view -> getAbsolutePath ( '' );
2013-02-19 00:48:08 +04:00
if ( ! file_exists ( $root )) {
return 0 ;
}
2013-02-15 01:46:28 +04:00
$iterator = new \RecursiveIteratorIterator ( new \RecursiveDirectoryIterator ( $root ),
2013-02-22 20:21:57 +04:00
\RecursiveIteratorIterator :: CHILD_FIRST );
$size = 0 ;
2013-02-07 18:16:29 +04:00
foreach ( $iterator as $path ) {
$relpath = substr ( $path , strlen ( $root ) - 1 );
if ( ! $view -> is_dir ( $relpath ) ) {
$size += $view -> filesize ( $relpath );
2013-02-22 20:21:57 +04:00
}
}
return $size ;
2013-02-07 18:16:29 +04:00
}
2013-02-21 15:37:13 +04:00
2013-02-25 17:29:31 +04:00
/**
* get current size of trash bin from a given user
*
* @ param $user user who owns the trash bin
* @ return mixed trash bin size or false if no trash bin size is stored
*/
private static function getTrashbinSize ( $user ) {
2013-03-22 15:47:43 +04:00
$query = \OC_DB :: prepare ( 'SELECT `size` FROM `*PREFIX*files_trashsize` WHERE `user`=?' );
2013-02-25 17:29:31 +04:00
$result = $query -> execute ( array ( $user )) -> fetchAll ();
if ( $result ) {
return $result [ 0 ][ 'size' ];
}
return false ;
}
/**
* write to the database how much space is in use for the trash bin
*
* @ param $user owner of the trash bin
* @ param $size size of the trash bin
*/
private static function setTrashbinSize ( $user , $size ) {
if ( self :: getTrashbinSize ( $user ) === false ) {
2013-03-22 15:47:43 +04:00
$query = \OC_DB :: prepare ( 'INSERT INTO `*PREFIX*files_trashsize` (`size`, `user`) VALUES (?, ?)' );
2013-02-25 17:29:31 +04:00
} else {
2013-03-22 15:47:43 +04:00
$query = \OC_DB :: prepare ( 'UPDATE `*PREFIX*files_trashsize` SET `size`=? WHERE `user`=?' );
2013-02-25 17:29:31 +04:00
}
$query -> execute ( array ( $size , $user ));
2013-02-21 15:37:13 +04:00
}
2013-02-07 18:16:29 +04:00
2013-01-31 21:04:00 +04:00
}