Skip to content
Open
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
12 changes: 12 additions & 0 deletions Nextcloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@
AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; };
AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; };
AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; };
AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; };
B5C9801E2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C9801D2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */; };
B5C980202DAD201A0041B146 /* NCSortMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C9801F2DAD201A0041B146 /* NCSortMenu.swift */; };
C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; };
D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; };
D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; };
F317C82E2E844C5300761AEA /* ClientIntegrationUIViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F317C82D2E844C5300761AEA /* ClientIntegrationUIViewer.swift */; };
Expand Down Expand Up @@ -1235,6 +1240,9 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = "<group>"; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = "<group>"; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = "<group>"; };
AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = "<group>"; };
B5C9801D2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift"; sourceTree = "<group>"; };
B5C9801F2DAD201A0041B146 /* NCSortMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSortMenu.swift; sourceTree = "<group>"; };
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2004,6 +2012,7 @@
children = (
F376A3732E5CC5FF0067EE25 /* ContextMenuActions.swift */,
F78C6FDD296D677300C952C3 /* NCContextMenu.swift */,
B5C9801F2DAD201A0041B146 /* NCSortMenu.swift */,
3704EB2923D5A58400455C5B /* NCMenu.storyboard */,
371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */,
AF935066276B84E700BD078F /* NCMenu+FloatingPanel.swift */,
Expand Down Expand Up @@ -2492,6 +2501,7 @@
F7603298252F0E550015A421 /* Collection Common */ = {
isa = PBXGroup;
children = (
B5C9801D2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */,
F75FE06B2BB01D0D00A0EFEF /* Cell */,
F78ACD50219046AC0088454D /* Section Header Footer */,
F70D7C3525FFBF81002B9E34 /* NCCollectionViewCommon.swift */,
Expand Down Expand Up @@ -4512,6 +4522,7 @@
F72944F52A8424F800246839 /* NCEndToEndMetadataV1.swift in Sources */,
F710D2022405826100A6033D /* NCViewerContextMenu.swift in Sources */,
F765E9CD295C585800A09ED8 /* NCUploadScanDocument.swift in Sources */,
B5C980202DAD201A0041B146 /* NCSortMenu.swift in Sources */,
F741C2242B6B9FD600E849BB /* NCMediaSelectTabBar.swift in Sources */,
F7BF9D822934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */,
AA8D31662D411FA100FE2775 /* NCShareDateCell.swift in Sources */,
Expand Down Expand Up @@ -4546,6 +4557,7 @@
F70898692EDDB51700EF85BD /* NCSelectOpen+SelectDelegate.swift in Sources */,
F7D4BF412CA2E8D800A5E746 /* TOPasscodeViewControllerAnimatedTransitioning.m in Sources */,
F7D4BF422CA2E8D800A5E746 /* TOPasscodeSettingsViewController.m in Sources */,
B5C9801E2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift in Sources */,
F7D4BF432CA2E8D800A5E746 /* TOPasscodeCircleImage.m in Sources */,
F78026102E9CFA3700B63436 /* NCTransfersView.swift in Sources */,
F7D4BF442CA2E8D800A5E746 /* TOPasscodeView.m in Sources */,
Expand Down
163 changes: 136 additions & 27 deletions iOSClient/Files/NCFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class NCFiles: NCCollectionViewCommon {
internal var lastScrollTime: TimeInterval = 0
internal var accumulatedScrollDown: CGFloat = 0

internal var syncMetadatasTask: Task<Void, Never>?

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

Expand Down Expand Up @@ -99,6 +101,9 @@ class NCFiles: NCCollectionViewCommon {
super.viewWillAppear(animated)

Task {
let capabilities = await database.getCapabilities(account: self.session.account) ?? NKCapabilities.Capabilities()
await mainNavigationController?.createPlusMenu(session: self.session, capabilities: capabilities)

await self.reloadDataSource()
}
}
Expand Down Expand Up @@ -142,10 +147,53 @@ class NCFiles: NCCollectionViewCommon {

// MARK: - DataSource

override func reloadDataSource() async {
guard !isSearchingMode else {
await super.reloadDataSource()
return
override func reloadDataSource() {
guard !isSearchingMode
else {
return super.reloadDataSource()
}

// Watchdog: this is only a fail safe "dead lock", I don't think the timeout will ever be called but at least nothing gets stuck, if after 5 sec. (which is a long time in this routine), the semaphore is still locked
//
if self.semaphoreReloadDataSource.wait(timeout: .now() + 5) == .timedOut {
self.semaphoreReloadDataSource.signal()
}

var predicate = self.defaultPredicate
let predicateDirectory = NSPredicate(format: "account == %@ AND serverUrl == %@", session.account, self.serverUrl)
let dataSourceMetadatas = self.dataSource.getMetadatas()

if NCKeychain().getPersonalFilesOnly(account: session.account) {
predicate = self.personalFilesOnlyPredicate
}

self.metadataFolder = database.getMetadataFolder(session: session, serverUrl: self.serverUrl)
self.richWorkspaceText = database.getTableDirectory(predicate: predicateDirectory)?.richWorkspace

let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView)
self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView)

if metadatas.isEmpty {
self.semaphoreReloadDataSource.signal()
return super.reloadDataSource()
}

self.dataSource.caching(metadatas: metadatas, dataSourceMetadatas: dataSourceMetadatas) {
self.semaphoreReloadDataSource.signal()
super.reloadDataSource()
}
}

override func getServerData(refresh: Bool = false) async {
await super.getServerData()

defer {
stopGUIGetServerData()
startSyncMetadata(metadatas: self.dataSource.getMetadatas())
}

Task {
await networking.networkingTasks.cancel(identifier: "\(self.serverUrl)_NCFiles")
}

self.metadataFolder = await self.database.getMetadataFolderAsync(session: self.session, serverUrl: self.serverUrl)
Expand Down Expand Up @@ -181,7 +229,7 @@ class NCFiles: NCCollectionViewCommon {

override func getServerData(forced: Bool = false) async {
defer {
stopGUIGetServerData()
restoreDefaultTitle()
startSyncMetadata(metadatas: self.dataSource.getMetadatas())
}

Expand Down Expand Up @@ -240,30 +288,22 @@ class NCFiles: NCCollectionViewCommon {
self.collectionView.reloadData()
}
}
guard resultsReadFile.error == .success,
let metadata = resultsReadFile.metadata else {
return(nil, resultsReadFile.error, reloadRequired)
guard resultsReadFile.error == .success, let metadata = resultsReadFile.metadata else {
return (nil, resultsReadFile.error, false)
}
let e2eEncrypted = metadata.e2eEncrypted
let ocId = metadata.ocId

await self.database.updateDirectoryRichWorkspaceAsync(metadata.richWorkspace, account: resultsReadFile.account, serverUrl: serverUrl)
let tableDirectory = await self.database.getTableDirectoryAsync(ocId: metadata.ocId)

// Verify LivePhoto
//
reloadRequired = await networking.setLivePhoto(account: resultsReadFile.account)
await NCManageDatabase.shared.deleteLivePhotoError()

let shouldSkipUpdate: Bool = (
!forced &&
!refresh &&
tableDirectory?.etag == metadata.etag &&
!metadata.e2eEncrypted &&
!self.dataSource.isEmpty()
)

if shouldSkipUpdate {
return (nil, NKError(), reloadRequired)
return (nil, NKError(), false)
}

startGUIGetServerData()
Expand All @@ -272,12 +312,75 @@ class NCFiles: NCCollectionViewCommon {
let (account, metadataFolder, metadatas, error) = await NCNetworking.shared.readFolderAsync(serverUrl: serverUrl,
account: session.account,
options: options) { task in
Task {
await NCNetworking.shared.networkingTasks.track(identifier: "\(self.serverUrl)_NCFiles", task: task)
}
self.dataSourceTask = task
if self.dataSource.isEmpty() {
self.collectionView.reloadData()
}

guard error == .success else {
return (nil, error, false)
}

if let metadataFolder {
self.metadataFolder = metadataFolder.detachedCopy()
self.richWorkspaceText = metadataFolder.richWorkspace
}

guard let metadataFolder,
isDirectoryE2EE,
NCKeychain().isEndToEndEnabled(account: account),
await !NCNetworkingE2EE().isInUpload(account: account, serverUrl: serverUrl) else {
return (metadatas, error, true)
}

/// E2EE
let lock = await self.database.getE2ETokenLockAsync(account: account, serverUrl: serverUrl)
let results = await NCNetworkingE2EE().getMetadata(fileId: metadataFolder.ocId, e2eToken: lock?.e2eToken, account: account)

let results = await NCNetworkingE2EE().getMetadata(fileId: ocId, e2eToken: lock?.e2eToken, account: account)

nkLog(tag: self.global.logTagE2EE, message: "Get metadata with error: \(results.error.errorCode)")
nkLog(tag: self.global.logTagE2EE, message: "Get metadata with metadata: \(results.e2eMetadata ?? ""), signature: \(results.signature ?? ""), version \(results.version ?? "")", minimumLogLevel: .verbose)

guard results.error == .success,
let e2eMetadata = results.e2eMetadata,
let version = results.version else {

// No metadata fount, re-send it
if results.error.errorCode == NCGlobal.shared.errorResourceNotFound {
await showInfoBanner(controller: self.controller, text: "Metadata not found")
let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, account: account)
if error != .success {
await showErrorBanner(controller: self.controller, text: error.errorDescription)
}
} else {
// show error
await showErrorBanner(controller: self.controller, text: error.errorDescription)
}

return(metadatas, error, reloadRequired)
}

let errorDecodeMetadata = await NCEndToEndMetadata().decodeMetadata(e2eMetadata, signature: results.signature, serverUrl: serverUrl, session: self.session)
nkLog(debug: "Decode e2ee metadata with error: \(errorDecodeMetadata.errorCode)")

if errorDecodeMetadata == .success {
let capabilities = await NKCapabilities.shared.getCapabilities(for: self.session.account)
if version == "v1", NCGlobal.shared.isE2eeVersion2(capabilities.e2EEApiVersion) {
await showInfoBanner(controller: self.controller, text: "Conversion metadata v1 to v2 required, please wait...")
nkLog(tag: self.global.logTagE2EE, message: "Conversion v1 to v2")
NCActivityIndicator.shared.start()

let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, updateVersionV1V2: true, account: account)
if error != .success {
await showErrorBanner(controller: self.controller, text: error.errorDescription)
}
NCActivityIndicator.shared.stop()
}
} else {
// Client Diagnostic
await self.database.addDiagnosticAsync(account: account, issue: NCGlobal.shared.diagnosticIssueE2eeErrors)
await showErrorBanner(controller: self.controller, text: error.errorDescription)
}

guard error == .success else {
Expand Down Expand Up @@ -318,14 +421,14 @@ class NCFiles: NCCollectionViewCommon {

// No metadata fount, re-send it
if results.error.errorCode == NCGlobal.shared.errorResourceNotFound {
await showInfoBanner(controller: self.controller, text: "Metadata not found")
NCContentPresenter().showInfo(description: "Metadata not found")
let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, account: account)
if error != .success {
await showErrorBanner(controller: self.controller, text: error.errorDescription)
NCContentPresenter().showError(error: error)
}
} else {
// show error
await showErrorBanner(controller: self.controller, text: error.errorDescription)
NCContentPresenter().showError(error: results.error)
}

return(metadatas, error, reloadRequired)
Expand All @@ -336,21 +439,21 @@ class NCFiles: NCCollectionViewCommon {

if errorDecodeMetadata == .success {
let capabilities = await NKCapabilities.shared.getCapabilities(for: self.session.account)
if version == "v1", NCGlobal.shared.isE2eeVersion2(capabilities.e2EEApiVersion) {
await showInfoBanner(controller: self.controller, text: "Conversion metadata v1 to v2 required, please wait...")
if version == "v1", capabilities.e2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {
NCContentPresenter().showInfo(description: "Conversion metadata v1 to v2 required, please wait...")
nkLog(tag: self.global.logTagE2EE, message: "Conversion v1 to v2")
NCActivityIndicator.shared.start()

let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, updateVersionV1V2: true, account: account)
if error != .success {
await showErrorBanner(controller: self.controller, text: error.errorDescription)
NCContentPresenter().showError(error: error)
}
NCActivityIndicator.shared.stop()
}
} else {
// Client Diagnostic
await self.database.addDiagnosticAsync(account: account, issue: NCGlobal.shared.diagnosticIssueE2eeErrors)
await showErrorBanner(controller: self.controller, text: error.errorDescription)
NCContentPresenter().showError(error: error)
}

return (metadatas, error, reloadRequired)
Expand Down Expand Up @@ -401,6 +504,12 @@ class NCFiles: NCCollectionViewCommon {
navigationController = UIStoryboard(name: "NCIntro", bundle: nil).instantiateInitialViewController() as? UINavigationController
}

UIApplication.shared.firstWindow?.rootViewController = navigationController
} else if let account = tblAccount?.account, account != currentAccount {
Task {
await NCAccount().changeAccount(account, userProfile: nil, controller: controller)
}

UIApplication.shared.mainAppWindow?.rootViewController = navigationController
} else if let account = tblAccount?.account, account != currentAccount {
Task {
Expand Down
Loading