2022-03-29 16:00:59 +03:00
/*
* Copyright ( C ) 2022 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 FileProvider
2022-12-27 02:18:00 +03:00
import OSLog
import NCDesktopClientSocketKit
2023-01-04 23:09:48 +03:00
import NextcloudKit
2022-03-29 16:00:59 +03:00
2023-01-28 19:07:13 +03:00
class FileProviderExtension : NSObject , NSFileProviderReplicatedExtension , NKCommonDelegate {
2022-05-13 14:50:17 +03:00
let domain : NSFileProviderDomain
2023-02-02 22:13:35 +03:00
let ncKit = NextcloudKit ( )
lazy var ncKitBackground : NKBackground = {
let nckb = NKBackground ( nkCommonInstance : ncKit . nkCommonInstance )
return nckb
} ( )
2023-01-04 23:09:48 +03:00
let appGroupIdentifier : String ? = Bundle . main . object ( forInfoDictionaryKey : " SocketApiPrefix " ) as ? String
2023-01-04 23:40:30 +03:00
var ncAccount : NextcloudAccount ?
2023-01-04 23:18:21 +03:00
lazy var socketClient : LocalSocketClient ? = {
2023-01-12 18:38:57 +03:00
guard let containerUrl = pathForAppGroupContainer ( ) else {
NSLog ( " Could not start file provider socket client properly as could not get container url " )
2023-01-04 23:18:21 +03:00
return nil ;
}
2023-01-12 18:38:57 +03:00
let socketPath = containerUrl . appendingPathComponent ( " .fileprovidersocket " , conformingTo : . archive )
2023-01-04 23:18:21 +03:00
let lineProcessor = FileProviderSocketLineProcessor ( delegate : self )
2023-01-12 18:38:57 +03:00
return LocalSocketClient ( socketPath : socketPath . path , lineProcessor : lineProcessor )
2023-01-04 23:18:21 +03:00
} ( )
2022-12-29 02:21:33 +03:00
2023-01-04 23:09:48 +03:00
let urlSessionIdentifier : String = " com.nextcloud.session.upload.fileproviderext "
let urlSessionMaximumConnectionsPerHost = 5
lazy var urlSession : URLSession = {
let configuration = URLSessionConfiguration . background ( withIdentifier : urlSessionIdentifier )
configuration . allowsCellularAccess = true
configuration . sessionSendsLaunchEvents = true
configuration . isDiscretionary = false
configuration . httpMaximumConnectionsPerHost = urlSessionMaximumConnectionsPerHost
configuration . requestCachePolicy = NSURLRequest . CachePolicy . reloadIgnoringLocalCacheData
configuration . sharedContainerIdentifier = appGroupIdentifier
2023-02-02 22:13:35 +03:00
let session = URLSession ( configuration : configuration , delegate : ncKitBackground , delegateQueue : OperationQueue . main )
2023-01-04 23:09:48 +03:00
return session
} ( )
2023-02-16 22:38:41 +03:00
var outstandingSessionTasks : [ String : URLSessionTask ] = [ : ]
var outstandingOcIdTemp : [ String : String ] = [ : ]
2023-01-04 23:09:48 +03:00
2022-03-29 16:00:59 +03:00
required init ( domain : NSFileProviderDomain ) {
2022-05-13 14:50:17 +03:00
self . domain = domain
2022-05-11 19:52:14 +03:00
// T h e c o n t a i n i n g a p p l i c a t i o n m u s t c r e a t e a d o m a i n u s i n g ` N S F i l e P r o v i d e r M a n a g e r . a d d ( _ : , c o m p l e t i o n H a n d l e r : ) ` . T h e s y s t e m w i l l t h e n l a u n c h t h e a p p l i c a t i o n e x t e n s i o n p r o c e s s , c a l l ` F i l e P r o v i d e r E x t e n s i o n . i n i t ( d o m a i n : ) ` t o i n s t a n t i a t e t h e e x t e n s i o n f o r t h a t d o m a i n , a n d c a l l m e t h o d s o n t h e i n s t a n c e .
2022-12-27 02:18:00 +03:00
2022-03-29 16:00:59 +03:00
super . init ( )
2023-01-04 23:18:21 +03:00
self . socketClient ? . start ( )
2022-03-29 16:00:59 +03:00
}
func invalidate ( ) {
// TODO: c l e a n u p a n y r e s o u r c e s
2023-03-10 20:37:00 +03:00
NSLog ( " Extension for domain %@ is being torn down " , domain . displayName )
2022-03-29 16:00:59 +03:00
}
2022-12-29 02:21:33 +03:00
// MARK: N S F i l e P r o v i d e r R e p l i c a t e d E x t e n s i o n p r o t o c o l m e t h o d s
2022-03-29 16:00:59 +03:00
func item ( for identifier : NSFileProviderItemIdentifier , request : NSFileProviderRequest , completionHandler : @ escaping ( NSFileProviderItem ? , Error ? ) -> Void ) -> Progress {
// r e s o l v e t h e g i v e n i d e n t i f i e r t o a r e c o r d i n t h e m o d e l
2023-01-27 03:54:32 +03:00
NSLog ( " Received item request for item with identifier: %@ " , identifier . rawValue )
2023-01-26 22:51:23 +03:00
if identifier = = . rootContainer {
2023-01-27 01:01:31 +03:00
guard let ncAccount = ncAccount else {
2023-01-28 19:07:13 +03:00
NSLog ( " Not providing item: %@ as account not set up yet " , identifier . rawValue )
2023-01-26 22:51:23 +03:00
completionHandler ( nil , NSFileProviderError ( . notAuthenticated ) )
return Progress ( )
}
2022-03-29 16:00:59 +03:00
2023-01-26 22:51:23 +03:00
let metadata = NextcloudItemMetadataTable ( )
2023-01-27 01:01:31 +03:00
metadata . account = ncAccount . ncKitAccount
2023-01-26 22:51:23 +03:00
metadata . directory = true
metadata . ocId = NSFileProviderItemIdentifier . rootContainer . rawValue
metadata . fileName = " root "
metadata . fileNameView = " root "
2023-01-27 01:01:31 +03:00
metadata . serverUrl = ncAccount . serverUrl
2023-03-11 03:44:25 +03:00
metadata . classFile = NKCommon . TypeClassFile . directory . rawValue
2023-01-26 22:51:23 +03:00
2023-02-02 22:13:35 +03:00
completionHandler ( FileProviderItem ( metadata : metadata , parentItemIdentifier : NSFileProviderItemIdentifier . rootContainer , ncKit : ncKit ) , nil )
2023-01-26 22:51:23 +03:00
return Progress ( )
}
let dbManager = NextcloudFilesDatabaseManager . shared
2023-03-10 20:37:00 +03:00
2023-01-26 22:51:23 +03:00
guard let metadata = dbManager . itemMetadataFromFileProviderItemIdentifier ( identifier ) ,
let parentItemIdentifier = parentItemIdentifierFromMetadata ( metadata ) else {
completionHandler ( nil , NSFileProviderError ( . noSuchItem ) )
return Progress ( )
}
2023-02-02 22:13:35 +03:00
completionHandler ( FileProviderItem ( metadata : metadata , parentItemIdentifier : parentItemIdentifier , ncKit : ncKit ) , nil )
2022-03-29 16:00:59 +03:00
return Progress ( )
}
func fetchContents ( for itemIdentifier : NSFileProviderItemIdentifier , version requestedVersion : NSFileProviderItemVersion ? , request : NSFileProviderRequest , completionHandler : @ escaping ( URL ? , NSFileProviderItem ? , Error ? ) -> Void ) -> Progress {
2023-02-16 22:38:41 +03:00
NSLog ( " Received request to fetch contents of item with identifier: %@ " , itemIdentifier . rawValue )
2023-03-07 15:47:01 +03:00
guard requestedVersion = = nil else {
// TODO: A d d p r o p e r s u p p o r t f o r f i l e v e r s i o n i n g
NSLog ( " Can't return contents for specific version as this is not supported. " )
completionHandler ( nil , nil , NSError ( domain : NSCocoaErrorDomain , code : NSFeatureUnsupportedError , userInfo : [ : ] ) )
return Progress ( )
}
guard ncAccount != nil else {
NSLog ( " Not fetching contents item: %@ as account not set up yet " , itemIdentifier . rawValue )
completionHandler ( nil , nil , NSFileProviderError ( . notAuthenticated ) )
return Progress ( )
}
2023-02-16 22:38:41 +03:00
let dbManager = NextcloudFilesDatabaseManager . shared
let ocId = itemIdentifier . rawValue
guard let metadata = dbManager . itemMetadataFromOcId ( ocId ) else {
NSLog ( " Could not acquire metadata of item with identifier: %@ " , itemIdentifier . rawValue )
completionHandler ( nil , nil , NSFileProviderError ( . noSuchItem ) )
return Progress ( )
}
guard ! metadata . isDocumentViewableOnly else {
NSLog ( " Could not get contents of item as is readonly: %@ %@ " , itemIdentifier . rawValue , metadata . fileName )
completionHandler ( nil , nil , NSFileProviderError ( . cannotSynchronize ) )
return Progress ( )
}
let serverUrlFileName = metadata . serverUrl + " / " + metadata . fileName
NSLog ( " Fetching file with name %@ at URL: %@ " , metadata . fileName , serverUrlFileName )
2023-03-07 22:19:10 +03:00
var progress = Progress ( )
2023-03-08 02:21:23 +03:00
// TODO: H a n d l e f o l d e r s n i c e l y
2023-02-16 22:38:41 +03:00
do {
let fileNameLocalPath = try localPathForNCFile ( ocId : metadata . ocId , fileNameView : metadata . fileNameView )
2023-02-20 15:42:28 +03:00
guard let updatedMetadata = dbManager . setStatusForItemMetadata ( metadata , status : NextcloudItemMetadataTable . Status . downloading ) else {
NSLog ( " Could not acquire updated metadata of item with identifier: %@ " , itemIdentifier . rawValue )
completionHandler ( nil , nil , NSFileProviderError ( . noSuchItem ) )
return Progress ( )
}
2023-02-16 22:38:41 +03:00
self . ncKit . download ( serverUrlFileName : serverUrlFileName ,
fileNameLocalPath : fileNameLocalPath . path ,
requestHandler : { _ in
} , taskHandler : { task in
self . outstandingSessionTasks [ serverUrlFileName ] = task
NSFileProviderManager ( for : self . domain ) ? . register ( task , forItemWithIdentifier : itemIdentifier , completionHandler : { _ in } )
2023-03-07 22:19:10 +03:00
} , progressHandler : { downloadProgress in
downloadProgress . copyCurrentStateToProgress ( progress )
2023-02-16 22:38:41 +03:00
} ) { _ , etag , date , _ , _ , _ , error in
self . outstandingSessionTasks . removeValue ( forKey : serverUrlFileName )
if error = = . success {
NSLog ( " Acquired contents of item with identifier: %@ and filename: %@ " , itemIdentifier . rawValue , updatedMetadata . fileName )
updatedMetadata . status = NextcloudItemMetadataTable . Status . normal . rawValue
updatedMetadata . date = ( date ? ? NSDate ( ) ) as Date
updatedMetadata . etag = etag ? ? " "
dbManager . addLocalFileMetadataFromItemMetadata ( updatedMetadata )
dbManager . addItemMetadata ( updatedMetadata )
guard let parentItemIdentifier = parentItemIdentifierFromMetadata ( updatedMetadata ) else {
completionHandler ( nil , nil , NSFileProviderError ( . noSuchItem ) )
return
}
let fpItem = FileProviderItem ( metadata : updatedMetadata , parentItemIdentifier : parentItemIdentifier , ncKit : self . ncKit )
completionHandler ( fileNameLocalPath , fpItem , nil )
} else {
NSLog ( " Could not acquire contents of item with identifier: %@ and fileName: %@ " , itemIdentifier . rawValue , updatedMetadata . fileName )
updatedMetadata . status = NextcloudItemMetadataTable . Status . downloadError . rawValue
updatedMetadata . sessionError = error . errorDescription
dbManager . addItemMetadata ( updatedMetadata )
completionHandler ( nil , nil , NSFileProviderError ( . cannotSynchronize ) )
}
}
} catch let error {
NSLog ( " Could not find local path for file %@, received error: %@ " , metadata . fileNameView , error . localizedDescription )
}
2023-03-07 22:19:10 +03:00
return progress
2022-03-29 16:00:59 +03:00
}
func createItem ( basedOn itemTemplate : NSFileProviderItem , fields : NSFileProviderItemFields , contents url : URL ? , options : NSFileProviderCreateItemOptions = [ ] , request : NSFileProviderRequest , completionHandler : @ escaping ( NSFileProviderItem ? , NSFileProviderItemFields , Bool , Error ? ) -> Void ) -> Progress {
// TODO: a n e w i t e m w a s c r e a t e d o n d i s k , p r o c e s s t h e i t e m ' s c r e a t i o n
2023-02-20 15:42:28 +03:00
2023-02-21 16:59:10 +03:00
NSLog ( " Received create item request for item with identifier: %@ and filename: %@ " , itemTemplate . itemIdentifier . rawValue , itemTemplate . filename )
2023-03-07 15:59:18 +03:00
guard itemTemplate . contentType != . symbolicLink else {
NSLog ( " Cannot create item, symbolic links not supported. " )
completionHandler ( itemTemplate , NSFileProviderItemFields ( ) , false , NSError ( domain : NSCocoaErrorDomain , code : NSFeatureUnsupportedError , userInfo : [ : ] ) )
return Progress ( )
}
2023-02-21 16:59:10 +03:00
guard let ncAccount = ncAccount else {
NSLog ( " Not creating item: %@ as account not set up yet " , itemTemplate . itemIdentifier . rawValue )
completionHandler ( itemTemplate , NSFileProviderItemFields ( ) , false , NSFileProviderError ( . notAuthenticated ) )
return Progress ( )
}
let dbManager = NextcloudFilesDatabaseManager . shared
let parentItemIdentifier = itemTemplate . parentItemIdentifier
let itemTemplateIsFolder = itemTemplate . contentType = = . folder ||
itemTemplate . contentType = = . directory
if options . contains ( . mayAlreadyExist ) {
// TODO: T h i s n e e d s t o b e p r o p e r l y h a n d l e d w i t h a c h e c k i n t h e d b
NSLog ( " Not creating item: %@ as it may already exist " , itemTemplate . itemIdentifier . rawValue )
completionHandler ( itemTemplate , NSFileProviderItemFields ( ) , false , NSFileProviderError ( . noSuchItem ) )
return Progress ( )
}
var parentItemMetadata : NextcloudDirectoryMetadataTable ?
if parentItemIdentifier = = . rootContainer {
let rootMetadata = NextcloudDirectoryMetadataTable ( )
rootMetadata . account = ncAccount . ncKitAccount
rootMetadata . ocId = NSFileProviderItemIdentifier . rootContainer . rawValue
rootMetadata . serverUrl = ncAccount . davFilesUrl
parentItemMetadata = rootMetadata
} else {
parentItemMetadata = dbManager . directoryMetadata ( ocId : parentItemIdentifier . rawValue )
}
guard let parentItemMetadata = parentItemMetadata else {
NSLog ( " Not creating item: %@, could not find metadata for parentItemIdentifier %@ " , itemTemplate . itemIdentifier . rawValue , parentItemIdentifier . rawValue )
completionHandler ( itemTemplate , NSFileProviderItemFields ( ) , false , NSFileProviderError ( . noSuchItem ) )
return Progress ( )
}
let fileNameLocalPath = url ? . path ? ? " "
let newServerUrlFileName = parentItemMetadata . serverUrl + " / " + itemTemplate . filename
NSLog ( " About to upload item with identifier: %@ of type: %@ (is folder: %@) and filename: %@ to server url: %@ with contents located at: %@ " , itemTemplate . itemIdentifier . rawValue , itemTemplate . contentType ? . identifier ? ? " UNKNOWN " , itemTemplateIsFolder ? " yes " : " no " , itemTemplate . filename , newServerUrlFileName , fileNameLocalPath )
if itemTemplateIsFolder {
self . ncKit . createFolder ( serverUrlFileName : newServerUrlFileName ) { account , ocId , _ , error in
guard error = = . success else {
NSLog ( " Could not create new folder with name: %@, received error: %@ " , itemTemplate . filename , error . errorDescription )
completionHandler ( itemTemplate , [ ] , false , NSFileProviderError ( . serverUnreachable ) )
return
}
self . ncKit . readFileOrFolder ( serverUrlFileName : newServerUrlFileName , depth : " 0 " , showHiddenFiles : true ) { account , files , _ , error in
guard error = = . success else {
NSLog ( " Could not read new folder with name: %@, received error: %@ " , itemTemplate . filename , error . errorDescription )
return
}
DispatchQueue . global ( ) . async {
2023-03-08 02:21:23 +03:00
dbManager . convertNKFilesFromDirectoryReadToItemMetadatas ( files , account : account ) { directoryMetadata , childDirectoriesMetadata , metadatas in
2023-02-21 16:59:10 +03:00
let newDirectoryMetadata = dbManager . directoryMetadataFromItemMetadata ( directoryItemMetadata : directoryMetadata )
dbManager . addDirectoryMetadata ( newDirectoryMetadata )
dbManager . addItemMetadata ( directoryMetadata )
let fpItem = FileProviderItem ( metadata : directoryMetadata , parentItemIdentifier : parentItemIdentifier , ncKit : self . ncKit )
completionHandler ( fpItem , [ ] , true , nil )
}
}
}
}
return Progress ( )
}
2023-03-07 22:19:10 +03:00
var progress = Progress ( )
2023-02-21 16:59:10 +03:00
self . ncKit . upload ( serverUrlFileName : newServerUrlFileName ,
fileNameLocalPath : fileNameLocalPath ,
requestHandler : { _ in
} , taskHandler : { task in
self . outstandingSessionTasks [ newServerUrlFileName ] = task
NSFileProviderManager ( for : self . domain ) ? . register ( task , forItemWithIdentifier : itemTemplate . itemIdentifier , completionHandler : { _ in } )
2023-03-07 22:19:10 +03:00
} , progressHandler : { uploadProgress in
uploadProgress . copyCurrentStateToProgress ( progress )
2023-02-21 16:59:10 +03:00
} ) { account , ocId , etag , date , size , _ , _ , error in
self . outstandingSessionTasks . removeValue ( forKey : newServerUrlFileName )
guard error = = . success , let ocId = ocId /* , s i z e = = i t e m T e m p l a t e . d o c u m e n t S i z e a s ! I n t 6 4 */ else {
NSLog ( " Could not upload item with filename: %@, received error: %@ " , itemTemplate . filename , error . errorDescription )
completionHandler ( itemTemplate , [ ] , false , NSFileProviderError ( . cannotSynchronize ) )
return
}
NSLog ( " Successfully uploaded item with identifier: %@ and filename: %@ " , ocId , itemTemplate . filename )
let newMetadata = NextcloudItemMetadataTable ( )
newMetadata . date = ( date ? ? NSDate ( ) ) as Date
newMetadata . etag = etag ? ? " "
newMetadata . account = account
newMetadata . fileName = itemTemplate . filename
newMetadata . fileNameView = itemTemplate . filename
newMetadata . ocId = ocId
newMetadata . size = size
newMetadata . contentType = itemTemplate . contentType ? . preferredMIMEType ? ? " "
newMetadata . directory = itemTemplateIsFolder
newMetadata . serverUrl = parentItemMetadata . serverUrl
newMetadata . session = " "
newMetadata . sessionError = " "
newMetadata . sessionTaskIdentifier = 0
newMetadata . status = NextcloudItemMetadataTable . Status . normal . rawValue
dbManager . addLocalFileMetadataFromItemMetadata ( newMetadata )
dbManager . addItemMetadata ( newMetadata )
let fpItem = FileProviderItem ( metadata : newMetadata , parentItemIdentifier : parentItemIdentifier , ncKit : self . ncKit )
completionHandler ( fpItem , [ ] , false , nil )
}
2023-03-07 22:19:10 +03:00
return progress
2022-03-29 16:00:59 +03:00
}
func modifyItem ( _ item : NSFileProviderItem , baseVersion version : NSFileProviderItemVersion , changedFields : NSFileProviderItemFields , contents newContents : URL ? , options : NSFileProviderModifyItemOptions = [ ] , request : NSFileProviderRequest , completionHandler : @ escaping ( NSFileProviderItem ? , NSFileProviderItemFields , Bool , Error ? ) -> Void ) -> Progress {
2023-03-06 21:11:34 +03:00
// A n i t e m w a s m o d i f i e d o n d i s k , p r o c e s s t h e i t e m ' s m o d i f i c a t i o n
// TODO: H a n d l e f i n d e r t h i n g s l i k e t a g s , o t h e r p o s s i b l e i t e m c h a n g e d f i e l d s
2023-02-27 19:31:35 +03:00
NSLog ( " Received modify item request for item with identifier: %@ and filename: %@ " , item . itemIdentifier . rawValue , item . filename )
guard let ncAccount = ncAccount else {
NSLog ( " Not modifying item: %@ as account not set up yet " , item . itemIdentifier . rawValue )
2023-03-06 20:18:46 +03:00
completionHandler ( item , [ ] , false , NSFileProviderError ( . notAuthenticated ) )
2023-02-27 19:31:35 +03:00
return Progress ( )
}
let dbManager = NextcloudFilesDatabaseManager . shared
let parentItemIdentifier = item . parentItemIdentifier
let itemTemplateIsFolder = item . contentType = = . folder ||
item . contentType = = . directory
if options . contains ( . mayAlreadyExist ) {
// TODO: T h i s n e e d s t o b e p r o p e r l y h a n d l e d w i t h a c h e c k i n t h e d b
NSLog ( " Modification for item: %@ may already exist " , item . itemIdentifier . rawValue )
}
var parentItemMetadata : NextcloudDirectoryMetadataTable ?
if parentItemIdentifier = = . rootContainer {
let rootMetadata = NextcloudDirectoryMetadataTable ( )
rootMetadata . account = ncAccount . ncKitAccount
rootMetadata . ocId = NSFileProviderItemIdentifier . rootContainer . rawValue
rootMetadata . serverUrl = ncAccount . davFilesUrl
parentItemMetadata = rootMetadata
} else {
parentItemMetadata = dbManager . directoryMetadata ( ocId : parentItemIdentifier . rawValue )
}
guard let parentItemMetadata = parentItemMetadata else {
NSLog ( " Not modifying item: %@, could not find metadata for parentItemIdentifier %@ " , item . itemIdentifier . rawValue , parentItemIdentifier . rawValue )
2023-03-06 20:18:46 +03:00
completionHandler ( item , [ ] , false , NSFileProviderError ( . noSuchItem ) )
2023-02-27 19:31:35 +03:00
return Progress ( )
}
let fileNameLocalPath = newContents ? . path ? ? " "
let newServerUrlFileName = parentItemMetadata . serverUrl + " / " + item . filename
NSLog ( " About to upload item with identifier: %@ of type: %@ (is folder: %@) and filename: %@ to server url: %@ with contents located at: %@ " , item . itemIdentifier . rawValue , item . contentType ? . identifier ? ? " UNKNOWN " , itemTemplateIsFolder ? " yes " : " no " , item . filename , newServerUrlFileName , fileNameLocalPath )
2023-03-06 20:18:46 +03:00
var modifiedItem = item
2023-03-06 21:11:34 +03:00
if changedFields . contains ( . filename ) || changedFields . contains ( . parentItemIdentifier ) {
2023-03-11 05:41:20 +03:00
NSLog ( " Changed fields for item with filename %@ includes filename or parentitemidentifier... " , item . filename )
2023-02-28 01:04:36 +03:00
let ocId = item . itemIdentifier . rawValue
guard let metadata = dbManager . itemMetadataFromOcId ( ocId ) else {
NSLog ( " Could not acquire metadata of item with identifier: %@ " , ocId )
2023-03-06 20:18:46 +03:00
completionHandler ( item , [ ] , false , NSFileProviderError ( . noSuchItem ) )
2023-02-28 01:04:36 +03:00
return Progress ( )
}
2023-03-06 20:18:46 +03:00
var renameError : NSFileProviderError ?
2023-02-28 01:04:36 +03:00
let oldServerUrlFileName = metadata . serverUrl + " / " + metadata . fileName
2023-03-06 20:18:46 +03:00
// W e w a n t t o w a i t f o r n e t w o r k o p e r a t i o n s t o f i n i s h b e f o r e w e f i r e o f f s u b s e q u e n t n e t w o r k
// o p e r a t i o n s , o r w e m i g h t c a u s e e x p l o s i o n s ( e . g . t r y i n g t o m o d i f y i t e m s t h a t h a v e j u s t b e e n
// m o v e d e l s e w h e r e )
let dispatchGroup = DispatchGroup ( )
dispatchGroup . enter ( )
2023-02-28 01:04:36 +03:00
self . ncKit . moveFileOrFolder ( serverUrlFileNameSource : oldServerUrlFileName ,
serverUrlFileNameDestination : newServerUrlFileName ,
overwrite : false ) { account , error in
guard error = = . success else {
2023-03-06 18:18:49 +03:00
NSLog ( " Could not move file or folder with name: %@, received error: %@ " , item . filename , error . errorDescription )
2023-03-06 20:18:46 +03:00
renameError = NSFileProviderError ( . serverUnreachable )
dispatchGroup . leave ( )
2023-02-28 01:04:36 +03:00
return
}
2023-03-06 21:11:34 +03:00
// R e m e m b e r t h a t a f o l d e r m e t a d a t a ' s s e r v e r U r l i s i t s d i r e c t s e r v e r U R L , w h i l e f o r
// a n i t e m m e t a d a t a t h e s e r v e r U R L i s t h e p a r e n t f o l d e r ' s U R L
2023-03-06 18:18:49 +03:00
if itemTemplateIsFolder {
dbManager . renameDirectoryAndPropagateToChildren ( ocId : ocId , newServerUrl : newServerUrlFileName , newFileName : item . filename )
} else {
2023-03-06 21:11:34 +03:00
dbManager . renameItemMetadata ( ocId : ocId , newServerUrl : parentItemMetadata . serverUrl , newFileName : item . filename )
2023-03-06 18:18:49 +03:00
}
2023-02-28 01:04:36 +03:00
guard let newMetadata = dbManager . itemMetadataFromOcId ( ocId ) else {
NSLog ( " Could not acquire metadata of item with identifier: %@ " , ocId )
2023-03-06 20:18:46 +03:00
renameError = NSFileProviderError ( . noSuchItem )
dispatchGroup . leave ( )
2023-02-28 01:04:36 +03:00
return
}
2023-03-06 20:18:46 +03:00
modifiedItem = FileProviderItem ( metadata : newMetadata , parentItemIdentifier : parentItemIdentifier , ncKit : self . ncKit )
dispatchGroup . leave ( )
}
dispatchGroup . wait ( )
2023-02-28 01:04:36 +03:00
2023-03-06 20:18:46 +03:00
guard renameError = = nil else {
NSLog ( " Stopping rename of item with ocId %@ due to error. " , ocId )
2023-03-11 05:41:20 +03:00
completionHandler ( modifiedItem , [ ] , false , renameError )
2023-03-06 20:18:46 +03:00
return Progress ( )
2023-02-28 01:04:36 +03:00
}
2023-03-06 20:18:46 +03:00
guard ! itemTemplateIsFolder else {
NSLog ( " Only handling renaming for folders. ocId: %@ " , modifiedItem . itemIdentifier . rawValue )
completionHandler ( modifiedItem , [ ] , false , nil )
2023-03-06 18:58:36 +03:00
return Progress ( )
}
}
guard ! itemTemplateIsFolder else {
2023-03-06 18:18:49 +03:00
NSLog ( " System requested modification for folder with ocID %@ (%@) of something other than folder name. " , item . itemIdentifier . rawValue , newServerUrlFileName )
2023-03-06 20:18:46 +03:00
completionHandler ( modifiedItem , [ ] , false , nil )
2023-02-28 01:04:36 +03:00
return Progress ( )
}
2023-03-07 22:19:10 +03:00
var progress = Progress ( )
2023-03-06 18:58:36 +03:00
if changedFields . contains ( . contents ) {
2023-03-11 05:41:20 +03:00
NSLog ( " Item modification for %@ includes contents " , item . filename )
2023-03-06 18:58:36 +03:00
guard newContents != nil else {
NSLog ( " WARNING. Could not upload modified contents as was provided nil contents url. ocId: %@ " , item . itemIdentifier . rawValue )
2023-03-06 20:18:46 +03:00
completionHandler ( modifiedItem , [ ] , false , NSFileProviderError ( . noSuchItem ) )
2023-03-06 18:58:36 +03:00
return Progress ( )
2023-02-27 19:31:35 +03:00
}
2023-03-06 18:58:36 +03:00
self . ncKit . upload ( serverUrlFileName : newServerUrlFileName ,
fileNameLocalPath : fileNameLocalPath ,
requestHandler : { _ in
} , taskHandler : { task in
self . outstandingSessionTasks [ newServerUrlFileName ] = task
NSFileProviderManager ( for : self . domain ) ? . register ( task , forItemWithIdentifier : item . itemIdentifier , completionHandler : { _ in } )
2023-03-07 22:19:10 +03:00
} , progressHandler : { uploadProgress in
uploadProgress . copyCurrentStateToProgress ( progress )
2023-03-06 18:58:36 +03:00
} ) { account , ocId , etag , date , size , _ , _ , error in
self . outstandingSessionTasks . removeValue ( forKey : newServerUrlFileName )
2023-02-27 19:31:35 +03:00
2023-03-06 18:58:36 +03:00
guard error = = . success , let ocId = ocId /* , s i z e = = i t e m T e m p l a t e . d o c u m e n t S i z e a s ! I n t 6 4 */ else {
NSLog ( " Could not upload item with filename: %@, received error: %@ " , item . filename , error . errorDescription )
2023-03-11 05:41:20 +03:00
completionHandler ( modifiedItem , [ ] , false , NSFileProviderError ( . cannotSynchronize ) )
2023-03-06 18:58:36 +03:00
return
}
2023-02-27 19:31:35 +03:00
2023-03-06 18:58:36 +03:00
NSLog ( " Successfully uploaded item with identifier: %@ and filename: %@ " , ocId , item . filename )
let newMetadata = NextcloudItemMetadataTable ( )
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 = parentItemMetadata . serverUrl
newMetadata . session = " "
newMetadata . sessionError = " "
newMetadata . sessionTaskIdentifier = 0
newMetadata . status = NextcloudItemMetadataTable . Status . normal . rawValue
dbManager . addLocalFileMetadataFromItemMetadata ( newMetadata )
dbManager . addItemMetadata ( newMetadata )
2023-02-27 19:31:35 +03:00
2023-03-06 20:18:46 +03:00
modifiedItem = FileProviderItem ( metadata : newMetadata , parentItemIdentifier : parentItemIdentifier , ncKit : self . ncKit )
completionHandler ( modifiedItem , [ ] , false , nil )
2023-03-06 18:58:36 +03:00
}
2023-03-11 05:41:20 +03:00
} else {
NSLog ( " Nothing more to do with %@, modifications complete " , item . filename )
completionHandler ( modifiedItem , [ ] , false , nil )
2023-02-27 19:31:35 +03:00
}
2023-03-07 22:19:10 +03:00
return progress
2022-03-29 16:00:59 +03:00
}
func deleteItem ( identifier : NSFileProviderItemIdentifier , baseVersion version : NSFileProviderItemVersion , options : NSFileProviderDeleteItemOptions = [ ] , request : NSFileProviderRequest , completionHandler : @ escaping ( Error ? ) -> Void ) -> Progress {
// TODO: a n i t e m w a s d e l e t e d o n d i s k , p r o c e s s t h e i t e m ' s d e l e t i o n
2023-03-07 03:15:09 +03:00
NSLog ( " Received delete item request for item with identifier: %@ " , identifier . rawValue )
guard ncAccount != nil else {
NSLog ( " Not deleting item: %@ as account not set up yet " , identifier . rawValue )
completionHandler ( NSFileProviderError ( . notAuthenticated ) )
return Progress ( )
}
let dbManager = NextcloudFilesDatabaseManager . shared
let ocId = identifier . rawValue
guard let itemMetadata = dbManager . itemMetadataFromOcId ( ocId ) else {
completionHandler ( NSFileProviderError ( . noSuchItem ) )
return Progress ( )
}
let serverFileNameUrl = itemMetadata . serverUrl + " / " + itemMetadata . fileName
guard serverFileNameUrl != " " else {
completionHandler ( NSFileProviderError ( . noSuchItem ) )
return Progress ( )
}
self . ncKit . deleteFileOrFolder ( serverUrlFileName : serverFileNameUrl ) { account , error in
guard error = = . success else {
NSLog ( " Could not delete item with ocId %@ and fileName %@, received error: %@ " , error . error . localizedDescription )
completionHandler ( NSFileProviderError ( . serverUnreachable ) )
return
}
NSLog ( " Successfully delete item with identifier: %@ and filename: %@ " , ocId , serverFileNameUrl )
let serverUrl = itemMetadata . serverUrl
2023-03-09 18:47:27 +03:00
if itemMetadata . directory {
dbManager . deleteDirectoryAndSubdirectoriesMetadata ( ocId : ocId )
2023-03-07 03:15:09 +03:00
}
if dbManager . localFileMetadataFromOcId ( ocId ) != nil {
dbManager . deleteLocalFileMetadata ( ocId : ocId )
}
completionHandler ( nil )
}
2022-03-29 16:00:59 +03:00
return Progress ( )
}
func enumerator ( for containerItemIdentifier : NSFileProviderItemIdentifier , request : NSFileProviderRequest ) throws -> NSFileProviderEnumerator {
2023-01-27 04:26:27 +03:00
guard let ncAccount = ncAccount else {
2023-03-09 21:00:35 +03:00
NSLog ( " Not providing enumerator for container with identifier %@ yet as account not set up " , containerItemIdentifier . rawValue )
2023-01-27 04:26:27 +03:00
throw NSFileProviderError ( . notAuthenticated )
}
2023-02-02 22:13:35 +03:00
return FileProviderEnumerator ( enumeratedItemIdentifier : containerItemIdentifier , ncAccount : ncAccount , ncKit : ncKit )
2022-03-29 16:00:59 +03:00
}
2022-12-29 02:21:33 +03:00
// MARK: N e x t c l o u d d e s k t o p c l i e n t c o m m u n i c a t i o n
2022-12-30 21:52:51 +03:00
func sendFileProviderDomainIdentifier ( ) {
2022-12-29 02:21:33 +03:00
let command = " FILE_PROVIDER_DOMAIN_IDENTIFIER_REQUEST_REPLY "
let argument = domain . identifier . rawValue
let message = command + " : " + argument + " \n "
2023-01-04 23:18:21 +03:00
socketClient ? . sendMessage ( message )
2022-12-29 02:21:33 +03:00
}
2022-12-30 21:52:51 +03:00
2023-01-30 22:32:35 +03:00
private func signalEnumeratorAfterAccountSetup ( ) {
guard let fpManager = NSFileProviderManager ( for : domain ) else {
NSLog ( " Could not get file provider manager for domain %@, cannot notify after account setup " , domain )
return
}
assert ( ncAccount != nil )
2023-03-09 21:00:35 +03:00
fpManager . signalErrorResolved ( NSFileProviderError ( . notAuthenticated ) ) { error in
if error != nil {
NSLog ( " Error resolving not authenticated, received error: %@ " , error ! . localizedDescription )
}
}
guard NextcloudFilesDatabaseManager . shared . anyItemMetadatasForAccount ( ncAccount ! . ncKitAccount ) else {
// W h e n w e h a v e n o t h i n g r e g i s t e r e d , f o r c e f u l l r e f r e s h .
2023-02-01 20:20:44 +03:00
// T h i s r e f r e s h e s t h e e n t i r e s t r u c t u r e o f t h e F i l e P r o v i d e r a n d c a l l s
// e n u m e r a t e I t e m s r a t h e r t h a n e n u m e r a t e C h a n g e s i n t h e e n u m e r a t o r
2023-01-30 22:32:35 +03:00
NSLog ( " Signalling manager for user %@ at server %@ to reimport everything " , ncAccount ! . username , ncAccount ! . serverUrl )
2023-03-09 21:00:35 +03:00
fpManager . reimportItems ( below : . rootContainer ) { error in
if error != nil {
NSLog ( " Error reimporting everything, received error: %@ " , error ! . localizedDescription )
}
}
2023-01-30 22:32:35 +03:00
return
}
2023-03-09 21:00:35 +03:00
NSLog ( " Signalling enumerators for user %@ at server %@ " , ncAccount ! . username , ncAccount ! . serverUrl )
2023-03-10 20:37:00 +03:00
fpManager . signalEnumerator ( for : . workingSet ) { error in
if error != nil {
NSLog ( " Error signalling enumerator for working set, received error: %@ " , error ! . localizedDescription )
2023-01-30 22:32:35 +03:00
}
}
}
2023-01-27 01:01:31 +03:00
func setupDomainAccount ( user : String , serverUrl : String , password : String ) {
ncAccount = NextcloudAccount ( user : user , serverUrl : serverUrl , password : password )
2023-02-02 22:13:35 +03:00
ncKit . setup ( user : ncAccount ! . username ,
userId : ncAccount ! . username ,
password : ncAccount ! . password ,
urlBase : ncAccount ! . serverUrl ,
userAgent : " Nextcloud-macOS/FileProviderExt " ,
nextcloudVersion : 25 ,
delegate : nil ) // TODO: a d d d e l e g a t e m e t h o d s f o r s e l f
2023-01-28 19:07:13 +03:00
2023-01-27 04:26:27 +03:00
NSLog ( " Nextcloud account set up in File Provider extension for user: %@ at server: %@ " , user , serverUrl )
2023-03-09 21:00:35 +03:00
signalEnumeratorAfterAccountSetup ( )
2022-12-30 21:52:51 +03:00
}
2022-03-29 16:00:59 +03:00
}