}
func modifyItem(
- _ item: NSFileProviderItem, baseVersion _: NSFileProviderItemVersion,
- changedFields: NSFileProviderItemFields, contents newContents: URL?,
- options: NSFileProviderModifyItemOptions = [], request: NSFileProviderRequest,
- completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?)
- ->
- Void
+ _ item: NSFileProviderItem,
+ baseVersion: NSFileProviderItemVersion,
+ changedFields: NSFileProviderItemFields,
+ contents newContents: URL?,
+ options: NSFileProviderModifyItemOptions = [],
+ request: NSFileProviderRequest,
+ completionHandler: @escaping (
+ NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?
+ ) -> Void
) -> Progress {
// An item was modified on disk, process the item's modification
// TODO: Handle finder things like tags, other possible item changed fields
+ let identifier = item.itemIdentifier
+ let ocId = identifier.rawValue
Logger.fileProviderExtension.debug(
- "Received modify item request for item with identifier: \(item.itemIdentifier.rawValue, privacy: .public) and filename: \(item.filename, privacy: .public)"
+ """
+ Received modify item request for item with identifier: \(ocId, privacy: .public)
+ and filename: \(item.filename, privacy: .public)
+ """
)
guard let ncAccount else {
Logger.fileProviderExtension.error(
- "Not modifying item: \(item.itemIdentifier.rawValue, privacy: .public) as account not set up yet"
+ "Not modifying item: \(ocId, privacy: .public) as account not set up yet."
)
completionHandler(item, [], false, NSFileProviderError(.notAuthenticated))
return Progress()
}
- let dbManager = FilesDatabaseManager.shared
- let parentItemIdentifier = item.parentItemIdentifier
- let itemTemplateIsFolder = item.contentType == .folder || item.contentType == .directory
-
- if options.contains(.mayAlreadyExist) {
- // TODO: This needs to be properly handled with a check in the db
- Logger.fileProviderExtension.warning(
- "Modification for item: \(item.itemIdentifier.rawValue, privacy: .public) may already exist"
- )
- }
-
- var parentItemServerUrl: String
-
- if parentItemIdentifier == .rootContainer {
- parentItemServerUrl = ncAccount.davFilesUrl
- } else {
- 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()
- }
-
- parentItemServerUrl = parentItemMetadata.serverUrl + "/" + parentItemMetadata.fileName
- }
-
- let fileNameLocalPath = newContents?.path ?? ""
- 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: .public) to server url: \(newServerUrlFileName, privacy: .public) with contents located at: \(fileNameLocalPath, privacy: .public)"
- )
-
- var modifiedItem = item
-
- // Create a serial dispatch queue
- // We want to wait for network operations to finish before we fire off subsequent network
- // operations, or we might cause explosions (e.g. trying to modify items that have just been
- // moved elsewhere)
- let dispatchQueue = DispatchQueue(label: "modifyItemQueue", qos: .userInitiated)
-
- if changedFields.contains(.filename) || changedFields.contains(.parentItemIdentifier) {
- dispatchQueue.async {
- let ocId = item.itemIdentifier.rawValue
- Logger.fileProviderExtension.debug(
- "Changed fields for item \(ocId, privacy: .public) with filename \(item.filename, privacy: .public) includes filename or parentitemidentifier..."
- )
-
- guard let metadata = dbManager.itemMetadataFromOcId(ocId) else {
- Logger.fileProviderExtension.error(
- "Could not acquire metadata of item with identifier: \(item.itemIdentifier.rawValue, privacy: .public)"
- )
- completionHandler(item, [], false, NSFileProviderError(.noSuchItem))
- return
- }
-
- var renameError: NSFileProviderError?
- let oldServerUrlFileName = metadata.serverUrl + "/" + metadata.fileName
-
- let moveFileOrFolderDispatchGroup = DispatchGroup() // Make this block wait until done
- moveFileOrFolderDispatchGroup.enter()
-
- self.ncKit.moveFileOrFolder(
- serverUrlFileNameSource: oldServerUrlFileName,
- serverUrlFileNameDestination: newServerUrlFileName,
- overwrite: false
- ) { _, error in
- guard error == .success else {
- Logger.fileTransfer.error(
- "Could not move file or folder: \(oldServerUrlFileName, privacy: .public) to \(newServerUrlFileName, privacy: .public), received error: \(error.errorDescription, privacy: .public)"
- )
- renameError = error.fileProviderError
- moveFileOrFolderDispatchGroup.leave()
- return
- }
-
- // Remember that a folder metadata's serverUrl is its direct server URL, while for
- // an item metadata the server URL is the parent folder's URL
- if itemTemplateIsFolder {
- _ = dbManager.renameDirectoryAndPropagateToChildren(
- ocId: ocId, newServerUrl: newServerUrlFileName,
- newFileName: item.filename)
- self.signalEnumerator { error in
- if error != nil {
- Logger.fileTransfer.error(
- "Error notifying change in moved directory: \(error)")
- }
- }
- } else {
- dbManager.renameItemMetadata(
- ocId: ocId, newServerUrl: parentItemServerUrl,
- newFileName: item.filename)
- }
-
- guard let newMetadata = dbManager.itemMetadataFromOcId(ocId) else {
- Logger.fileTransfer.error(
- "Could not acquire metadata of item with identifier: \(ocId, privacy: .public), cannot correctly inform of modification"
- )
- renameError = NSFileProviderError(.noSuchItem)
- moveFileOrFolderDispatchGroup.leave()
- return
- }
-
- modifiedItem = Item(
- metadata: newMetadata,
- parentItemIdentifier: parentItemIdentifier,
- ncKit: self.ncKit
- )
- moveFileOrFolderDispatchGroup.leave()
- }
-
- moveFileOrFolderDispatchGroup.wait()
-
- guard renameError == nil else {
- Logger.fileTransfer.error(
- "Stopping rename of item with ocId \(ocId, privacy: .public) due to error: \(renameError!.localizedDescription, privacy: .public)"
- )
- completionHandler(modifiedItem, [], false, renameError)
- return
- }
-
- guard !itemTemplateIsFolder else {
- Logger.fileTransfer.debug(
- "Only handling renaming for folders. ocId: \(ocId, privacy: .public)")
- completionHandler(modifiedItem, [], false, nil)
- return
- }
- }
-
- // Return the progress if item is folder here while the async block runs
- guard !itemTemplateIsFolder else {
- return Progress()
- }
- }
-
- guard !itemTemplateIsFolder else {
- Logger.fileTransfer.debug(
- "System requested modification for folder with ocID \(item.itemIdentifier.rawValue, privacy: .public) (\(newServerUrlFileName, privacy: .public)) of something other than folder name."
+ guard let item = Item.storedItem(identifier: identifier, usingKit: ncKit) else {
+ Logger.fileProviderExtension.error(
+ "Not modifying item: \(ocId, privacy: .public) as item not found."
)
- completionHandler(modifiedItem, [], false, nil)
+ completionHandler(item, [], false, NSFileProviderError(.noSuchItem))
return Progress()
}
let progress = Progress()
-
- if changedFields.contains(.contents) {
- dispatchQueue.async {
- Logger.fileProviderExtension.debug(
- "Item modification for \(item.itemIdentifier.rawValue, privacy: .public) \(item.filename, privacy: .public) includes contents"
- )
-
- guard newContents != nil else {
- Logger.fileProviderExtension.warning(
- "WARNING. Could not upload modified contents as was provided nil contents url. ocId: \(item.itemIdentifier.rawValue, privacy: .public)"
- )
- completionHandler(modifiedItem, [], false, NSFileProviderError(.noSuchItem))
- return
- }
-
- let ocId = item.itemIdentifier.rawValue
- guard let metadata = dbManager.itemMetadataFromOcId(ocId) else {
- Logger.fileProviderExtension.error(
- "Could not acquire metadata of item with identifier: \(ocId, privacy: .public)"
- )
- completionHandler(
- item, NSFileProviderItemFields(), false, NSFileProviderError(.noSuchItem))
- return
- }
-
- dbManager.setStatusForItemMetadata(
- metadata, status: ItemMetadata.Status.uploading
- ) { updatedMetadata in
-
- if updatedMetadata == nil {
- Logger.fileProviderExtension.warning(
- "Could not acquire updated metadata of item with identifier: \(ocId, privacy: .public), unable to update item status to uploading"
- )
- }
-
- self.ncKit.upload(
- serverUrlFileName: newServerUrlFileName,
- fileNameLocalPath: fileNameLocalPath,
- requestHandler: { request in
- progress.setHandlersFromAfRequest(request)
- },
- taskHandler: { task in
- NSFileProviderManager(for: self.domain)?.register(
- task, forItemWithIdentifier: item.itemIdentifier,
- completionHandler: { _ in })
- },
- progressHandler: { uploadProgress in
- uploadProgress.copyCurrentStateToProgress(progress)
- }
- ) { account, ocId, etag, date, size, _, _, error in
- if error == .success, let ocId {
- Logger.fileProviderExtension.info(
- "Successfully uploaded item with identifier: \(ocId, privacy: .public) and filename: \(item.filename, privacy: .public)"
- )
-
- if size != item.documentSize as? Int64 {
- Logger.fileTransfer.warning(
- "Created item upload reported as successful, but there are differences between the received file size (\(size, privacy: .public)) and the original file size (\(item.documentSize??.int64Value ?? 0))"
- )
- }
-
- let newMetadata = ItemMetadata()
- newMetadata.date = (date ?? NSDate()) as Date
- newMetadata.etag = etag ?? ""
- newMetadata.account = account
- newMetadata.fileName = item.filename
- newMetadata.fileNameView = item.filename
- newMetadata.ocId = ocId
- newMetadata.size = size
- newMetadata.contentType = item.contentType?.preferredMIMEType ?? ""
- newMetadata.directory = itemTemplateIsFolder
- newMetadata.serverUrl = parentItemServerUrl
- newMetadata.session = ""
- newMetadata.sessionError = ""
- newMetadata.sessionTaskIdentifier = 0
- newMetadata.status = ItemMetadata.Status.normal.rawValue
-
- dbManager.addLocalFileMetadataFromItemMetadata(newMetadata)
- dbManager.addItemMetadata(newMetadata)
-
- modifiedItem = Item(
- metadata: newMetadata,
- parentItemIdentifier: parentItemIdentifier,
- ncKit: self.ncKit
- )
- completionHandler(modifiedItem, [], false, nil)
- } else {
- Logger.fileTransfer.error(
- "Could not upload item \(item.itemIdentifier.rawValue, privacy: .public) with filename: \(item.filename, privacy: .public), received error: \(error.errorDescription, privacy: .public)"
- )
-
- metadata.status = ItemMetadata.Status.uploadError.rawValue
- metadata.sessionError = error.errorDescription
-
- dbManager.addItemMetadata(metadata)
-
- completionHandler(modifiedItem, [], false, error.fileProviderError)
- return
- }
- }
- }
- }
- } else {
- Logger.fileProviderExtension.debug(
- "Nothing more to do with \(item.itemIdentifier.rawValue, privacy: .public) \(item.filename, privacy: .public), modifications complete"
+ Task {
+ let (modifiedItem, error) = await item.modify(
+ baseVersion: baseVersion,
+ changedFields: changedFields,
+ contents: newContents,
+ options: options,
+ request: request,
+ ncAccount: ncAccount,
+ domain: domain,
+ progress: progress
)
- completionHandler(modifiedItem, [], false, nil)
+ completionHandler(modifiedItem ?? item, [], false, error)
}
-
return progress
}