var file = data.files[0]; | var file = data.files[0]; | ||||
try { | try { | ||||
// FIXME: not so elegant... need to refactor that method to return a value | // FIXME: not so elegant... need to refactor that method to return a value | ||||
Files.isFileNameValid(file.name, FileList.getCurrentDirectory()); | |||||
Files.isFileNameValid(file.name); | |||||
} | } | ||||
catch (errorMessage) { | catch (errorMessage) { | ||||
data.textStatus = 'invalidcharacters'; | data.textStatus = 'invalidcharacters'; | ||||
throw t('files', 'URL cannot be empty'); | throw t('files', 'URL cannot be empty'); | ||||
} else if (type !== 'web' && !Files.isFileNameValid(filename)) { | } else if (type !== 'web' && !Files.isFileNameValid(filename)) { | ||||
// Files.isFileNameValid(filename) throws an exception itself | // Files.isFileNameValid(filename) throws an exception itself | ||||
} else if (FileList.getCurrentDirectory() === '/' && filename.toLowerCase() === 'shared') { | |||||
throw t('files', 'In the home folder \'Shared\' is a reserved filename'); | |||||
} else if (FileList.inList(filename)) { | } else if (FileList.inList(filename)) { | ||||
throw t('files', '{new_name} already exists', {new_name: filename}); | throw t('files', '{new_name} already exists', {new_name: filename}); | ||||
} else { | } else { |
}; | }; | ||||
var addAction = function (name, action, displayName) { | var addAction = function (name, action, displayName) { | ||||
// NOTE: Temporary fix to prevent rename action in root of Shared directory | |||||
if (name === 'Rename' && $('#dir').val() === '/Shared') { | |||||
return true; | |||||
} | |||||
if ((name === 'Download' || action !== defaultAction) && name !== 'Delete') { | if ((name === 'Download' || action !== defaultAction) && name !== 'Delete') { | ||||
addAction(name, ah, displayName); | addAction(name, ah, displayName); | ||||
} | } | ||||
}); | }); | ||||
if(actions.Share && !($('#dir').val() === '/' && file === 'Shared')){ | |||||
if(actions.Share){ | |||||
displayName = t('files', 'Share'); | displayName = t('files', 'Share'); | ||||
addAction('Share', actions.Share, displayName); | addAction('Share', actions.Share, displayName); | ||||
} | } | ||||
$('#fileList tr').each(function () { | $('#fileList tr').each(function () { | ||||
FileActions.display($(this).children('td.filename')); | FileActions.display($(this).children('td.filename')); | ||||
}); | }); | ||||
$('#fileList').trigger(jQuery.Event("fileActionsReady")); | $('#fileList').trigger(jQuery.Event("fileActionsReady")); | ||||
}); | }); |
var filename = input.val(); | var filename = input.val(); | ||||
if (filename !== oldname) { | if (filename !== oldname) { | ||||
// Files.isFileNameValid(filename) throws an exception itself | // Files.isFileNameValid(filename) throws an exception itself | ||||
Files.isFileNameValid(filename, FileList.getCurrentDirectory()); | |||||
Files.isFileNameValid(filename); | |||||
if (FileList.inList(filename)) { | if (FileList.inList(filename)) { | ||||
throw t('files', '{new_name} already exists', {new_name: filename}); | throw t('files', '{new_name} already exists', {new_name: filename}); | ||||
} | } |
* Throws a string exception with an error message if | * Throws a string exception with an error message if | ||||
* the file name is not valid | * the file name is not valid | ||||
*/ | */ | ||||
isFileNameValid: function (name, root) { | |||||
isFileNameValid: function (name) { | |||||
var trimmedName = name.trim(); | var trimmedName = name.trim(); | ||||
if (trimmedName === '.' | |||||
|| trimmedName === '..' | |||||
|| (root === '/' && trimmedName.toLowerCase() === 'shared')) | |||||
if (trimmedName === '.' || trimmedName === '..') | |||||
{ | { | ||||
throw t('files', '"{name}" is an invalid file name.', {name: name}); | throw t('files', '"{name}" is an invalid file name.', {name: name}); | ||||
} else if (trimmedName.length === 0) { | } else if (trimmedName.length === 0) { |
'data' => NULL | 'data' => NULL | ||||
); | ); | ||||
// rename to "/Shared" is denied | |||||
if( $dir === '/' and $newname === 'Shared' ) { | |||||
$result['data'] = array( | |||||
'message' => $this->l10n->t("Invalid folder name. Usage of 'Shared' is reserved.") | |||||
); | |||||
// rename to non-existing folder is denied | // rename to non-existing folder is denied | ||||
} else if (!$this->view->file_exists($dir)) { | |||||
if (!$this->view->file_exists($dir)) { | |||||
$result['data'] = array('message' => (string)$this->l10n->t( | $result['data'] = array('message' => (string)$this->l10n->t( | ||||
'The target folder has been moved or deleted.', | 'The target folder has been moved or deleted.', | ||||
array($dir)), | array($dir)), | ||||
); | ); | ||||
// rename to existing file is denied | // rename to existing file is denied | ||||
} else if ($this->view->file_exists($dir . '/' . $newname)) { | } else if ($this->view->file_exists($dir . '/' . $newname)) { | ||||
$result['data'] = array( | $result['data'] = array( | ||||
'message' => $this->l10n->t( | 'message' => $this->l10n->t( | ||||
"The name %s is already used in the folder %s. Please choose a different name.", | "The name %s is already used in the folder %s. Please choose a different name.", | ||||
} else if ( | } else if ( | ||||
// rename to "." is denied | // rename to "." is denied | ||||
$newname !== '.' and | $newname !== '.' and | ||||
// rename of "/Shared" is denied | |||||
!($dir === '/' and $oldname === 'Shared') and | |||||
// THEN try to rename | // THEN try to rename | ||||
$this->view->rename($dir . '/' . $oldname, $dir . '/' . $newname) | $this->view->rename($dir . '/' . $oldname, $dir . '/' . $newname) | ||||
) { | ) { |
\OC\Files\Filesystem::tearDown(); | \OC\Files\Filesystem::tearDown(); | ||||
} | } | ||||
/** | |||||
* @brief test rename of file/folder named "Shared" | |||||
*/ | |||||
function testRenameSharedFolder() { | |||||
$dir = '/'; | |||||
$oldname = 'Shared'; | |||||
$newname = 'new_name'; | |||||
$this->viewMock->expects($this->at(0)) | |||||
->method('file_exists') | |||||
->with('/') | |||||
->will($this->returnValue(true)); | |||||
$result = $this->files->rename($dir, $oldname, $newname); | |||||
$expected = array( | |||||
'success' => false, | |||||
'data' => array('message' => '%s could not be renamed') | |||||
); | |||||
$this->assertEquals($expected, $result); | |||||
} | |||||
/** | |||||
* @brief test rename of file/folder named "Shared" | |||||
*/ | |||||
function testRenameSharedFolderInSubdirectory() { | |||||
$dir = '/test'; | |||||
$oldname = 'Shared'; | |||||
$newname = 'new_name'; | |||||
$this->viewMock->expects($this->at(0)) | |||||
->method('file_exists') | |||||
->with('/test') | |||||
->will($this->returnValue(true)); | |||||
$this->viewMock->expects($this->any()) | |||||
->method('getFileInfo') | |||||
->will($this->returnValue(new \OC\Files\FileInfo( | |||||
'/test', | |||||
null, | |||||
'/test', | |||||
array( | |||||
'fileid' => 123, | |||||
'type' => 'dir', | |||||
'mimetype' => 'httpd/unix-directory', | |||||
'mtime' => 0, | |||||
'permissions' => 31, | |||||
'size' => 18, | |||||
'etag' => 'abcdef', | |||||
'directory' => '/', | |||||
'name' => 'new_name', | |||||
)))); | |||||
$result = $this->files->rename($dir, $oldname, $newname); | |||||
$this->assertTrue($result['success']); | |||||
$this->assertEquals(123, $result['data']['id']); | |||||
$this->assertEquals('new_name', $result['data']['name']); | |||||
$this->assertEquals(18, $result['data']['size']); | |||||
$this->assertEquals('httpd/unix-directory', $result['data']['mimetype']); | |||||
$icon = \OC_Helper::mimetypeIcon('dir'); | |||||
$icon = substr($icon, 0, -3) . 'svg'; | |||||
$this->assertEquals($icon, $result['data']['icon']); | |||||
} | |||||
/** | |||||
* @brief test rename of file/folder to "Shared" | |||||
*/ | |||||
function testRenameFolderToShared() { | |||||
$dir = '/'; | |||||
$oldname = 'oldname'; | |||||
$newname = 'Shared'; | |||||
$result = $this->files->rename($dir, $oldname, $newname); | |||||
$expected = array( | |||||
'success' => false, | |||||
'data' => array('message' => "Invalid folder name. Usage of 'Shared' is reserved.") | |||||
); | |||||
$this->assertEquals($expected, $result); | |||||
} | |||||
/** | /** | ||||
* @brief test rename of file/folder | * @brief test rename of file/folder | ||||
*/ | */ |
expect(error).toEqual(false); | expect(error).toEqual(false); | ||||
} | } | ||||
}); | }); | ||||
it('Validates correct file names do not create Shared folder in root', function() { | |||||
// create shared file in subfolder | |||||
var error = false; | |||||
try { | |||||
expect(Files.isFileNameValid('shared', '/foo')).toEqual(true); | |||||
expect(Files.isFileNameValid('Shared', '/foo')).toEqual(true); | |||||
} | |||||
catch (e) { | |||||
error = e; | |||||
} | |||||
expect(error).toEqual(false); | |||||
// create shared file in root | |||||
var threwException = false; | |||||
try { | |||||
Files.isFileNameValid('Shared', '/'); | |||||
console.error('Invalid file name not detected'); | |||||
} | |||||
catch (e) { | |||||
threwException = true; | |||||
} | |||||
expect(threwException).toEqual(true); | |||||
// create shared file in root | |||||
var threwException = false; | |||||
try { | |||||
Files.isFileNameValid('shared', '/'); | |||||
console.error('Invalid file name not detected'); | |||||
} | |||||
catch (e) { | |||||
threwException = true; | |||||
} | |||||
expect(threwException).toEqual(true); | |||||
}); | |||||
it('Detects invalid file names', function() { | it('Detects invalid file names', function() { | ||||
var fileNames = [ | var fileNames = [ | ||||
'', | '', |
*/ | */ | ||||
public function createFile($name, $data = null) { | public function createFile($name, $data = null) { | ||||
if (strtolower($name) === 'shared' && empty($this->path)) { | |||||
throw new \Sabre_DAV_Exception_Forbidden(); | |||||
} | |||||
// for chunked upload also updating a existing file is a "createFile" | // for chunked upload also updating a existing file is a "createFile" | ||||
// because we create all the chunks before reasamble them to the existing file. | // because we create all the chunks before reasamble them to the existing file. | ||||
if (isset($_SERVER['HTTP_OC_CHUNKED'])) { | if (isset($_SERVER['HTTP_OC_CHUNKED'])) { | ||||
*/ | */ | ||||
public function createDirectory($name) { | public function createDirectory($name) { | ||||
if (strtolower($name) === 'shared' && empty($this->path)) { | |||||
throw new \Sabre_DAV_Exception_Forbidden(); | |||||
} | |||||
if (!\OC\Files\Filesystem::isCreatable($this->path)) { | if (!\OC\Files\Filesystem::isCreatable($this->path)) { | ||||
throw new \Sabre_DAV_Exception_Forbidden(); | throw new \Sabre_DAV_Exception_Forbidden(); | ||||
} | } | ||||
*/ | */ | ||||
public function delete() { | public function delete() { | ||||
if ($this->path === 'Shared') { | |||||
throw new \Sabre_DAV_Exception_Forbidden(); | |||||
} | |||||
if (!\OC\Files\Filesystem::isDeletable($this->path)) { | if (!\OC\Files\Filesystem::isDeletable($this->path)) { | ||||
throw new \Sabre_DAV_Exception_Forbidden(); | throw new \Sabre_DAV_Exception_Forbidden(); | ||||
} | } |
// mark file as partial while uploading (ignored by the scanner) | // mark file as partial while uploading (ignored by the scanner) | ||||
$partpath = $this->path . '.ocTransferId' . rand() . '.part'; | $partpath = $this->path . '.ocTransferId' . rand() . '.part'; | ||||
// if file is located in /Shared we write the part file to the users | |||||
// root folder because we can't create new files in /shared | |||||
// we extend the name with a random number to avoid overwriting a existing file | |||||
if (dirname($partpath) === 'Shared') { | |||||
$partpath = pathinfo($partpath, PATHINFO_FILENAME) . rand() . '.part'; | |||||
} | |||||
try { | try { | ||||
$putOkay = $fs->file_put_contents($partpath, $data); | $putOkay = $fs->file_put_contents($partpath, $data); | ||||
if ($putOkay === false) { | if ($putOkay === false) { | ||||
public function delete() { | public function delete() { | ||||
$fs = $this->getFS(); | $fs = $this->getFS(); | ||||
if ($this->path === 'Shared') { | |||||
throw new \Sabre_DAV_Exception_Forbidden(); | |||||
} | |||||
if (!$fs->isDeletable($this->path)) { | if (!$fs->isDeletable($this->path)) { | ||||
throw new \Sabre_DAV_Exception_Forbidden(); | throw new \Sabre_DAV_Exception_Forbidden(); | ||||
} | } |
} | } | ||||
if ($sourceDir !== $destinationDir) { | if ($sourceDir !== $destinationDir) { | ||||
// for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir | // for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir | ||||
if (ltrim($destinationDir, '/') === '' && strtolower($sourceNode->getName()) === 'shared') { | |||||
throw new \Sabre_DAV_Exception_Forbidden(); | |||||
} | |||||
if (!$fs->isUpdatable($sourceDir)) { | if (!$fs->isUpdatable($sourceDir)) { | ||||
throw new \Sabre_DAV_Exception_Forbidden(); | throw new \Sabre_DAV_Exception_Forbidden(); | ||||
} | } |