setName('encryption:fix-encrypted-version') ->setDescription('Fix the encrypted version if the encrypted file(s) are not downloadable.') ->addArgument( 'user', InputArgument::OPTIONAL, 'The id of the user whose files need fixing' )->addOption( 'path', 'p', InputOption::VALUE_REQUIRED, 'Limit files to fix with path, e.g., --path="/Music/Artist". If path indicates a directory, all the files inside directory will be fixed.' )->addOption( 'all', null, InputOption::VALUE_NONE, 'Run the fix for all users on the system, mutually exclusive with specifying a user id.' ); } protected function execute(InputInterface $input, OutputInterface $output): int { $skipSignatureCheck = $this->config->getSystemValueBool('encryption_skip_signature_check', false); $this->supportLegacy = $this->config->getSystemValueBool('encryption.legacy_format_support', false); if ($skipSignatureCheck) { $output->writeln("Repairing is not possible when \"encryption_skip_signature_check\" is set. Please disable this flag in the configuration.\n"); return self::FAILURE; } if (!$this->util->isMasterKeyEnabled()) { $output->writeln("Repairing only works with master key encryption.\n"); return self::FAILURE; } $user = $input->getArgument('user'); $all = $input->getOption('all'); $pathOption = \trim(($input->getOption('path') ?? ''), '/'); if (!$user && !$all) { $output->writeln('Either a user id or --all needs to be provided'); return self::FAILURE; } if ($user) { if ($all) { $output->writeln('Specifying a user id and --all are mutually exclusive'); return self::FAILURE; } if ($this->userManager->get($user) === null) { $output->writeln("User id $user does not exist. Please provide a valid user id"); return self::FAILURE; } return $this->runForUser($user, $pathOption, $output); } $result = 0; $this->userManager->callForSeenUsers(function (IUser $user) use ($pathOption, $output, &$result) { $output->writeln('Processing files for ' . $user->getUID()); $result = $this->runForUser($user->getUID(), $pathOption, $output); return $result === 0; }); return $result; } private function runForUser(string $user, string $pathOption, OutputInterface $output): int { $pathToWalk = "/$user/files"; if ($pathOption !== '') { $pathToWalk = "$pathToWalk/$pathOption"; } return $this->walkPathOfUser($user, $pathToWalk, $output); } /** * @return int 0 for success, 1 for error */ private function walkPathOfUser(string $user, string $path, OutputInterface $output): int { $this->setupUserFs($user); if (!$this->view->file_exists($path)) { $output->writeln("Path \"$path\" does not exist. Please provide a valid path."); return self::FAILURE; } if ($this->view->is_file($path)) { $output->writeln("Verifying the content of file \"$path\""); $this->verifyFileContent($path, $output); return self::SUCCESS; } $directories = []; $directories[] = $path; while ($root = \array_pop($directories)) { $directoryContent = $this->view->getDirectoryContent($root); foreach ($directoryContent as $file) { $path = $root . '/' . $file['name']; if ($this->view->is_dir($path)) { $directories[] = $path; } else { $output->writeln("Verifying the content of file \"$path\""); $this->verifyFileContent($path, $output); } } } return self::SUCCESS; } /** * @param bool $ignoreCorrectEncVersionCall, setting this variable to false avoids recursion */ private function verifyFileContent(string $path, OutputInterface $output, bool $ignoreCorrectEncVersionCall = true): bool { try { // since we're manually poking around the encrypted state we need to ensure that this isn't cached in the encryption wrapper $mount = $this->view->getMount($path); $storage = $mount->getStorage(); if ($storage && $storage->instanceOfStorage(Encryption::class)) { $storage->clearIsEncryptedCache(); } /** * In encryption, the files are read in a block size of 8192 bytes * Read block size of 8192 and a bit more (808 bytes) * If there is any problem, the first block should throw the signature * mismatch error. Which as of now, is enough to proceed ahead to * correct the encrypted version. */ $handle = $this->view->fopen($path, 'rb'); if ($handle === false) { $output->writeln("Failed to open file: \"$path\" skipping"); return true; } if (\fread($handle, 9001) !== false) { $fileInfo = $this->view->getFileInfo($path); if (!$fileInfo) { $output->writeln("File info not found for file: \"$path\""); return true; } $encryptedVersion = $fileInfo->getEncryptedVersion(); $stat = $this->view->stat($path); if (($encryptedVersion == 0) && isset($stat['hasHeader']) && ($stat['hasHeader'] == true)) { // The file has encrypted to false but has an encryption header if ($ignoreCorrectEncVersionCall === true) { // Lets rectify the file by correcting encrypted version $output->writeln("Attempting to fix the path: \"$path\""); return $this->correctEncryptedVersion($path, $output); } return false; } $output->writeln("The file \"$path\" is: OK"); } \fclose($handle); return true; } catch (ServerNotAvailableException $e) { // not a "bad signature" error and likely "le
define( [
	"qunit",
	"jquery",
	"lib/helper",
	"ui/widgets/autocomplete"
], function( QUnit, $, helper ) {

QUnit.module( "autocomplete: methods", { afterEach: helper.moduleAfterEach }  );

QUnit.test( "destroy", function( assert ) {
	assert.expect( 1 );
	assert.domEqual( "#autocomplete", function() {
		$( "#autocomplete" ).autocomplete().autocomplete( "destroy" );
	} );
} );

QUnit.test( "search, close", function( assert ) {
	assert.expect( 6 );
	var data = [ "c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "perl" ],
		element = $( "#autocomplete" ).autocomplete( {
			source: data,
			minLength: 0
		} ),
		menu = element.autocomplete( "widget" );

	assert.ok( menu.is( ":hidden" ), "menu is hidden on init" );

	element.autocomplete( "search" );
	assert.ok( menu.is( ":visible" ), "menu is visible after search" );
	assert.equal( menu.find( ".ui-menu-item" ).length, data.length, "all items for a blank search" );

	element.val( "has" ).autocomplete( "search" );
	assert.equal( menu.find( ".ui-menu-item" ).text(), "haskell", "only one item for set input value" );

	element.autocomplete( "search", "ja" );
	assert.equal( menu.find( ".ui-menu-item" ).length, 2, "only java and javascript for 'ja'" );

	element.autocomplete( "close" );
	assert.ok( menu.is( ":hidden" ), "menu is hidden after close" );
} );

QUnit.test( "widget", function( assert ) {
	assert.expect( 2 );
	var element = $( "#autocomplete" ).autocomplete(),
		widgetElement = element.autocomplete( "widget" );
	assert.equal( widgetElement.length, 1, "one element" );
	assert.hasClasses( widgetElement, "ui-menu" );
} );

} );