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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import Foundation
import NextcloudCapabilitiesKit
import NextcloudKit
import UniformTypeIdentifiers

public extension Item {
///
Expand Down Expand Up @@ -190,11 +191,17 @@ public extension Item {
)
}

let contentType: String = if itemTemplate.contentType == .aliasFile {
UTType.aliasFile.identifier
} else {
itemTemplate.contentType?.preferredMIMEType ?? ""
}

let newMetadata = SendableItemMetadata(
ocId: ocId,
account: account.ncKitAccount,
classFile: "", // Placeholder as not set in original code
contentType: itemTemplate.contentType?.preferredMIMEType ?? "",
contentType: contentType,
creationDate: Date(), // Default as not set in original code
date: date ?? Date(),
directory: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@preconcurrency import FileProvider
import Foundation
import NextcloudKit
import UniformTypeIdentifiers

public extension Item {
private func fetchDirectoryContents(
Expand Down Expand Up @@ -199,6 +200,18 @@ public extension Item {

logger.debug("Acquired contents of item.", [.item: ocId, .name: updatedMetadata.fileName])

if !isDirectory, updatedMetadata.contentType != UTType.aliasFile.identifier {
if let fileHandle = try? FileHandle(forReadingFrom: localPath) {
let magic = fileHandle.readData(ofLength: 4)
try? fileHandle.close()
// Apple Bookmark/Alias format magic bytes: "book" (0x62 0x6F 0x6F 0x6B)
if magic == Data([0x62, 0x6F, 0x6F, 0x6B]) {
logger.debug("Detected macOS alias file by magic number.", [.name: updatedMetadata.fileName])
updatedMetadata.contentType = UTType.aliasFile.identifier
}
}
}

updatedMetadata.status = Status.normal.rawValue
updatedMetadata.downloaded = true
// HACK: We were previously failing to correctly set the uploaded state to true for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import NextcloudFileProviderKitMocks
import NextcloudKit
import RealmSwift
import TestInterface
import UniformTypeIdentifiers
import XCTest

final class ItemFetchTests: NextcloudFileProviderKitTestCase {
Expand Down Expand Up @@ -72,6 +73,51 @@ final class ItemFetchTests: NextcloudFileProviderKitTestCase {
XCTAssertEqual(fetchedItem.creationDate, item.creationDate)
}

func testFetchAliasFileContentsDetectsAliasType() async throws {
let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem)
remoteInterface.injectMock(Self.account)

// Construct minimal data starting with the Apple Bookmark magic "book" (0x62 0x6F 0x6F 0x6B)
var aliasData = Data([0x62, 0x6F, 0x6F, 0x6B])
aliasData.append(contentsOf: [UInt8](repeating: 0, count: 60))

let remoteItem = MockRemoteItem(
identifier: "aliasItem",
versionIdentifier: "0",
name: "My Alias", // no extension, no MIME type — as the server would present it
remotePath: Self.account.davFilesUrl + "/My Alias",
data: aliasData,
account: Self.account.ncKitAccount,
username: Self.account.username,
userId: Self.account.id,
serverUrl: Self.account.serverUrl
)
rootItem.children = [remoteItem]
remoteItem.parent = rootItem

// contentType is empty — as it arrives from the server with no MIME type
let itemMetadata = remoteItem.toItemMetadata(account: Self.account)
XCTAssertTrue(itemMetadata.contentType.isEmpty)
Self.dbManager.addItemMetadata(itemMetadata)

let item = Item(
metadata: itemMetadata,
parentItemIdentifier: .rootContainer,
account: Self.account,
remoteInterface: remoteInterface,
dbManager: Self.dbManager
)

let (_, fetchedItemMaybe, error) = await item.fetchContents(dbManager: Self.dbManager)
XCTAssertNil(error)
let fetchedItem = try XCTUnwrap(fetchedItemMaybe)

XCTAssertEqual(fetchedItem.contentType, UTType.aliasFile)

let storedMetadata = Self.dbManager.itemMetadata(ocId: itemMetadata.ocId)
XCTAssertEqual(storedMetadata?.contentType, UTType.aliasFile.identifier)
}

func testFetchDirectoryContents() async throws {
let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem)
remoteInterface.injectMock(Self.account)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,7 @@ import OSLog
return progress
}

func fetchContents(
for itemIdentifier: NSFileProviderItemIdentifier,
version requestedVersion: NSFileProviderItemVersion?,
request: NSFileProviderRequest,
completionHandler: @escaping (URL?, NSFileProviderItem?, Error?) -> Void
) -> Progress {
func fetchContents(for itemIdentifier: NSFileProviderItemIdentifier, version requestedVersion: NSFileProviderItemVersion?, request: NSFileProviderRequest, completionHandler: @escaping (URL?, NSFileProviderItem?, Error?) -> Void) -> Progress {
let actionId = UUID()
insertSyncAction(actionId)
logger.debug("Received request to fetch contents of item.", [.item: itemIdentifier, .request: request])
Expand Down Expand Up @@ -222,33 +217,21 @@ import OSLog
return Progress()
}


let progress = Progress()

Task {
guard let item = await Item.storedItem(
identifier: itemIdentifier,
account: ncAccount,
remoteInterface: ncKit,
dbManager: dbManager,
log: log
) else {
guard let item = await Item.storedItem(identifier: itemIdentifier, account: ncAccount, remoteInterface: ncKit, dbManager: dbManager, log: log) else {
logger.error("Not fetching contents for item because item was not found.", [.item: itemIdentifier])

completionHandler(
nil,
nil,
NSError.fileProviderErrorForNonExistentItem(withIdentifier: itemIdentifier)
)
completionHandler(nil, nil, NSError.fileProviderErrorForNonExistentItem(withIdentifier: itemIdentifier))
insertErrorAction(actionId)
return
}

let (localUrl, updatedItem, error) = await item.fetchContents(
domain: self.domain, progress: progress, dbManager: dbManager
)
let (localUrl, updatedItem, error) = await item.fetchContents(domain: self.domain, progress: progress, dbManager: dbManager)
removeSyncAction(actionId)
completionHandler(localUrl, updatedItem, error)
}

return progress
}

Expand Down
Loading