+++ /dev/null
-/*
- * Copyright (C) 2023 by Claudio Cambra <claudio.cambra@nextcloud.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-import Foundation
-import RealmSwift
-
-class NextcloudDirectoryMetadataTable: Object {
- func isInSameRemoteState(_ comparingMetadata: NextcloudDirectoryMetadataTable) -> Bool {
- return comparingMetadata.etag == self.etag &&
- comparingMetadata.e2eEncrypted == self.e2eEncrypted &&
- comparingMetadata.favorite == self.favorite &&
- comparingMetadata.permissions == self.permissions
- }
-
- @Persisted(primaryKey: true) var ocId: String
- @Persisted var account = ""
- @Persisted var colorFolder: String?
- @Persisted var e2eEncrypted: Bool = false
- @Persisted var etag = ""
- @Persisted var favorite: Bool = false
- @Persisted var fileId = ""
- @Persisted var offline: Bool = false
- @Persisted var permissions = ""
- @Persisted var richWorkspace: String?
- @Persisted var serverUrl = ""
- @Persisted var parentDirectoryServerUrl = ""
-}
let config = Realm.Configuration(
fileURL: self.databasePath,
schemaVersion: self.schemaVersion,
- objectTypes: [NextcloudItemMetadataTable.self, NextcloudDirectoryMetadataTable.self, NextcloudLocalFileMetadataTable.self]
+ objectTypes: [NextcloudItemMetadataTable.self, NextcloudLocalFileMetadataTable.self]
)
Realm.Configuration.defaultConfiguration = config
return nil
}
- func directoryMetadata(account: String, serverUrl: String) -> NextcloudDirectoryMetadataTable? {
- if let metadata = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first {
- return NextcloudDirectoryMetadataTable(value: metadata)
- }
+ func directoryMetadata(account: String, serverUrl: String) -> NextcloudItemMetadataTable? {
+ // We want to split by "/" (e.g. cloud.nc.com/files/a/b) but we need to be mindful of "https://c.nc.com"
+ let problematicSeparator = "://"
+ let placeholderSeparator = "__TEMP_REPLACE__"
+ let serverUrlWithoutPrefix = serverUrl.replacingOccurrences(of: problematicSeparator, with: placeholderSeparator)
+ var splitServerUrl = serverUrlWithoutPrefix.split(separator: "/")
+ let directoryItemFileName = String(splitServerUrl.removeLast())
+ let directoryItemServerUrl = splitServerUrl.joined(separator: "/").replacingOccurrences(of: placeholderSeparator, with: problematicSeparator)
- return nil
- }
-
- func directoryMetadata(ocId: String) -> NextcloudDirectoryMetadataTable? {
- if let metadata = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("ocId == %@", ocId).first {
- return NextcloudDirectoryMetadataTable(value: metadata)
+ if let metadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl == %@ AND fileName == %@ AND directory == true", account, directoryItemServerUrl, directoryItemFileName).first {
+ return NextcloudItemMetadataTable(value: metadata)
}
return nil
}
- private func sortedDirectoryMetadatas(_ metadatas: Results<NextcloudDirectoryMetadataTable>) -> [NextcloudDirectoryMetadataTable] {
- let sortedMetadatas = metadatas.sorted(byKeyPath: "serverUrl", ascending: true)
- return Array(sortedMetadatas.map { NextcloudDirectoryMetadataTable(value: $0) })
- }
-
- func childDirectoriesForDirectory(_ directoryMetadata: NextcloudDirectoryMetadataTable) -> [NextcloudDirectoryMetadataTable] {
- let metadatas = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("serverUrl BEGINSWITH %@ AND ocId != %@", directoryMetadata.serverUrl, directoryMetadata.account)
- return sortedDirectoryMetadatas(metadatas)
+ func childDirectoriesForDirectory(_ directoryMetadata: NextcloudItemMetadataTable) -> [NextcloudItemMetadataTable] {
+ let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
+ let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("serverUrl BEGINSWITH %@ AND ocId != %@ AND directory == true", directoryServerUrl, directoryMetadata.account)
+ return sortedItemMetadatas(metadatas)
}
- func parentDirectoryMetadataForItem(_ itemMetadata: NextcloudItemMetadataTable) -> NextcloudDirectoryMetadataTable? {
+ func parentDirectoryMetadataForItem(_ itemMetadata: NextcloudItemMetadataTable) -> NextcloudItemMetadataTable? {
return directoryMetadata(account: itemMetadata.account, serverUrl: itemMetadata.serverUrl)
}
- func directoryMetadatas(account: String) -> [NextcloudDirectoryMetadataTable] {
- let metadatas = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("account == %@", account)
- return sortedDirectoryMetadatas(metadatas)
- }
-
- func directoryMetadatas(account: String, parentDirectoryServerUrl: String) -> [NextcloudDirectoryMetadataTable] {
- let metadatas = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND parentDirectoryServerUrl == %@", account, parentDirectoryServerUrl)
- return sortedDirectoryMetadatas(metadatas)
- }
-
- private func processDirectoryMetadatasToDelete(databaseToWriteTo: Realm,
- existingDirectoryMetadatas: Results<NextcloudDirectoryMetadataTable>,
- updatedDirectoryMetadatas: [NextcloudDirectoryMetadataTable]) {
-
- for existingMetadata in existingDirectoryMetadatas {
- guard !updatedDirectoryMetadatas.contains(where: { $0.ocId == existingMetadata.ocId }),
- let metadataToDelete = directoryMetadata(ocId: existingMetadata.ocId) else { continue }
-
- Logger.ncFilesDatabase.debug("Deleting directory metadata during update. ocID: \(existingMetadata.ocId, privacy: .public), etag: \(existingMetadata.etag, privacy: .public), serverUrl: \(existingMetadata.serverUrl)")
-
- self.deleteDirectoryAndSubdirectoriesMetadata(ocId: metadataToDelete.ocId)
- }
- }
-
- private func processDirectoryMetadatasToUpdate(databaseToWriteTo: Realm,
- existingDirectoryMetadatas: Results<NextcloudDirectoryMetadataTable>,
- updatedDirectoryMetadatas: [NextcloudDirectoryMetadataTable]) {
-
- assert(databaseToWriteTo.isInWriteTransaction)
-
- for updatedMetadata in updatedDirectoryMetadatas {
- if let existingMetadata = existingDirectoryMetadatas.first(where: { $0.ocId == updatedMetadata.ocId }) {
-
- if !existingMetadata.isInSameRemoteState(updatedMetadata) {
-
- databaseToWriteTo.add(NextcloudDirectoryMetadataTable(value: updatedMetadata), update: .all)
- Logger.ncFilesDatabase.debug("Updated existing directory metadata. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), serverUrl: \(updatedMetadata.serverUrl)")
- }
- // Don't update under other circumstances in which the metadata already exists
-
- } else { // This is a new metadata
- databaseToWriteTo.add(NextcloudDirectoryMetadataTable(value: updatedMetadata), update: .all)
- Logger.ncFilesDatabase.debug("Created new directory metadata during update. ocID: \(updatedMetadata.ocId, privacy: .public), etag: \(updatedMetadata.etag, privacy: .public), serverUrl: \(updatedMetadata.serverUrl)")
- }
- }
- }
-
- func updateDirectoryMetadatas(account: String, parentDirectoryServerUrl: String, updatedDirectoryMetadatas: [NextcloudDirectoryMetadataTable]) {
- let database = ncDatabase()
-
- let existingDirectoryMetadatas = ncDatabase().objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND parentDirectoryServerUrl == %@", account, parentDirectoryServerUrl)
-
- // Actual db writing handled internally
- processDirectoryMetadatasToDelete(databaseToWriteTo: database,
- existingDirectoryMetadatas: existingDirectoryMetadatas,
- updatedDirectoryMetadatas: updatedDirectoryMetadatas)
-
- do {
- try database.write {
-
- processDirectoryMetadatasToUpdate(databaseToWriteTo: database,
- existingDirectoryMetadatas: existingDirectoryMetadatas,
- updatedDirectoryMetadatas: updatedDirectoryMetadatas)
- }
- } catch let error {
- Logger.ncFilesDatabase.error("Could not update directory metadatas, received error: \(error.localizedDescription)")
- }
- }
-
- func directoryMetadataFromItemMetadata(directoryItemMetadata: NextcloudItemMetadataTable, recordEtag: Bool = false) -> NextcloudDirectoryMetadataTable {
- var newDirectoryMetadata = NextcloudDirectoryMetadataTable()
- let directoryOcId = directoryItemMetadata.ocId
-
- if let existingDirectoryMetadata = directoryMetadata(ocId: directoryOcId) {
- newDirectoryMetadata = existingDirectoryMetadata
+ func directoryMetadata(ocId: String) -> NextcloudItemMetadataTable? {
+ if let metadata = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("ocId == %@ AND directory == true", ocId).first {
+ return NextcloudItemMetadataTable(value: metadata)
}
- if recordEtag {
- newDirectoryMetadata.etag = directoryItemMetadata.etag
- }
-
- newDirectoryMetadata.ocId = directoryOcId
- newDirectoryMetadata.fileId = directoryItemMetadata.fileId
- newDirectoryMetadata.parentDirectoryServerUrl = directoryItemMetadata.serverUrl
- newDirectoryMetadata.serverUrl = directoryItemMetadata.serverUrl + "/" + directoryItemMetadata.fileNameView
- newDirectoryMetadata.account = directoryItemMetadata.account
- newDirectoryMetadata.e2eEncrypted = directoryItemMetadata.e2eEncrypted
- newDirectoryMetadata.favorite = directoryItemMetadata.favorite
- newDirectoryMetadata.permissions = directoryItemMetadata.permissions
-
- return newDirectoryMetadata
+ return nil
}
- func updateDirectoryMetadatasFromItemMetadatas(account: String, parentDirectoryServerUrl: String, updatedDirectoryItemMetadatas: [NextcloudItemMetadataTable], recordEtag: Bool = false) {
-
- var updatedDirMetadatas: [NextcloudDirectoryMetadataTable] = []
-
- for directoryItemMetadata in updatedDirectoryItemMetadatas {
- let newDirectoryMetadata = directoryMetadataFromItemMetadata(directoryItemMetadata: directoryItemMetadata, recordEtag: recordEtag)
- updatedDirMetadatas.append(newDirectoryMetadata)
- }
-
- updateDirectoryMetadatas(account: account, parentDirectoryServerUrl: parentDirectoryServerUrl, updatedDirectoryMetadatas: updatedDirMetadatas)
+ func directoryMetadatas(account: String) -> [NextcloudItemMetadataTable] {
+ let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND directory == true", account)
+ return sortedItemMetadatas(metadatas)
}
- func addDirectoryMetadata(_ metadata: NextcloudDirectoryMetadataTable) {
- let database = ncDatabase()
-
- do {
- try database.write {
- database.add(metadata, update: .all)
- Logger.ncFilesDatabase.debug("Added new directory metadata. ocId: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), serverUrl: \(metadata.serverUrl)")
- }
- } catch let error {
- Logger.ncFilesDatabase.error("Could not add new directory metadata. ocId: \(metadata.ocId, privacy: .public), etag: \(metadata.etag, privacy: .public), serverUrl: \(metadata.serverUrl), received error: \(error.localizedDescription, privacy: .public)")
- }
+ func directoryMetadatas(account: String, parentDirectoryServerUrl: String) -> [NextcloudItemMetadataTable] {
+ let metadatas = ncDatabase().objects(NextcloudItemMetadataTable.self).filter("account == %@ AND parentDirectoryServerUrl == %@ AND directory == true", account, parentDirectoryServerUrl)
+ return sortedItemMetadatas(metadatas)
}
// Deletes all metadatas related to the info of the directory provided
func deleteDirectoryAndSubdirectoriesMetadata(ocId: String) {
let database = ncDatabase()
- guard let directoryMetadata = database.objects(NextcloudDirectoryMetadataTable.self).filter("ocId == %@", ocId).first else {
+ guard let directoryMetadata = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@ AND directory == true", ocId).first else {
Logger.ncFilesDatabase.error("Could not find directory metadata for ocId \(ocId, privacy: .public). Not proceeding with deletion")
return
}
- let results = database.objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account, directoryMetadata.serverUrl)
+ let directoryUrlPath = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
+ let results = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account, directoryUrlPath)
for result in results {
deleteItemMetadata(ocId: result.ocId)
do {
try database.write {
- Logger.ncFilesDatabase.debug("Deleting root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryMetadata.etag, privacy: .public), serverUrl: \(directoryMetadata.serverUrl)")
+ Logger.ncFilesDatabase.debug("Deleting root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryMetadata.etag, privacy: .public), serverUrl: \(directoryUrlPath)")
database.delete(results)
}
} catch let error {
- Logger.ncFilesDatabase.error("Could not delete root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryMetadata.etag, privacy: .public), serverUrl: \(directoryMetadata.serverUrl), received error: \(error.localizedDescription, privacy: .public)")
+ Logger.ncFilesDatabase.error("Could not delete root directory metadata in recursive delete. ocID: \(directoryMetadata.ocId, privacy: .public), etag: \(directoryMetadata.etag, privacy: .public), serverUrl: \(directoryUrlPath), received error: \(error.localizedDescription, privacy: .public)")
}
}
let database = ncDatabase()
do {
- try database.write {
- guard let directoryTableResult = database.objects(NextcloudDirectoryMetadataTable.self).filter("ocId == %@", ocId).first,
- let directoryItemResult = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@", ocId).first else {
- Logger.ncFilesDatabase.error("Could not find a directory with ocID \(ocId, privacy: .public), cannot proceed with recursive renaming")
- return
- }
+ guard let directoryMetadata = database.objects(NextcloudItemMetadataTable.self).filter("ocId == %@ AND directory == true", ocId).first else {
+ Logger.ncFilesDatabase.error("Could not find a directory with ocID \(ocId, privacy: .public), cannot proceed with recursive renaming")
+ return
+ }
- let oldServerUrl = directoryTableResult.serverUrl
+ let oldServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
- let childItemResults = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryTableResult.account, oldServerUrl)
- let childDirectoryResults = database.objects(NextcloudDirectoryMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryTableResult.account, oldServerUrl)
+ let childItemResults = database.objects(NextcloudItemMetadataTable.self).filter("account == %@ AND serverUrl BEGINSWITH %@", directoryMetadata.account, oldServerUrl)
- directoryTableResult.serverUrl = newServerUrl
- database.add(directoryTableResult, update: .all)
- directoryItemResult.fileName = newFileName
- directoryItemResult.fileNameView = newFileName
- database.add(directoryItemResult, update: .all)
- Logger.ncFilesDatabase.debug("Renamed root renaming directory at \(oldServerUrl) to \(newServerUrl)")
+ renameItemMetadata(ocId: ocId, newServerUrl: newServerUrl, newFileName: newFileName)
+ Logger.ncFilesDatabase.debug("Renamed root renaming directory")
+ try database.write {
for childItem in childItemResults {
let oldServerUrl = childItem.serverUrl
let movedServerUrl = oldServerUrl.replacingOccurrences(of: oldServerUrl, with: newServerUrl)
database.add(childItem, update: .all)
Logger.ncFilesDatabase.debug("Moved childItem at \(oldServerUrl) to \(movedServerUrl)")
}
-
- for childDirectory in childDirectoryResults {
- let oldServerUrl = childDirectory.serverUrl
- let oldParentServerUrl = childDirectory.parentDirectoryServerUrl
- let movedServerUrl = oldServerUrl.replacingOccurrences(of: oldServerUrl, with: newServerUrl)
- let movedParentServerUrl = oldServerUrl.replacingOccurrences(of: oldParentServerUrl, with: newServerUrl)
- childDirectory.serverUrl = movedServerUrl
- childDirectory.parentDirectoryServerUrl = movedParentServerUrl
- database.add(childDirectory, update: .all)
- Logger.ncFilesDatabase.debug("Moved childDirectory at \(oldServerUrl) to \(movedServerUrl)")
- }
}
} catch let error {
Logger.ncFilesDatabase.error("Could not rename directory metadata with ocId: \(ocId, privacy: .public) to new serverUrl: \(newServerUrl), received error: \(error.localizedDescription, privacy: .public)")
dispatchGroup.enter() // Add to outer counter
dispatchQueue.async {
+ let directoryServerUrl = directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
+
guard serverError == nil else {
- Logger.enumeration.info("Skipping enumeration of directory for working set: \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) as we have an error.")
+ Logger.enumeration.info("Skipping enumeration of directory for working set: \(directoryServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) as we have an error.")
dispatchGroup.leave()
return;
}
let currentNetworkTaskDispatchGroup = DispatchGroup() // To make this serial queue wait until this task is done
currentNetworkTaskDispatchGroup.enter()
- FileProviderEnumerator.readServerUrl(directoryMetadata.serverUrl, ncAccount: self.ncAccount, ncKit: self.ncKit) { metadatas, _, _, _, readError in
+ FileProviderEnumerator.readServerUrl(directoryServerUrl, ncAccount: self.ncAccount, ncKit: self.ncKit) { metadatas, _, _, _, readError in
guard readError == nil else {
- Logger.enumeration.error("Finishing enumeration of working set directory \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) with error \(readError!.localizedDescription, privacy: .public)")
+ Logger.enumeration.error("Finishing enumeration of working set directory \(directoryServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) with error \(readError!.localizedDescription, privacy: .public)")
let nkError = NKError(error: readError!)
if nkError.isUnauthenticatedError || nkError.isCouldntConnectError {
if let metadatas = metadatas {
allMetadatas += metadatas
} else {
- allMetadatas += dbManager.itemMetadatas(account: self.ncAccount.ncKitAccount, serverUrl: directoryMetadata.serverUrl)
+ allMetadatas += dbManager.itemMetadatas(account: self.ncAccount.ncKitAccount, serverUrl: directoryServerUrl)
}
currentNetworkTaskDispatchGroup.leave()
private static func fullRecursiveScan(ncAccount: NextcloudAccount, ncKit: NextcloudKit, scanChangesOnly: Bool, completionHandler: @escaping(_ newMetadatas: [NextcloudItemMetadataTable], _ updatedMetadatas: [NextcloudItemMetadataTable], _ deletedMetadatas: [NextcloudItemMetadataTable]) -> Void) {
- let rootContainerDirectoryMetadata = NextcloudDirectoryMetadataTable()
- rootContainerDirectoryMetadata.serverUrl = ncAccount.davFilesUrl
- rootContainerDirectoryMetadata.account = ncAccount.ncKitAccount
+ let rootContainerDirectoryMetadata = NextcloudItemMetadataTable()
+ rootContainerDirectoryMetadata.directory = true
rootContainerDirectoryMetadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
// Create a serial dispatch queue
}
}
- private static func scanRecursively(_ directoryMetadata: NextcloudDirectoryMetadataTable, ncAccount: NextcloudAccount, ncKit: NextcloudKit, scanChangesOnly: Bool) -> (metadatas: [NextcloudItemMetadataTable], newMetadatas: [NextcloudItemMetadataTable], updatedMetadatas: [NextcloudItemMetadataTable], deletedMetadatas: [NextcloudItemMetadataTable]) {
+ private static func scanRecursively(_ directoryMetadata: NextcloudItemMetadataTable, ncAccount: NextcloudAccount, ncKit: NextcloudKit, scanChangesOnly: Bool) -> (metadatas: [NextcloudItemMetadataTable], newMetadatas: [NextcloudItemMetadataTable], updatedMetadatas: [NextcloudItemMetadataTable], deletedMetadatas: [NextcloudItemMetadataTable]) {
+
+ assert(directoryMetadata.directory, "Can only recursively scan a directory.")
var allMetadatas: [NextcloudItemMetadataTable] = []
var allNewMetadatas: [NextcloudItemMetadataTable] = []
dispatchGroup.enter()
- Logger.enumeration.debug("About to read: \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash))")
- FileProviderEnumerator.readServerUrl(directoryMetadata.serverUrl, ncAccount: ncAccount, ncKit: ncKit, stopAtMatchingEtags: scanChangesOnly) { metadatas, newMetadatas, updatedMetadatas, deletedMetadatas, readError in
+ let itemServerUrl = directoryMetadata.ocId == NSFileProviderItemIdentifier.rootContainer.rawValue ?
+ ncAccount.davFilesUrl : directoryMetadata.serverUrl + "/" + directoryMetadata.fileName
+
+ Logger.enumeration.debug("About to read: \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash))")
+
+ FileProviderEnumerator.readServerUrl(itemServerUrl, ncAccount: ncAccount, ncKit: ncKit, stopAtMatchingEtags: scanChangesOnly) { metadatas, newMetadatas, updatedMetadatas, deletedMetadatas, readError in
if readError != nil {
let nkReadError = NKError(error: readError!)
// Is the error is that we have found matching etags on this item, then ignore it
// if we are doing a full rescan
guard nkReadError.isNoChangesError && scanChangesOnly else {
- Logger.enumeration.error("Finishing enumeration of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) with \(readError!.localizedDescription, privacy: .public)")
+ Logger.enumeration.error("Finishing enumeration of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) with \(readError!.localizedDescription, privacy: .public)")
if nkReadError.isNotFoundError {
Logger.enumeration.info("404 error means item no longer exists. Deleting metadata and reporting as deletion without error")
}
}
- Logger.enumeration.info("Finished reading serverUrl: \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+ Logger.enumeration.info("Finished reading serverUrl: \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
if let metadatas = metadatas {
allMetadatas += metadatas
} else {
- Logger.enumeration.warning("WARNING: Nil metadatas received for reading of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+ Logger.enumeration.warning("WARNING: Nil metadatas received for reading of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
}
if let newMetadatas = newMetadatas {
allNewMetadatas += newMetadatas
} else {
- Logger.enumeration.warning("WARNING: Nil new metadatas received for reading of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+ Logger.enumeration.warning("WARNING: Nil new metadatas received for reading of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
}
if let updatedMetadatas = updatedMetadatas {
allUpdatedMetadatas += updatedMetadatas
} else {
- Logger.enumeration.warning("WARNING: Nil updated metadatas received for reading of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+ Logger.enumeration.warning("WARNING: Nil updated metadatas received for reading of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
}
if let deletedMetadatas = deletedMetadatas {
allDeletedMetadatas += deletedMetadatas
} else {
- Logger.enumeration.warning("WARNING: Nil deleted metadatas received for reading of changes at \(directoryMetadata.serverUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
+ Logger.enumeration.warning("WARNING: Nil deleted metadatas received for reading of changes at \(itemServerUrl, privacy: OSLogPrivacy.auto(mask: .hash)) for user: \(ncAccount.ncKitAccount, privacy: OSLogPrivacy.auto(mask: .hash))")
}
dispatchGroup.leave()
dispatchGroup.wait()
- var updatedDirectories: [NextcloudDirectoryMetadataTable] = []
+ var updatedDirectories: [NextcloudItemMetadataTable] = []
for updatedMetadata in allUpdatedMetadatas {
if updatedMetadata.directory {
guard let directoryMetadata = dbManager.directoryMetadata(ocId: updatedMetadata.ocId) else {
// We have now scanned this directory's contents, so update with etag in order to not check again if not needed
// unless it's the root container
if serverUrl != ncAccount.davFilesUrl {
- let directoryItemMetadata = dbManager.directoryMetadataFromItemMetadata(directoryItemMetadata: directoryMetadata, recordEtag: true)
- dbManager.addDirectoryMetadata(directoryItemMetadata)
+ dbManager.addItemMetadata(directoryMetadata)
}
- // STORE ETAG-LESS DIRECTORY METADATA FOR CHILD DIRECTORIES
- // Since we haven't scanned the contents of the child directories, don't record their itemMetadata etags in the directory tables
- // This will delete database records for directories that we did not get from the readFileOrFolder (indicating they were deleted)
- // as well as deleting the records for all the children contained by the directories.
- // TODO: Find a way to detect if files have been moved rather than deleted and change the metadata server urls, move the materialised files
- dbManager.updateDirectoryMetadatasFromItemMetadatas(account: ncKitAccount, parentDirectoryServerUrl: serverUrl, updatedDirectoryItemMetadatas: childDirectoriesMetadata)
- // TODO: Notify working set changed if new folders found
-
dbManager.updateItemMetadatas(account: ncKitAccount, serverUrl: serverUrl, updatedMetadatas: metadatas) { newMetadatas, updatedMetadatas, deletedMetadatas in
completionHandler(metadatas, newMetadatas, updatedMetadatas, deletedMetadatas, nil)
}
return Progress()
}
- var parentItemMetadata: NextcloudDirectoryMetadataTable?
+ var parentItemServerUrl: String
if parentItemIdentifier == .rootContainer {
- let rootMetadata = NextcloudDirectoryMetadataTable()
-
- rootMetadata.account = ncAccount.ncKitAccount
- rootMetadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
- rootMetadata.serverUrl = ncAccount.davFilesUrl
-
- parentItemMetadata = rootMetadata
+ parentItemServerUrl = ncAccount.davFilesUrl
} else {
- parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue)
- }
+ guard let parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue) else {
+ Logger.fileProviderExtension.error("Not creating item: \(itemTemplate.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
+ completionHandler(itemTemplate, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
+ return Progress()
+ }
- guard let parentItemMetadata = parentItemMetadata else {
- Logger.fileProviderExtension.error("Not creating item: \(itemTemplate.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
- completionHandler(itemTemplate, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
- return Progress()
+ parentItemServerUrl = parentItemMetadata.serverUrl + "/" + parentItemMetadata.fileName
}
let fileNameLocalPath = url?.path ?? ""
- let newServerUrlFileName = parentItemMetadata.serverUrl + "/" + itemTemplate.filename
+ let newServerUrlFileName = parentItemServerUrl + "/" + itemTemplate.filename
Logger.fileProviderExtension.debug("About to upload item with identifier: \(itemTemplate.itemIdentifier.rawValue, privacy: .public) of type: \(itemTemplate.contentType?.identifier ?? "UNKNOWN") (is folder: \(itemTemplateIsFolder ? "yes" : "no") and filename: \(itemTemplate.filename) to server url: \(newServerUrlFileName, privacy: OSLogPrivacy.auto(mask: .hash)) with contents located at: \(fileNameLocalPath, privacy: OSLogPrivacy.auto(mask: .hash))")
DispatchQueue.global().async {
dbManager.convertNKFilesFromDirectoryReadToItemMetadatas(files, account: account) { directoryMetadata, childDirectoriesMetadata, metadatas in
- let newDirectoryMetadata = dbManager.directoryMetadataFromItemMetadata(directoryItemMetadata: directoryMetadata)
- dbManager.addDirectoryMetadata(newDirectoryMetadata)
dbManager.addItemMetadata(directoryMetadata)
let fpItem = FileProviderItem(metadata: directoryMetadata, parentItemIdentifier: parentItemIdentifier, ncKit: self.ncKit)
newMetadata.size = size
newMetadata.contentType = itemTemplate.contentType?.preferredMIMEType ?? ""
newMetadata.directory = itemTemplateIsFolder
- newMetadata.serverUrl = parentItemMetadata.serverUrl
+ newMetadata.serverUrl = parentItemServerUrl
newMetadata.session = ""
newMetadata.sessionError = ""
newMetadata.sessionTaskIdentifier = 0
Logger.fileProviderExtension.warning("Modification for item: \(item.itemIdentifier.rawValue, privacy: .public) may already exist")
}
- var parentItemMetadata: NextcloudDirectoryMetadataTable?
+ var parentItemServerUrl: String
if parentItemIdentifier == .rootContainer {
- let rootMetadata = NextcloudDirectoryMetadataTable()
-
- rootMetadata.account = ncAccount.ncKitAccount
- rootMetadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue
- rootMetadata.serverUrl = ncAccount.davFilesUrl
-
- parentItemMetadata = rootMetadata
+ parentItemServerUrl = ncAccount.davFilesUrl
} else {
- parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue)
- }
+ guard let parentItemMetadata = dbManager.directoryMetadata(ocId: parentItemIdentifier.rawValue) else {
+ Logger.fileProviderExtension.error("Not modifying item: \(item.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
+ completionHandler(item, [], false, NSFileProviderError(.noSuchItem))
+ return Progress()
+ }
- guard let parentItemMetadata = parentItemMetadata else {
- Logger.fileProviderExtension.error("Not modifying item: \(item.itemIdentifier.rawValue, privacy: .public), could not find metadata for parentItemIdentifier \(parentItemIdentifier.rawValue, privacy: .public)")
- completionHandler(item, [], false, NSFileProviderError(.noSuchItem))
- return Progress()
+ parentItemServerUrl = parentItemMetadata.serverUrl + "/" + parentItemMetadata.fileName
}
let fileNameLocalPath = newContents?.path ?? ""
- let newServerUrlFileName = parentItemMetadata.serverUrl + "/" + item.filename
+ let newServerUrlFileName = parentItemServerUrl + "/" + item.filename
Logger.fileProviderExtension.debug("About to upload modified item with identifier: \(item.itemIdentifier.rawValue, privacy: .public) of type: \(item.contentType?.identifier ?? "UNKNOWN") (is folder: \(itemTemplateIsFolder ? "yes" : "no") and filename: \(item.filename, privacy: OSLogPrivacy.auto(mask: .hash)) to server url: \(newServerUrlFileName, privacy: OSLogPrivacy.auto(mask: .hash)) with contents located at: \(fileNameLocalPath, privacy: OSLogPrivacy.auto(mask: .hash))")
if itemTemplateIsFolder {
dbManager.renameDirectoryAndPropagateToChildren(ocId: ocId, newServerUrl: newServerUrlFileName, newFileName: item.filename)
} else {
- dbManager.renameItemMetadata(ocId: ocId, newServerUrl: parentItemMetadata.serverUrl, newFileName: item.filename)
+ dbManager.renameItemMetadata(ocId: ocId, newServerUrl: parentItemServerUrl, newFileName: item.filename)
}
guard let newMetadata = dbManager.itemMetadataFromOcId(ocId) else {
newMetadata.size = size
newMetadata.contentType = item.contentType?.preferredMIMEType ?? ""
newMetadata.directory = itemTemplateIsFolder
- newMetadata.serverUrl = parentItemMetadata.serverUrl
+ newMetadata.serverUrl = parentItemServerUrl
newMetadata.session = ""
newMetadata.sessionError = ""
newMetadata.sessionTaskIdentifier = 0
materialisedEnumerator.enumerateItems(for: materialisedObserver, startingAt: startingPage)
}
+ func signalEnumerator(completionHandler: @escaping(_ error: Error?) -> Void) {
+ guard let fpManager = NSFileProviderManager(for: self.domain) else {
+ Logger.fileProviderExtension.error("Could not get file provider manager for domain, could not signal enumerator. This might lead to future conflicts.")
+ return
+ }
+
+ fpManager.signalEnumerator(for: .workingSet, completionHandler: completionHandler)
+ }
+
// MARK: Nextcloud desktop client communication
func sendFileProviderDomainIdentifier() {
let command = "FILE_PROVIDER_DOMAIN_IDENTIFIER_REQUEST_REPLY"
import FileProvider
class NextcloudAccount: NSObject {
- let webDavFilesUrlSuffix: String = "/remote.php/dav/files/"
+ private let webDavFilesUrlSuffix: String = "/remote.php/dav/files/"
let username, password, ncKitAccount, serverUrl, davFilesUrl: String
init(user: String, serverUrl: String, password: String) {
5307A6EB2965DB8D001E0C6A /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5307A6EA2965DB8D001E0C6A /* RealmSwift */; };
5307A6F229675346001E0C6A /* NextcloudFilesDatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */; };
5318AD9129BF42FB00CBB71C /* NextcloudItemMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */; };
- 5318AD9329BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9229BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift */; };
5318AD9529BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */; };
5318AD9729BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9629BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift */; };
5318AD9929BF58D000CBB71C /* NKError+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9829BF58D000CBB71C /* NKError+Extensions.swift */; };
/* Begin PBXFileReference section */
5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudFilesDatabaseManager.swift; sourceTree = "<group>"; };
5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudItemMetadataTable.swift; sourceTree = "<group>"; };
- 5318AD9229BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudDirectoryMetadataTable.swift; sourceTree = "<group>"; };
5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudLocalFileMetadataTable.swift; sourceTree = "<group>"; };
5318AD9629BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderMaterialisedEnumerationObserver.swift; sourceTree = "<group>"; };
5318AD9829BF58D000CBB71C /* NKError+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NKError+Extensions.swift"; sourceTree = "<group>"; };
isa = PBXGroup;
children = (
5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */,
- 5318AD9229BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift */,
5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */,
5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */,
);
5352E85B29B7BFE6002CE85C /* Progress+Extensions.swift in Sources */,
536EFC36295E3C1100F4CB13 /* NextcloudAccount.swift in Sources */,
538E396D27F4765000FA63D5 /* FileProviderExtension.swift in Sources */,
- 5318AD9329BF432B00CBB71C /* NextcloudDirectoryMetadataTable.swift in Sources */,
536EFBF7295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift in Sources */,
5318AD9929BF58D000CBB71C /* NKError+Extensions.swift in Sources */,
5318AD9529BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift in Sources */,