Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Sources/NextcloudCapabilitiesKit/Capabilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public struct Capabilities: Equatable, Sendable {
public let userStatus: UserStatus?
public let filesSharing: FilesSharing?
public let notifyPush: NotifyPush?
public let sharing: Sharing?
public let systemTags: SystemTags?
public let weatherStatus: WeatherStatus?
public let provisioningAPI: ProvisioningAPI?

public init?(data: Data) {
guard let anyJson = try? JSONSerialization.jsonObject(with: data, options: []) else {
Expand Down Expand Up @@ -65,5 +69,9 @@ public struct Capabilities: Equatable, Sendable {
userStatus = UserStatus(capabilities: capabilities)
filesSharing = FilesSharing(capabilities: capabilities)
notifyPush = NotifyPush(capabilities: capabilities)
sharing = Sharing(capabilities: capabilities)
systemTags = SystemTags(capabilities: capabilities)
weatherStatus = WeatherStatus(capabilities: capabilities)
provisioningAPI = ProvisioningAPI(capabilities: capabilities)
}
}
14 changes: 10 additions & 4 deletions Sources/NextcloudCapabilitiesKit/Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
import Foundation

public struct Core: Equatable, Sendable {
let pollInterval: Int
let webdavRoot: String
let referenceApi: Bool
let referenceRegex: String
public let pollInterval: Int
public let webdavRoot: String
public let referenceApi: Bool
public let referenceRegex: String
public let modRewriteWorking: Bool
public let user: CoreUser?
public let canCreateAppToken: Bool?

init?(capabilities: [String: Any]) {
guard let capabilities = capabilities["core"] as? [String: Any] else {
Expand All @@ -19,5 +22,8 @@ public struct Core: Equatable, Sendable {
webdavRoot = capabilities["webdav-root"] as? String ?? "remote.php/webdav"
referenceApi = capabilities["reference-api"] as? Bool ?? false
referenceRegex = capabilities["reference-regex"] as? String ?? ""
modRewriteWorking = capabilities["mod-rewrite-working"] as? Bool ?? false
user = CoreUser(capabilities: capabilities["user"] as? [String: Any] ?? [:])
canCreateAppToken = capabilities["can-create-app-token"] as? Bool
}
}
23 changes: 23 additions & 0 deletions Sources/NextcloudCapabilitiesKit/CoreUser.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: LGPL-3.0-or-later

import Foundation

public struct CoreUser: Equatable, Sendable {
public let language: String
public let locale: String
public let timeZone: String

init?(capabilities: [String: Any]) {
guard let language = capabilities["language"] as? String,
let locale = capabilities["locale"] as? String,
let timeZone = capabilities["timezone"] as? String
else {
return nil
}

self.language = language
self.locale = locale
self.timeZone = timeZone
}
}
18 changes: 15 additions & 3 deletions Sources/NextcloudCapabilitiesKit/Dav.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
import Foundation

public struct Dav: Equatable, Sendable {
let chunking: String
let bulkUpload: String
public let chunking: String
public let publicSharesChunking: Bool
public let searchSupportsCreationTime: Bool
public let searchSupportsUploadTime: Bool
public let searchSupportsLastActivity: Bool
public let bulkUpload: String?
public let absenceSupported: Bool
public let absenceReplacement: Bool

init?(capabilities: [String: Any]) {
guard let capabilities = capabilities["dav"] as? [String: Any] else {
Expand All @@ -14,6 +20,12 @@ public struct Dav: Equatable, Sendable {
}

chunking = capabilities["chunking"] as? String ?? ""
bulkUpload = capabilities["bulkupload"] as? String ?? ""
publicSharesChunking = capabilities["public_shares_chunking"] as? Bool ?? false
searchSupportsCreationTime = capabilities["search_supports_creation_time"] as? Bool ?? false
searchSupportsUploadTime = capabilities["search_supports_upload_time"] as? Bool ?? false
searchSupportsLastActivity = capabilities["search_supports_last_activity"] as? Bool ?? false
bulkUpload = capabilities["bulkupload"] as? String
absenceSupported = capabilities["absence-supported"] as? Bool ?? false
absenceReplacement = capabilities["absence-replacement"] as? Bool ?? false
}
}
26 changes: 26 additions & 0 deletions Sources/NextcloudCapabilitiesKit/Files/FileConversion.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: LGPL-3.0-or-later

import Foundation

public struct FileConversion: Equatable, Sendable {
public let from: String
public let to: String
public let `extension`: String
public let displayName: String

init?(capabilities: [String: Any]) {
guard let from = capabilities["from"] as? String,
let to = capabilities["to"] as? String,
let fileExtension = capabilities["extension"] as? String,
let displayName = capabilities["displayName"] as? String
else {
return nil
}

self.from = from
self.to = to
`extension` = fileExtension
self.displayName = displayName
}
}
14 changes: 14 additions & 0 deletions Sources/NextcloudCapabilitiesKit/Files/Files.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@ import Foundation
public struct Files: Equatable, Sendable {
public let bigFileChunking: Bool
public let blackListedFiles: [String]
public let forbiddenFilenames: [String]
public let forbiddenFilenameBasenames: [String]
public let forbiddenFilenameCharacters: [String]
public let forbiddenFilenameExtensions: [String]
public let fileConversions: [FileConversion]
public let directEditing: DirectEditing?
public let chunkedUpload: ChunkedUpload?
public let comments: Bool
public let undelete: Bool
public let deleteFromTrash: Bool
public let versioning: Bool
public let versionLabeling: Bool
public let versionDeletion: Bool
public let locking: String?
public let windowsCompatibleFilenames: Bool

init?(capabilities: [String: Any]) {
guard let capabilities = capabilities["files"] as? [String: Any] else {
Expand All @@ -23,13 +30,20 @@ public struct Files: Equatable, Sendable {

bigFileChunking = capabilities["bigfilechunking"] as? Bool ?? false
blackListedFiles = capabilities["blacklisted_files"] as? [String] ?? []
forbiddenFilenames = capabilities["forbidden_filenames"] as? [String] ?? []
forbiddenFilenameBasenames = capabilities["forbidden_filename_basenames"] as? [String] ?? []
forbiddenFilenameCharacters = capabilities["forbidden_filename_characters"] as? [String] ?? []
forbiddenFilenameExtensions = capabilities["forbidden_filename_extensions"] as? [String] ?? []
fileConversions = (capabilities["file_conversions"] as? [[String: Any]] ?? []).compactMap(FileConversion.init)
directEditing = DirectEditing(filesCapabilities: capabilities)
chunkedUpload = ChunkedUpload(filesCapabilities: capabilities)
comments = capabilities["comments"] as? Bool ?? false
undelete = capabilities["undelete"] as? Bool ?? false
deleteFromTrash = capabilities["delete_from_trash"] as? Bool ?? false
versioning = capabilities["versioning"] as? Bool ?? false
versionLabeling = capabilities["version_labeling"] as? Bool ?? false
versionDeletion = capabilities["version_deletion"] as? Bool ?? false
locking = capabilities["locking"] as? String ?? nil
windowsCompatibleFilenames = capabilities["windows_compatible_filenames"] as? Bool ?? false
}
}
23 changes: 23 additions & 0 deletions Sources/NextcloudCapabilitiesKit/ProvisioningAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: LGPL-3.0-or-later

import Foundation

public struct ProvisioningAPI: Equatable, Sendable {
public let version: String
public let accountPropertyScopesVersion: Int
public let accountPropertyScopesFederatedEnabled: Bool
public let accountPropertyScopesPublishedEnabled: Bool

init?(capabilities: [String: Any]) {
guard let capabilities = capabilities["provisioning_api"] as? [String: Any] else {
debugPrint("No provisioning API data in received capabilities.")
return nil
}

version = capabilities["version"] as? String ?? ""
accountPropertyScopesVersion = capabilities["AccountPropertyScopesVersion"] as? Int ?? 0
accountPropertyScopesFederatedEnabled = capabilities["AccountPropertyScopesFederatedEnabled"] as? Bool ?? false
accountPropertyScopesPublishedEnabled = capabilities["AccountPropertyScopesPublishedEnabled"] as? Bool ?? false
}
}
4 changes: 4 additions & 0 deletions Sources/NextcloudCapabilitiesKit/Shares/FilesSharing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ public struct FilesSharing: Equatable, Sendable {
public let sharee: Sharee?
public let apiEnabled: Bool
public let resharing: Bool
public let groupSharing: Bool
public let defaultPermissions: Int
public let excludeReshareFromEdit: Bool

init?(capabilities: [String: Any]) {
guard let capabilities = capabilities["files_sharing"] as? [String: Any] else {
Expand All @@ -28,7 +30,9 @@ public struct FilesSharing: Equatable, Sendable {
sharee = Sharee(filesSharingCapabilities: capabilities)
apiEnabled = capabilities["api_enabled"] as? Bool ?? false
resharing = capabilities["resharing"] as? Bool ?? false
groupSharing = capabilities["group_sharing"] as? Bool ?? false
defaultPermissions = capabilities["default_permissions"] as? Int ?? 0
excludeReshareFromEdit = capabilities["exclude_reshare_from_edit"] as? Bool ?? false
debugPrint("Parsed share capabilities.")
}
}
17 changes: 17 additions & 0 deletions Sources/NextcloudCapabilitiesKit/Shares/Share Type/Email.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,38 @@
import Foundation

public struct Email: Equatable, Sendable {
public let enabled: Bool
public let sendsPasswordByEmail: Bool
public let uploadFilesDropEnabled: Bool
public let passwordEnabled: Bool
public let passwordEnforced: Bool
public let expireDateEnabled: Bool
public let expireDateEnforced: Bool

init?(filesSharingCapabilities: [String: Any]) {
guard let emailCaps = filesSharingCapabilities["sharebymail"] as? [String: Any] else {
debugPrint("No email data in received files sharingcapabilities.")
return nil
}

enabled = emailCaps["enabled"] as? Bool ?? false
sendsPasswordByEmail = emailCaps["send_password_by_mail"] as? Bool ?? false
uploadFilesDropEnabled = (emailCaps["upload_files_drop"] as? [String: Any])?["enabled"] as? Bool ?? false

if let passwordCapabilities = emailCaps["password"] as? [String: Any] {
passwordEnabled = passwordCapabilities["enabled"] as? Bool ?? false
passwordEnforced = passwordCapabilities["enforced"] as? Bool ?? false
} else {
passwordEnabled = false
passwordEnforced = false
}

if let expireDateCapabilities = emailCaps["expire_date"] as? [String: Any] {
expireDateEnabled = expireDateCapabilities["enabled"] as? Bool ?? false
expireDateEnforced = expireDateCapabilities["enforced"] as? Bool ?? false
} else {
expireDateEnabled = false
expireDateEnforced = false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import Foundation

public struct Federation: Equatable, Sendable {
let outgoing: Bool
let incoming: Bool
let expireDateEnabled: Bool
let expireDateSupported: Bool
public let outgoing: Bool
public let incoming: Bool
public let expireDateEnabled: Bool
public let expireDateSupported: Bool

init?(filesSharingCapabilities: [String: Any]) {
guard let federationCaps = filesSharingCapabilities["federation"] as? [String: Any] else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import Foundation

public struct Group: Equatable, Sendable {
let enabled: Bool
let expireDateEnabled: Bool
public let enabled: Bool
public let expireDateEnabled: Bool

init?(filesSharingCapabilities: [String: Any]) {
guard let groupCaps = filesSharingCapabilities["group"] as? [String: Any] else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ public struct PublicLink: Equatable, Sendable {
public let enabled: Bool
public let allowUpload: Bool
public let supportsUploadOnly: Bool
public let sendMail: Bool
public let customTokens: Bool
public let askOptionalPassword: Bool
public let passwordEnforced: Bool
public let expireDateEnabled: Bool
public let expireDateEnforced: Bool
public let expireDateDays: Int
public let internalExpireDateEnabled: Bool
public let internalExpireDateEnforced: Bool
public let internalExpireDateDays: Int
public let remoteExpireDateEnabled: Bool
public let remoteExpireDateEnforced: Bool
public let remoteExpireDateDays: Int
public let multipleAllowed: Bool
Expand All @@ -25,8 +30,14 @@ public struct PublicLink: Equatable, Sendable {

enabled = publicLinkCaps["enabled"] as? Bool ?? false
allowUpload = publicLinkCaps["upload"] as? Bool ?? false
supportsUploadOnly = publicLinkCaps["supports_upload_only"] as? Bool ?? false
multipleAllowed = publicLinkCaps["multiple"] as? Bool ?? false
supportsUploadOnly = publicLinkCaps["upload_files_drop"] as? Bool
?? publicLinkCaps["supports_upload_only"] as? Bool
?? false
sendMail = publicLinkCaps["send_mail"] as? Bool ?? false
customTokens = publicLinkCaps["custom_tokens"] as? Bool ?? false
multipleAllowed = publicLinkCaps["multiple_links"] as? Bool
?? publicLinkCaps["multiple"] as? Bool
?? false

if let passwordCaps = publicLinkCaps["password"] as? [String: Any] {
askOptionalPassword = passwordCaps["askForOptionalPassword"] as? Bool ?? false
Expand All @@ -37,25 +48,31 @@ public struct PublicLink: Equatable, Sendable {
}

if let expireDateCapabilities = publicLinkCaps["expire_date"] as? [String: Any] {
expireDateEnabled = expireDateCapabilities["enabled"] as? Bool ?? false
expireDateDays = expireDateCapabilities["days"] as? Int ?? 1
expireDateEnforced = expireDateCapabilities["enforced"] as? Bool ?? false
} else {
expireDateEnabled = false
expireDateDays = 1
expireDateEnforced = false
}

if let internalExpDateCaps = publicLinkCaps["expire_date_internal"] as? [String: Any] {
internalExpireDateEnabled = internalExpDateCaps["enabled"] as? Bool ?? false
internalExpireDateDays = internalExpDateCaps["days"] as? Int ?? 1
internalExpireDateEnforced = internalExpDateCaps["enforced"] as? Bool ?? false
} else {
internalExpireDateEnabled = false
internalExpireDateDays = 1
internalExpireDateEnforced = false
}

if let remoteExpDateCaps = publicLinkCaps["expire_date_remote"] as? [String: Any] {
remoteExpireDateEnabled = remoteExpDateCaps["enabled"] as? Bool ?? false
remoteExpireDateDays = remoteExpDateCaps["days"] as? Int ?? 1
remoteExpireDateEnforced = remoteExpDateCaps["enforced"] as? Bool ?? false
} else {
remoteExpireDateEnabled = false
remoteExpireDateDays = 1
remoteExpireDateEnforced = false
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/NextcloudCapabilitiesKit/Shares/Share Type/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import Foundation

public struct User: Equatable, Sendable {
let sendMail: Bool
let expireDateEnabled: Bool
public let sendMail: Bool
public let expireDateEnabled: Bool

init?(filesSharingCapabilities: [String: Any]) {
guard let userCaps = filesSharingCapabilities["user"] as? [String: Any] else {
Expand Down
6 changes: 4 additions & 2 deletions Sources/NextcloudCapabilitiesKit/Shares/Sharee.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
import Foundation

public struct Sharee: Equatable, Sendable {
let queryLookupDefault: Bool
let alwaysShowUnique: Bool
public let minSearchStringLength: Int
public let queryLookupDefault: Bool
public let alwaysShowUnique: Bool

init?(filesSharingCapabilities: [String: Any]) {
guard let shareeCaps = filesSharingCapabilities["sharee"] as? [String: Any] else {
debugPrint("No sharee data in received files sharingcapabilities.")
return nil
}

minSearchStringLength = shareeCaps["minSearchStringLength"] as? Int ?? 0
queryLookupDefault = shareeCaps["query_lookup_default"] as? Bool ?? false
alwaysShowUnique = shareeCaps["always_show_unique"] as? Bool ?? false
}
Expand Down
Loading
Loading