2015-08-24 06:03:53 -04:00
< ? php
2024-05-23 03:26:56 -04:00
2025-08-25 09:58:51 -04:00
declare ( strict_types = 1 );
2015-08-24 06:03:53 -04:00
/**
2024-05-23 03:26:56 -04:00
* SPDX - FileCopyrightText : 2016 - 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX - FileCopyrightText : 2016 ownCloud , Inc .
* SPDX - License - Identifier : AGPL - 3.0 - only
2015-08-24 06:03:53 -04:00
*/
2025-08-25 09:58:51 -04:00
2015-08-24 06:03:53 -04:00
namespace OC\Encryption ;
use OC\Encryption\Exceptions\DecryptionFailedException ;
use OC\Files\View ;
2019-11-22 14:52:10 -05:00
use OCP\Encryption\IEncryptionModule ;
2024-01-30 05:48:09 -05:00
use OCP\Encryption\IManager ;
2015-08-24 06:03:53 -04:00
use OCP\IUserManager ;
use Symfony\Component\Console\Helper\ProgressBar ;
use Symfony\Component\Console\Input\InputInterface ;
use Symfony\Component\Console\Output\OutputInterface ;
class DecryptAll {
2025-08-25 09:58:51 -04:00
/** @var array<string,list<string>> files which couldn't be decrypted */
protected array $failed = [];
2015-08-24 06:03:53 -04:00
public function __construct (
2024-01-30 05:48:09 -05:00
protected IManager $encryptionManager ,
protected IUserManager $userManager ,
2024-09-19 05:10:31 -04:00
protected View $rootView ,
2015-08-24 06:03:53 -04:00
) {
}
/**
* start to decrypt all files
*
* @ param string $user which users data folder should be decrypted , default = all users
* @ throws \Exception
*/
2025-08-25 09:58:51 -04:00
public function decryptAll ( InputInterface $input , OutputInterface $output , string $user = '' ) : bool {
2016-06-07 03:13:11 -04:00
if ( $user !== '' && $this -> userManager -> userExists ( $user ) === false ) {
2025-08-25 09:58:51 -04:00
$output -> writeln ( 'User "' . $user . '" does not exist. Please check the username and try again' );
2015-09-21 07:13:38 -04:00
return false ;
}
2025-08-25 09:58:51 -04:00
$output -> writeln ( 'prepare encryption modules...' );
if ( $this -> prepareEncryptionModules ( $input , $output , $user ) === false ) {
2015-08-24 06:03:53 -04:00
return false ;
}
2025-08-25 09:58:51 -04:00
$output -> writeln ( ' done.' );
2015-08-24 06:03:53 -04:00
2025-08-25 09:58:51 -04:00
$this -> failed = [];
$this -> decryptAllUsersFiles ( $output , $user );
2015-08-24 06:03:53 -04:00
2025-08-25 09:58:51 -04:00
/** @psalm-suppress RedundantCondition $this->failed is modified by decryptAllUsersFiles, not clear why psalm fails to see it */
2015-08-24 06:03:53 -04:00
if ( empty ( $this -> failed )) {
2025-08-25 09:58:51 -04:00
$output -> writeln ( 'all files could be decrypted successfully!' );
2015-08-24 06:03:53 -04:00
} else {
2025-08-25 09:58:51 -04:00
$output -> writeln ( 'Files for following users couldn\'t be decrypted, ' );
$output -> writeln ( 'maybe the user is not set up in a way that supports this operation: ' );
2015-08-24 06:03:53 -04:00
foreach ( $this -> failed as $uid => $paths ) {
2025-08-25 09:58:51 -04:00
$output -> writeln ( ' ' . $uid );
2018-12-14 06:00:49 -05:00
foreach ( $paths as $path ) {
2025-08-25 09:58:51 -04:00
$output -> writeln ( ' ' . $path );
2018-12-14 06:00:49 -05:00
}
2015-08-24 06:03:53 -04:00
}
2025-08-25 09:58:51 -04:00
$output -> writeln ( '' );
2015-08-24 06:03:53 -04:00
}
return true ;
}
/**
* prepare encryption modules to perform the decrypt all function
*/
2025-08-25 09:58:51 -04:00
protected function prepareEncryptionModules ( InputInterface $input , OutputInterface $output , string $user ) : bool {
2015-08-24 06:03:53 -04:00
// prepare all encryption modules for decrypt all
$encryptionModules = $this -> encryptionManager -> getEncryptionModules ();
foreach ( $encryptionModules as $moduleDesc ) {
/** @var IEncryptionModule $module */
$module = call_user_func ( $moduleDesc [ 'callback' ]);
2025-08-25 09:58:51 -04:00
$output -> writeln ( '' );
$output -> writeln ( 'Prepare "' . $module -> getDisplayName () . '"' );
$output -> writeln ( '' );
if ( $module -> prepareDecryptAll ( $input , $output , $user ) === false ) {
$output -> writeln ( 'Module "' . $moduleDesc [ 'displayName' ] . '" does not support the functionality to decrypt all files again or the initialization of the module failed!' );
2015-08-24 06:03:53 -04:00
return false ;
}
}
return true ;
}
/**
* iterate over all user and encrypt their files
2016-03-08 10:24:27 -05:00
*
2015-08-24 06:03:53 -04:00
* @ param string $user which users files should be decrypted , default = all users
*/
2025-08-25 09:58:51 -04:00
protected function decryptAllUsersFiles ( OutputInterface $output , string $user = '' ) : void {
$output -> writeln ( " \n " );
2015-08-24 06:03:53 -04:00
$userList = [];
2016-06-07 03:13:11 -04:00
if ( $user === '' ) {
2025-08-25 09:58:51 -04:00
$fetchUsersProgress = new ProgressBar ( $output );
2015-08-24 06:03:53 -04:00
$fetchUsersProgress -> setFormat ( " %message% \n [%bar%] " );
$fetchUsersProgress -> start ();
2024-08-23 09:10:27 -04:00
$fetchUsersProgress -> setMessage ( 'Fetch list of users...' );
2015-08-24 06:03:53 -04:00
$fetchUsersProgress -> advance ();
foreach ( $this -> userManager -> getBackends () as $backend ) {
$limit = 500 ;
$offset = 0 ;
do {
$users = $backend -> getUsers ( '' , $limit , $offset );
foreach ( $users as $user ) {
$userList [] = $user ;
}
$offset += $limit ;
$fetchUsersProgress -> advance ();
} while ( count ( $users ) >= $limit );
2024-08-23 09:10:27 -04:00
$fetchUsersProgress -> setMessage ( 'Fetch list of users... finished' );
2015-08-24 06:03:53 -04:00
$fetchUsersProgress -> finish ();
}
} else {
$userList [] = $user ;
}
2025-08-25 09:58:51 -04:00
$output -> writeln ( " \n \n " );
2015-08-24 06:03:53 -04:00
2025-08-25 09:58:51 -04:00
$progress = new ProgressBar ( $output );
2015-08-24 06:03:53 -04:00
$progress -> setFormat ( " %message% \n [%bar%] " );
$progress -> start ();
2024-08-23 09:10:27 -04:00
$progress -> setMessage ( 'starting to decrypt files...' );
2015-08-24 06:03:53 -04:00
$progress -> advance ();
$numberOfUsers = count ( $userList );
$userNo = 1 ;
foreach ( $userList as $uid ) {
$userCount = " $uid ( $userNo of $numberOfUsers ) " ;
$this -> decryptUsersFiles ( $uid , $progress , $userCount );
$userNo ++ ;
}
2024-08-23 09:10:27 -04:00
$progress -> setMessage ( 'starting to decrypt files... finished' );
2015-08-24 06:03:53 -04:00
$progress -> finish ();
2025-08-25 09:58:51 -04:00
$output -> writeln ( " \n \n " );
2015-08-24 06:03:53 -04:00
}
/**
* encrypt files from the given user
*/
2025-08-25 09:58:51 -04:00
protected function decryptUsersFiles ( string $uid , ProgressBar $progress , string $userCount ) : void {
2015-08-24 06:03:53 -04:00
$this -> setupUserFS ( $uid );
2020-03-26 04:30:18 -04:00
$directories = [];
2016-03-08 10:24:27 -05:00
$directories [] = '/' . $uid . '/files' ;
2015-08-24 06:03:53 -04:00
2016-03-08 10:24:27 -05:00
while ( $root = array_pop ( $directories )) {
2015-08-24 06:03:53 -04:00
$content = $this -> rootView -> getDirectoryContent ( $root );
foreach ( $content as $file ) {
2016-07-27 09:11:48 -04:00
// only decrypt files owned by the user
2020-04-10 08:19:56 -04:00
if ( $file -> getStorage () -> instanceOfStorage ( 'OCA\Files_Sharing\SharedStorage' )) {
2016-07-27 09:11:48 -04:00
continue ;
}
2015-08-24 06:03:53 -04:00
$path = $root . '/' . $file [ 'name' ];
if ( $this -> rootView -> is_dir ( $path )) {
$directories [] = $path ;
continue ;
} else {
try {
$progress -> setMessage ( " decrypt files for user $userCount : $path " );
$progress -> advance ();
2016-05-24 04:24:21 -04:00
if ( $file -> isEncrypted () === false ) {
2015-08-24 06:03:53 -04:00
$progress -> setMessage ( " decrypt files for user $userCount : $path (already decrypted) " );
$progress -> advance ();
2016-03-08 10:24:27 -05:00
} else {
if ( $this -> decryptFile ( $path ) === false ) {
$progress -> setMessage ( " decrypt files for user $userCount : $path (already decrypted) " );
$progress -> advance ();
}
2015-08-24 06:03:53 -04:00
}
} catch ( \Exception $e ) {
if ( isset ( $this -> failed [ $uid ])) {
$this -> failed [ $uid ][] = $path ;
} else {
$this -> failed [ $uid ] = [ $path ];
}
}
}
}
}
}
/**
* encrypt file
*/
2025-08-25 09:58:51 -04:00
protected function decryptFile ( string $path ) : bool {
2018-10-24 10:49:39 -04:00
// skip already decrypted files
$fileInfo = $this -> rootView -> getFileInfo ( $path );
if ( $fileInfo !== false && ! $fileInfo -> isEncrypted ()) {
return true ;
}
2015-08-24 06:03:53 -04:00
$source = $path ;
$target = $path . '.decrypted.' . $this -> getTimestamp ();
try {
$this -> rootView -> copy ( $source , $target );
2020-02-01 00:20:33 -05:00
$this -> rootView -> touch ( $target , $fileInfo -> getMTime ());
2015-08-24 06:03:53 -04:00
$this -> rootView -> rename ( $target , $source );
} catch ( DecryptionFailedException $e ) {
if ( $this -> rootView -> file_exists ( $target )) {
$this -> rootView -> unlink ( $target );
}
return false ;
}
return true ;
}
/**
* get current timestamp
*/
2025-08-25 09:58:51 -04:00
protected function getTimestamp () : int {
2015-08-24 06:03:53 -04:00
return time ();
}
/**
* setup user file system
*/
2025-08-25 09:58:51 -04:00
protected function setupUserFS ( string $uid ) : void {
2015-08-24 06:03:53 -04:00
\OC_Util :: tearDownFS ();
\OC_Util :: setupFS ( $uid );
}
}