Skip to content

Commit

Permalink
Merge pull request #13552 from nextcloud/bugfix/wcf-limitations
Browse files Browse the repository at this point in the history
BugFix - Check Filename Limitation With Correct Server Version and Configuration
  • Loading branch information
alperozturk96 authored Sep 12, 2024
2 parents 5ec798c + d17018b commit 2f4dcbd
Show file tree
Hide file tree
Showing 21 changed files with 112 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
package com.nextcloud.utils

import com.nextcloud.utils.fileNameValidator.FileNameValidator
import com.owncloud.android.AbstractIT
import com.owncloud.android.AbstractOnServerIT
import com.owncloud.android.R
import com.owncloud.android.lib.resources.status.NextcloudVersion
import com.owncloud.android.lib.resources.status.OCCapability
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
Expand All @@ -19,7 +20,7 @@ import org.junit.Before
import org.junit.Test

@Suppress("TooManyFunctions")
class FileNameValidatorTests : AbstractIT() {
class FileNameValidatorTests : AbstractOnServerIT() {

private var capability: OCCapability = fileDataStorageManager.getCapability(account.name)

Expand All @@ -33,13 +34,15 @@ class FileNameValidatorTests : AbstractIT() {
"lpt0", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7",
"lpt8", "lpt9", "lpt¹", "lpt²", "lpt³"]
"""
forbiddenFilenameExtensionJson = """[".filepart",".part"]"""
forbiddenFilenameExtensionJson = """[" ",".",".part",".part"]"""
forbiddenFilenameCharactersJson = """["<", ">", ":", "\\\\", "/", "|", "?", "*", "&"]"""
}
}

@Test
fun testInvalidCharacter() {
testOnlyOnServer(NextcloudVersion.nextcloud_30)

val result = FileNameValidator.checkFileName("file<name", capability, targetContext)
assertEquals(
String.format(targetContext.getString(R.string.file_name_validator_error_invalid_character), "<"),
Expand All @@ -49,26 +52,43 @@ class FileNameValidatorTests : AbstractIT() {

@Test
fun testReservedName() {
testOnlyOnServer(NextcloudVersion.nextcloud_30)

val result = FileNameValidator.checkFileName("CON", capability, targetContext)
assertEquals(targetContext.getString(R.string.file_name_validator_error_reserved_names, "CON"), result)
assertEquals(targetContext.getString(R.string.file_name_validator_error_reserved_names, "con"), result)
}

@Test
fun testForbiddenFilenameExtension() {
val result = FileNameValidator.checkFileName("my_fav_file.filepart", capability, targetContext)
testOnlyOnServer(NextcloudVersion.nextcloud_30)

val result = FileNameValidator.checkFileName("my_fav_file.part", capability, targetContext)
assertEquals(
targetContext.getString(R.string.file_name_validator_error_forbidden_file_extensions, "filepart"),
targetContext.getString(R.string.file_name_validator_error_forbidden_file_extensions, ".part"),
result
)
}

@Test
fun testEndsWithSpaceOrPeriod() {
val result = FileNameValidator.checkFileName("filename ", capability, targetContext)
assertEquals(targetContext.getString(R.string.file_name_validator_error_ends_with_space_period), result)

val result2 = FileNameValidator.checkFileName("filename.", capability, targetContext)
assertEquals(targetContext.getString(R.string.file_name_validator_error_ends_with_space_period), result2)
val firstFilename = "test "
val secondFilename = "test."
val result = FileNameValidator.checkFileName(firstFilename, capability, targetContext)
val result2 = FileNameValidator.checkFileName(secondFilename, capability, targetContext)

if (capability.version.isOlderThan(NextcloudVersion.nextcloud_30)) {
assertEquals(null, result)
assertEquals(null, result2)
} else {
assertEquals(
targetContext.getString(R.string.file_name_validator_error_forbidden_file_extensions, " "),
result
)
assertEquals(
targetContext.getString(R.string.file_name_validator_error_forbidden_file_extensions, "."),
result2
)
}
}

@Test
Expand All @@ -79,7 +99,7 @@ class FileNameValidatorTests : AbstractIT() {

@Test
fun testFileAlreadyExists() {
val existingFiles = mutableSetOf("existingFile")
val existingFiles = setOf("existingFile")
val result = FileNameValidator.checkFileName("existingFile", capability, targetContext, existingFiles)
assertEquals(targetContext.getString(R.string.file_already_exists), result)
}
Expand All @@ -98,7 +118,7 @@ class FileNameValidatorTests : AbstractIT() {

@Test
fun testIsFileNameAlreadyExist() {
val existingFiles = mutableSetOf("existingFile")
val existingFiles = setOf("existingFile")
assertTrue(FileNameValidator.isFileNameAlreadyExist("existingFile", existingFiles))
assertFalse(FileNameValidator.isFileNameAlreadyExist("newFile", existingFiles))
}
Expand All @@ -114,6 +134,8 @@ class FileNameValidatorTests : AbstractIT() {

@Test
fun testFolderPathWithReservedName() {
testOnlyOnServer(NextcloudVersion.nextcloud_30)

val folderPath = "CON"
val filePaths = listOf("file1.txt", "file2.doc", "file3.jpg")

Expand All @@ -123,6 +145,8 @@ class FileNameValidatorTests : AbstractIT() {

@Test
fun testFilePathWithReservedName() {
testOnlyOnServer(NextcloudVersion.nextcloud_30)

val folderPath = "validFolder"
val filePaths = listOf("file1.txt", "PRN.doc", "file3.jpg")

Expand All @@ -132,6 +156,8 @@ class FileNameValidatorTests : AbstractIT() {

@Test
fun testFolderPathWithInvalidCharacter() {
testOnlyOnServer(NextcloudVersion.nextcloud_30)

val folderPath = "invalid<Folder"
val filePaths = listOf("file1.txt", "file2.doc", "file3.jpg")

Expand All @@ -141,6 +167,8 @@ class FileNameValidatorTests : AbstractIT() {

@Test
fun testFilePathWithInvalidCharacter() {
testOnlyOnServer(NextcloudVersion.nextcloud_30)

val folderPath = "validFolder"
val filePaths = listOf("file1.txt", "file|2.doc", "file3.jpg")

Expand All @@ -154,7 +182,7 @@ class FileNameValidatorTests : AbstractIT() {
val filePaths = listOf("file1.txt", "file2.doc", "file3.jpg")

val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext)
assertFalse(result)
assertEquals(capability.version.isOlderThan(NextcloudVersion.nextcloud_30), result)
}

@Test
Expand All @@ -163,11 +191,13 @@ class FileNameValidatorTests : AbstractIT() {
val filePaths = listOf("file1.txt", "file2.doc", "file3.")

val result = FileNameValidator.checkFolderAndFilePaths(folderPath, filePaths, capability, targetContext)
assertFalse(result)
assertEquals(capability.version.isOlderThan(NextcloudVersion.nextcloud_30), result)
}

@Test
fun testFilePathWithNestedFolder() {
testOnlyOnServer(NextcloudVersion.nextcloud_30)

val folderPath = "validFolder\\secondValidFolder\\CON"
val filePaths = listOf("file1.txt", "file2.doc", "file3.")

Expand All @@ -185,6 +215,8 @@ class FileNameValidatorTests : AbstractIT() {

@Test
fun testOnlyFolderPathWithOneReservedName() {
testOnlyOnServer(NextcloudVersion.nextcloud_30)

val folderPath = "/A1/Aaaww/CON/W/C2/"

val result = FileNameValidator.checkFolderAndFilePaths(folderPath, listOf(), capability, targetContext)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.nextcloud.utils.extensions.forbiddenFilenames
import com.nextcloud.utils.extensions.removeFileExtension
import com.owncloud.android.R
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.lib.resources.status.NextcloudVersion
import com.owncloud.android.lib.resources.status.OCCapability

object FileNameValidator {
Expand All @@ -36,7 +37,7 @@ object FileNameValidator {
filename: String,
capability: OCCapability,
context: Context,
existedFileNames: MutableSet<String>? = null
existedFileNames: Set<String>? = null
): String? {
if (TextUtils.isEmpty(filename)) {
return context.getString(R.string.filename_empty)
Expand All @@ -48,50 +49,50 @@ object FileNameValidator {
}
}

if (filename.endsWith(StringConstants.SPACE) || filename.endsWith(StringConstants.DOT)) {
return context.getString(R.string.file_name_validator_error_ends_with_space_period)
}

checkInvalidCharacters(filename, capability, context)?.let {
return it
}

capability.forbiddenFilenameBaseNames().let {
val forbiddenFilenameBaseNames = capability.forbiddenFilenameBaseNames().map { it.lowercase() }

if (forbiddenFilenameBaseNames.contains(filename.lowercase()) || forbiddenFilenameBaseNames.contains(
filename.removeFileExtension().lowercase()
)
) {
return context.getString(
R.string.file_name_validator_error_reserved_names,
filename.substringBefore(StringConstants.DOT)
)
if (capability.version.isNewerOrEqual(NextcloudVersion.nextcloud_30)) {
checkInvalidCharacters(filename, capability, context)?.let {
return it
}
}

capability.forbiddenFilenamesJson?.let {
val forbiddenFilenames = capability.forbiddenFilenames().map { it.lowercase() }

if (forbiddenFilenames.contains(filename.uppercase()) || forbiddenFilenames.contains(
filename.removeFileExtension().uppercase()
)
) {
return context.getString(
R.string.file_name_validator_error_reserved_names,
filename.substringBefore(StringConstants.DOT)
)
}
}

capability.forbiddenFilenameExtensionJson?.let {
val forbiddenFilenameExtension = capability.forbiddenFilenameExtension()

if (forbiddenFilenameExtension.any { filename.endsWith(it, ignoreCase = true) }) {
return context.getString(
R.string.file_name_validator_error_forbidden_file_extensions,
filename.substringAfter(StringConstants.DOT)
)
capability.run {
val filenameVariants = setOf(filename.lowercase(), filename.removeFileExtension().lowercase())

forbiddenFilenameBaseNames().let {
val forbiddenBaseNames = forbiddenFilenameBaseNames().map { it.lowercase() }

for (forbiddenBaseName in forbiddenBaseNames) {
if (forbiddenBaseName in filenameVariants) {
return context.getString(
R.string.file_name_validator_error_reserved_names,
forbiddenBaseName
)
}
}
}

forbiddenFilenamesJson?.let {
val forbiddenFilenames = forbiddenFilenames().map { it.lowercase() }

for (forbiddenFilename in forbiddenFilenames) {
if (forbiddenFilename in filenameVariants) {
return context.getString(
R.string.file_name_validator_error_reserved_names,
forbiddenFilename
)
}
}
}

forbiddenFilenameExtensionJson?.let {
for (forbiddenExtension in forbiddenFilenameExtension()) {
if (filename.endsWith(forbiddenExtension, ignoreCase = true)) {
return context.getString(
R.string.file_name_validator_error_forbidden_file_extensions,
forbiddenExtension
)
}
}
}
}
}

Expand Down Expand Up @@ -153,5 +154,5 @@ object FileNameValidator {

fun isFileHidden(name: String): Boolean = !TextUtils.isEmpty(name) && name[0] == '.'

fun isFileNameAlreadyExist(name: String, fileNames: MutableSet<String>): Boolean = fileNames.contains(name)
fun isFileNameAlreadyExist(name: String, fileNames: Set<String>): Boolean = fileNames.contains(name)
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList
private fun getOCCapability(): OCCapability = fileDataStorageManager.getCapability(accountProvider.user.accountName)

private fun checkFileNameAfterEachType(fileNames: MutableSet<String>) {
val newFileName = binding.userInput.text?.toString()?.trim() ?: ""
val newFileName = binding.userInput.text?.toString() ?: ""

val fileNameValidatorResult: String? =
FileNameValidator.checkFileName(newFileName, getOCCapability(), requireContext(), fileNames)
Expand Down Expand Up @@ -172,7 +172,7 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList
override fun onClick(dialog: DialogInterface, which: Int) {
if (which == AlertDialog.BUTTON_POSITIVE) {
val newFolderName = (getDialog()?.findViewById<View>(R.id.user_input) as TextView)
.text.toString().trim { it <= ' ' }
.text.toString()

val errorMessage: String? =
FileNameValidator.checkFileName(newFolderName, getOCCapability(), requireContext())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class RenameFileDialogFragment : DialogFragment(), DialogInterface.OnClickListen
var newFileName = ""

if (binding.userInput.text != null) {
newFileName = binding.userInput.text.toString().trim { it <= ' ' }
newFileName = binding.userInput.text.toString()
}

val errorMessage = checkFileName(newFileName, oCCapability, requireContext(), null)
Expand Down Expand Up @@ -166,7 +166,7 @@ class RenameFileDialogFragment : DialogFragment(), DialogInterface.OnClickListen
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
var newFileName = ""
if (binding.userInput.text != null) {
newFileName = binding.userInput.text.toString().trim { it <= ' ' }
newFileName = binding.userInput.text.toString()
}

val errorMessage = checkFileName(newFileName, oCCapability, requireContext(), fileNames)
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,7 @@
<string name="file_migration_waiting_for_unfinished_sync">بإنتظار مزامنة كاملة…</string>
<string name="file_name_validator_current_path_is_invalid">اسم المجلد الحالي غير صحيح. قُم رجاءً بتغيير اسم المجلد. إعادة التوجيه إلى المجلد الجذر</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">مسار المجلد يحتوي على أسماء محجوزة أو حروف غير صالحة</string>
<string name="file_name_validator_error_ends_with_space_period">اسم ينتهي بفراغ أو نقطة</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s امتداد ملف غير مسموحٍ به</string>
<string name="file_name_validator_error_forbidden_file_extensions">%s امتداد ملف غير مسموحٍ به</string>
<string name="file_name_validator_error_invalid_character">اسم يحتوي على حروف غير صالحة: %s</string>
<string name="file_name_validator_error_reserved_names">%s اسم ممنوع</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. قُم رجاءً بتغيير اسم الملف قبل نقله أو نسخه</string>
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/res/values-b+en+001/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,7 @@
<string name="file_migration_waiting_for_unfinished_sync">Awaiting full sync…</string>
<string name="file_name_validator_current_path_is_invalid">Current folder name is invalid, please rename the folder. Redirecting to root</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">Folder path contains reserved names or invalid character</string>
<string name="file_name_validator_error_ends_with_space_period">Name ends with a space or a period</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s is a forbidden file extension</string>
<string name="file_name_validator_error_forbidden_file_extensions">%s is a forbidden file extension</string>
<string name="file_name_validator_error_invalid_character">Name contains an invalid character: %s</string>
<string name="file_name_validator_error_reserved_names">%s is a forbidden name</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Please rename the file before moving or copying</string>
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,7 @@
<string name="file_migration_waiting_for_unfinished_sync">Warte auf die Fertigstellung aller Synchronisierungen …</string>
<string name="file_name_validator_current_path_is_invalid">Der aktuelle Ordnername ist unzulässig. Bitte benennen Sie den Ordner um. Weiterleitung zum Stammverzeichnis</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">Der Ordnerpfad enthält reservierte Namen oder ungültige Zeichen</string>
<string name="file_name_validator_error_ends_with_space_period">Der Name endet mit einem Leerzeichen oder einem Punkt</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%s ist eine unzulässige Dateierweiterung</string>
<string name="file_name_validator_error_forbidden_file_extensions">%s ist eine unzulässige Dateierweiterung</string>
<string name="file_name_validator_error_invalid_character">Der Name enthält ein unzulässiges Zeichen: %s</string>
<string name="file_name_validator_error_reserved_names">%s ist ein unzulässiger Name</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Bitte Datei umbenennen, bevor diese verschoben oder kopiert wird</string>
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/res/values-eu/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,7 @@
<string name="file_migration_waiting_for_unfinished_sync">Sinkronizazio osoaren zain...</string>
<string name="file_name_validator_current_path_is_invalid">Uneko karpetaren izena ez da baliozkoa, mesedez izena aldatu karpetari. Errora birbideratzen</string>
<string name="file_name_validator_error_contains_reserved_names_or_invalid_characters">Karpetaren bideak izen erreserbatuak edo karaktere baliogabeak ditu</string>
<string name="file_name_validator_error_ends_with_space_period">Izena tarte batekin edo puntu batekin amaitzen da</string>
<string name="file_name_validator_error_forbidden_file_extensions">.%sdebekatutako fitxategi-luzapena da</string>
<string name="file_name_validator_error_forbidden_file_extensions">%sdebekatutako fitxategi-luzapena da</string>
<string name="file_name_validator_error_invalid_character">Izenak karaktere baliogabe bat dauka: %s</string>
<string name="file_name_validator_error_reserved_names">%s debekatutako izen bat da</string>
<string name="file_name_validator_rename_before_move_or_copy">%s. Aldatu fitxategiaren izena mugitu edo kopiatu aurretik</string>
Expand Down
Loading

0 comments on commit 2f4dcbd

Please sign in to comment.