Skip to content

Commit 67f3cf5

Browse files
authored
Skip auto rename and filename validator if WCF disabled or version <=32 (#201)
* WIP Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com> * Add tests Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com> * Add more tests Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com> * Copyright Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com> * Refactor Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com> --------- Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
1 parent 0c2541e commit 67f3cf5

6 files changed

Lines changed: 321 additions & 150 deletions

File tree

Sources/NextcloudKit/NextcloudKit+Capabilities.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ public extension NextcloudKit {
288288
let bigfilechunking: Bool?
289289
let versiondeletion: Bool?
290290
let versionlabeling: Bool?
291+
let windowsCompatibleFilenamesEnabled: Bool?
291292
let forbiddenFileNames: [String]?
292293
let forbiddenFileNameBasenames: [String]?
293294
let forbiddenFileNameCharacters: [String]?
@@ -298,6 +299,7 @@ public extension NextcloudKit {
298299
case undelete, locking, comments, versioning, directEditing, bigfilechunking
299300
case versiondeletion = "version_deletion"
300301
case versionlabeling = "version_labeling"
302+
case windowsCompatibleFilenamesEnabled = "windows_compatible_filenames"
301303
case forbiddenFileNames = "forbidden_filenames"
302304
case forbiddenFileNameBasenames = "forbidden_filename_basenames"
303305
case forbiddenFileNameCharacters = "forbidden_filename_characters"
@@ -419,6 +421,7 @@ public extension NextcloudKit {
419421

420422
capabilities.securityGuardDiagnostics = json.securityguard?.diagnostics ?? false
421423

424+
capabilities.windowsCompatibleFilenamesEnabled = json.files?.windowsCompatibleFilenamesEnabled ?? false
422425
capabilities.forbiddenFileNames = json.files?.forbiddenFileNames ?? []
423426
capabilities.forbiddenFileNameBasenames = json.files?.forbiddenFileNameBasenames ?? []
424427
capabilities.forbiddenFileNameCharacters = json.files?.forbiddenFileNameCharacters ?? []
@@ -511,6 +514,8 @@ final public class NKCapabilities: Sendable {
511514
public var assistantEnabled: Bool = false // NC28
512515
public var isLivePhotoServerAvailable: Bool = false // NC28
513516
public var securityGuardDiagnostics = false
517+
/// Only taken into account for major version >= 32
518+
public var windowsCompatibleFilenamesEnabled = false
514519
public var forbiddenFileNames: [String] = []
515520
public var forbiddenFileNameBasenames: [String] = []
516521
public var forbiddenFileNameCharacters: [String] = []
@@ -523,6 +528,28 @@ final public class NKCapabilities: Sendable {
523528
public var directEditingTemplates: [NKEditorTemplate] = []
524529

525530
public init() {}
531+
532+
/**
533+
Determines whether Windows-compatible filename (WCF) restrictions should be applied
534+
for the current server version and configuration.
535+
536+
Behavior:
537+
- For Nextcloud 32 and newer, WCF enforcement depends on the `windowsCompatibleFilenamesEnabled` flag
538+
provided by the server capabilities.
539+
- For Nextcloud 30 and 31, WCF restrictions are always applied (feature considered enabled).
540+
- For versions older than 30, WCF is not supported, and no restrictions are applied.
541+
542+
- Returns: `true` if WCF restrictions should be enforced based on the server version and configuration; `false` otherwise.
543+
*/
544+
public var shouldEnforceWindowsCompatibleFilenames: Bool {
545+
if serverVersionMajor >= 32 {
546+
return windowsCompatibleFilenamesEnabled
547+
} else if serverVersionMajor >= 30 {
548+
return true
549+
} else {
550+
return false
551+
}
552+
}
526553
}
527554

528555
// MARK: - Public API

Sources/NextcloudKit/Utils/FileAutoRenamer.swift

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,24 @@
44

55
import Foundation
66

7-
//
8-
// AutoRenameManager.swift
9-
// Nextcloud
10-
//
11-
// Created by Milen Pivchev on 09.10.24.
12-
// Copyright © 2024 Marino Faggiana. All rights reserved.
13-
//
14-
157
public final class FileAutoRenamer: Sendable {
168
private let forbiddenFileNameCharacters: [String]
179
private let forbiddenFileNameExtensions: [String]
10+
private let capabilities: NKCapabilities.Capabilities
1811

1912
private let replacement = "_"
2013

21-
public init(forbiddenFileNameCharacters: [String] = [], forbiddenFileNameExtensions: [String] = []) {
22-
self.forbiddenFileNameCharacters = forbiddenFileNameCharacters
23-
self.forbiddenFileNameExtensions = forbiddenFileNameExtensions.map { $0.lowercased() }
14+
public init(capabilities: NKCapabilities.Capabilities) {
15+
self.forbiddenFileNameCharacters = capabilities.forbiddenFileNameCharacters
16+
self.forbiddenFileNameExtensions = capabilities.forbiddenFileNameExtensions.map { $0.lowercased() }
17+
self.capabilities = capabilities
2418
}
2519

2620
public func rename(filename: String, isFolderPath: Bool = false) -> String {
21+
if !capabilities.shouldEnforceWindowsCompatibleFilenames {
22+
return filename
23+
}
24+
2725
var pathSegments = filename.split(separator: "/", omittingEmptySubsequences: false).map { String($0) }
2826
var mutableForbiddenFileNameCharacters = self.forbiddenFileNameCharacters
2927

Sources/NextcloudKit/Utils/FileNameValidator.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public final class FileNameValidator: Sendable {
99
private let forbiddenFileNameBasenames: [String]
1010
private let forbiddenFileNameCharacters: [String]
1111
private let forbiddenFileNameExtensions: [String]
12+
private let capabilities: NKCapabilities.Capabilities
1213

1314
public func fileEmptyNameError() -> NKError {
1415
NKError(errorCode: NSURLErrorCannotCreateFile, errorDescription: NSLocalizedString("_file_name_empty_", value: "File name cannot be empty.", comment: ""))
@@ -36,14 +37,19 @@ public final class FileNameValidator: Sendable {
3637
return NKError(errorCode: NSURLErrorCannotCreateFile, errorDescription: errorMessage)
3738
}
3839

39-
public init(forbiddenFileNames: [String], forbiddenFileNameBasenames: [String], forbiddenFileNameCharacters: [String], forbiddenFileNameExtensions: [String]) {
40-
self.forbiddenFileNames = forbiddenFileNames.map { $0.uppercased() }
41-
self.forbiddenFileNameBasenames = forbiddenFileNameBasenames.map { $0.uppercased() }
42-
self.forbiddenFileNameCharacters = forbiddenFileNameCharacters
43-
self.forbiddenFileNameExtensions = forbiddenFileNameExtensions.map { $0.uppercased() }
40+
public init(capabilities: NKCapabilities.Capabilities) {
41+
self.forbiddenFileNames = capabilities.forbiddenFileNames.map { $0.uppercased() }
42+
self.forbiddenFileNameBasenames = capabilities.forbiddenFileNameBasenames.map { $0.uppercased() }
43+
self.forbiddenFileNameCharacters = capabilities.forbiddenFileNameCharacters
44+
self.forbiddenFileNameExtensions = capabilities.forbiddenFileNameExtensions.map { $0.uppercased() }
45+
self.capabilities = capabilities
4446
}
4547

4648
public func checkFileName(_ filename: String) -> NKError? {
49+
if !capabilities.shouldEnforceWindowsCompatibleFilenames {
50+
return nil
51+
}
52+
4753
if filename.trimmingCharacters(in: .whitespaces).isEmpty {
4854
return fileEmptyNameError()
4955
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-FileCopyrightText: Nextcloud GmbH
2+
// SPDX-FileCopyrightText: 2025 Milen Pivchev
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
import Testing
6+
@testable import NextcloudKit
7+
8+
/// Behavior:
9+
/// - For Nextcloud 32 and newer, WCF enforcement depends on the `windowsCompatibleFilenamesEnabled` flag
10+
/// provided by the server capabilities.
11+
/// - For Nextcloud 30 and 31, WCF restrictions are always applied (feature considered enabled).
12+
/// - For versions older than 30, WCF is not supported, and no restrictions are applied.
13+
@Suite("WCF restriction checks")
14+
struct CheckWCFRestrictionsTests {
15+
private func makeCapabilities(serverMajor: Int, wcfEnabled: Bool) -> NKCapabilities.Capabilities {
16+
let capabilities = NKCapabilities.Capabilities()
17+
capabilities.serverVersionMajor = serverMajor
18+
capabilities.windowsCompatibleFilenamesEnabled = wcfEnabled
19+
return capabilities
20+
}
21+
22+
@Test("Returns false for versions older than 30")
23+
func returnsFalseForVersionsOlderThan30() {
24+
let capabilities = makeCapabilities(serverMajor: 29, wcfEnabled: true)
25+
#expect(capabilities.shouldEnforceWindowsCompatibleFilenames == false)
26+
}
27+
28+
@Test("Returns true for version 30 when WCF is ALWAYS enabled. Flag is ignored.")
29+
func returnsTrueForVersion30() {
30+
let capabilities = makeCapabilities(serverMajor: 30, wcfEnabled: false)
31+
#expect(capabilities.shouldEnforceWindowsCompatibleFilenames == true)
32+
}
33+
34+
@Test("Returns true for version 31 when WCF is ALAWYS enabled. Flag is ignored.")
35+
func returnsTrueForVersion31() {
36+
let capabilities = makeCapabilities(serverMajor: 31, wcfEnabled: false)
37+
#expect(capabilities.shouldEnforceWindowsCompatibleFilenames == true)
38+
}
39+
40+
@Test("Returns true for version 32 when WCF enabled")
41+
func returnsTrueForVersion32WhenEnabled() {
42+
let capabilities = makeCapabilities(serverMajor: 32, wcfEnabled: true)
43+
#expect(capabilities.shouldEnforceWindowsCompatibleFilenames == true)
44+
}
45+
46+
@Test("Returns false for version 32 when WCF disabled")
47+
func returnsFalseForVersion32WhenDisabled() {
48+
let capabilities = makeCapabilities(serverMajor: 32, wcfEnabled: false)
49+
#expect(capabilities.shouldEnforceWindowsCompatibleFilenames == false)
50+
}
51+
}

0 commit comments

Comments
 (0)