Compare commits

...

4 Commits

Author SHA1 Message Date
Amir Abbas Mousavian 4f56e20441 createFile definition improved to resolve #10 2016-12-01 12:12:53 +03:30
Amir Abbas Mousavian 9dda618b73 Revert "createFile definition improved"
This reverts commit da60c05188.
2016-12-01 12:10:14 +03:30
Amir Abbas Mousavian da60c05188 createFile definition improved 2016-12-01 11:54:03 +03:30
Amir Abbas Mousavian 4366855d54 Added NSCopying conformance, SMB headers gardening 2016-11-28 19:40:09 +03:30
18 changed files with 252 additions and 160 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.7.0"
s.version = "0.7.1"
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and macOS."
# This description is used to generate tags and improve search results.
+2 -19
View File
@@ -11,9 +11,6 @@
7902C0871D61B67100564440 /* RemoteSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* RemoteSession.swift */; };
7902C0881D61B67100564440 /* RemoteSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7902C0851D61B56D00564440 /* RemoteSession.swift */; };
791950F51DE58A5400B4426E /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 791950F41DE58A5400B4426E /* libxml2.tbd */; };
7924B1931D89DAE000589DB7 /* AEXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7924B18C1D89DAE000589DB7 /* AEXML.h */; };
7924B1941D89DAE000589DB7 /* AEXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7924B18C1D89DAE000589DB7 /* AEXML.h */; };
7924B1951D89DAE000589DB7 /* AEXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 7924B18C1D89DAE000589DB7 /* AEXML.h */; };
7924B1961D89DAE000589DB7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18D1D89DAE000589DB7 /* Document.swift */; };
7924B1971D89DAE000589DB7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18D1D89DAE000589DB7 /* Document.swift */; };
7924B1981D89DAE000589DB7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18D1D89DAE000589DB7 /* Document.swift */; };
@@ -23,9 +20,6 @@
7924B19C1D89DAE000589DB7 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18F1D89DAE000589DB7 /* Error.swift */; };
7924B19D1D89DAE000589DB7 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18F1D89DAE000589DB7 /* Error.swift */; };
7924B19E1D89DAE000589DB7 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B18F1D89DAE000589DB7 /* Error.swift */; };
7924B19F1D89DAE000589DB7 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7924B1901D89DAE000589DB7 /* Info.plist */; };
7924B1A01D89DAE000589DB7 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7924B1901D89DAE000589DB7 /* Info.plist */; };
7924B1A11D89DAE000589DB7 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7924B1901D89DAE000589DB7 /* Info.plist */; };
7924B1A21D89DAE000589DB7 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1911D89DAE000589DB7 /* Options.swift */; };
7924B1A31D89DAE000589DB7 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1911D89DAE000589DB7 /* Options.swift */; };
7924B1A41D89DAE000589DB7 /* Options.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7924B1911D89DAE000589DB7 /* Options.swift */; };
@@ -100,11 +94,9 @@
/* Begin PBXFileReference section */
7902C0851D61B56D00564440 /* RemoteSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteSession.swift; sourceTree = "<group>"; };
791950F41DE58A5400B4426E /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.1.sdk/usr/lib/libxml2.tbd; sourceTree = DEVELOPER_DIR; };
7924B18C1D89DAE000589DB7 /* AEXML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEXML.h; sourceTree = "<group>"; };
7924B18D1D89DAE000589DB7 /* Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = "<group>"; };
7924B18E1D89DAE000589DB7 /* Element.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Element.swift; sourceTree = "<group>"; };
7924B18F1D89DAE000589DB7 /* Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
7924B1901D89DAE000589DB7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7924B1911D89DAE000589DB7 /* Options.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Options.swift; sourceTree = "<group>"; };
7924B1921D89DAE000589DB7 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FPSStreamTask.swift; sourceTree = "<group>"; };
@@ -174,11 +166,9 @@
7924B18B1D89DAE000589DB7 /* AEXML */ = {
isa = PBXGroup;
children = (
7924B18C1D89DAE000589DB7 /* AEXML.h */,
7924B18D1D89DAE000589DB7 /* Document.swift */,
7924B18E1D89DAE000589DB7 /* Element.swift */,
7924B18F1D89DAE000589DB7 /* Error.swift */,
7924B1901D89DAE000589DB7 /* Info.plist */,
7924B1911D89DAE000589DB7 /* Options.swift */,
7924B1921D89DAE000589DB7 /* Parser.swift */,
);
@@ -261,7 +251,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1931D89DAE000589DB7 /* AEXML.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -269,7 +258,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1941D89DAE000589DB7 /* AEXML.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -277,7 +265,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1951D89DAE000589DB7 /* AEXML.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -382,7 +369,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7924B19F1D89DAE000589DB7 /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -390,7 +376,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1A01D89DAE000589DB7 /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -398,7 +383,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7924B1A11D89DAE000589DB7 /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -573,6 +557,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
BUNDLE_VERSION_STRING = 0.7.1;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -609,7 +594,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-iOS";
PRODUCT_NAME = FileProvider;
SDKROOT = iphoneos;
@@ -626,6 +610,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
BUNDLE_VERSION_STRING = 0.7.1;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -713,7 +698,6 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-OSX";
PRODUCT_NAME = FileProvider;
SDKROOT = macosx;
@@ -816,7 +800,6 @@
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.mousavian.FileProvider-tvOS";
PRODUCT_NAME = FileProvider;
SDKROOT = appletvos;
+26 -8
View File
@@ -13,7 +13,7 @@ import CoreGraphics
// Because this class uses NSURLSession, it's necessary to disable App Transport Security
// in case of using this class with unencrypted HTTP connection.
open class DropboxFileProvider: NSObject, FileProviderBasic {
open class DropboxFileProvider: NSObject, FileProviderBasicRemote {
open static let type: String = "WebDAV"
open let isPathRelative: Bool = true
open let baseURL: URL?
@@ -25,24 +25,31 @@ open class DropboxFileProvider: NSObject, FileProviderBasic {
}
open weak var delegate: FileProviderDelegate?
open let credential: URLCredential?
open private(set) var cache: URLCache?
public var useCache: Bool = false
public var validatingCache: Bool = true
fileprivate var _session: URLSession?
fileprivate var sessionDelegate: SessionDelegate?
internal var session: URLSession {
public var session: URLSession {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
let queue = OperationQueue()
//queue.underlyingQueue = dispatch_queue
_session = URLSession(configuration: URLSessionConfiguration.default, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: queue)
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: queue)
}
return _session!
}
public init? (credential: URLCredential?) {
public init? (credential: URLCredential?, cache: URLCache? = nil) {
self.baseURL = nil
dispatch_queue = DispatchQueue(label: "FileProvider.\(DropboxFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent)
//let url = baseURL.uw_absoluteString
self.credential = credential
self.cache = cache
}
deinit {
@@ -106,8 +113,9 @@ extension DropboxFileProvider: FileProviderOperations {
return doOperation(.create(path: path), completionHandler: completionHandler)
}
public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
return self.writeContents(path: path, contents: data ?? Data(), completionHandler: completionHandler)
public func create(file fileName: String, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let filePath = (path as NSString).appendingPathComponent(fileName)
return self.writeContents(path: filePath, contents: data ?? Data(), completionHandler: completionHandler)
}
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
@@ -337,4 +345,14 @@ extension DropboxFileProvider: ExtendedFileProvider {
}
}
extension DropboxFileProvider: FileProvider {}
extension DropboxFileProvider: FileProvider {
open func copy(with zone: NSZone? = nil) -> Any {
let copy = DropboxFileProvider(credential: self.credential, cache: self.cache)!
copy.currentPath = self.currentPath
copy.delegate = self.delegate
copy.fileOperationDelegate = self.fileOperationDelegate
copy.useCache = self.useCache
copy.validatingCache = self.validatingCache
return copy
}
}
+57 -3
View File
@@ -114,13 +114,68 @@ public protocol FileProviderBasic: class {
func storageProperties(completionHandler: @escaping ((_ total: Int64, _ used: Int64) -> Void))
}
public protocol FileProviderBasicRemote: FileProviderBasic {
var session: URLSession { get }
var cache: URLCache? { get }
var useCache: Bool { get set }
var validatingCache: Bool { get set }
}
internal extension FileProviderBasicRemote {
func returnCachedDate(with request: URLRequest, validatingCache: Bool, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Swift.Void) -> Bool {
guard let cache = self.cache else { return false }
if let response = cache.cachedResponse(for: request) {
var validatedCache = !validatingCache
let lastModifiedDate = (response.response as? HTTPURLResponse)?.allHeaderFields["Last-Modified"] as? String
let eTag = (response.response as? HTTPURLResponse)?.allHeaderFields["ETag"] as? String
if lastModifiedDate == nil && eTag == nil, validatingCache {
var validateRequest = request
validateRequest.httpMethod = "HEAD"
let group = DispatchGroup()
group.enter()
self.session.dataTask(with: validateRequest, completionHandler: { (_, response, e) in
if let httpResponse = response as? HTTPURLResponse {
let currentETag = httpResponse.allHeaderFields["ETag"] as? String
let currentLastModifiedDate = httpResponse.allHeaderFields["ETag"] as? String ?? "nonvalidetag"
validatedCache = (eTag != nil && currentETag == eTag)
|| (lastModifiedDate != nil && currentLastModifiedDate == lastModifiedDate)
}
group.leave()
}).resume()
_ = group.wait(timeout: DispatchTime.now() + self.session.configuration.timeoutIntervalForRequest)
}
if validatedCache {
completionHandler(response.data, response.response, nil)
return true
}
}
return false
}
func runDataTask(with request: URLRequest, operationHandle: RemoteOperationHandle? = nil, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Swift.Void) {
let useCache = self.useCache
let validatingCache = self.validatingCache
dispatch_queue.async {
if useCache {
if self.returnCachedDate(with: request, validatingCache: validatingCache, completionHandler: completionHandler) {
return
}
}
let task = self.session.dataTask(with: request, completionHandler: completionHandler)
task.taskDescription = operationHandle?.operationType.json
operationHandle?.add(task: task)
task.resume()
}
}
}
public protocol FileProviderOperations: FileProviderBasic {
var fileOperationDelegate : FileOperationDelegate? { get set }
@discardableResult
func create(folder: String, at: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func create(file: FileObject, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle?
func create(file: String, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func moveItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
@@ -151,8 +206,7 @@ public protocol FileProviderMonitor: FileProviderBasic {
func isRegisteredForNotification(path: String) -> Bool
}
public protocol FileProvider: FileProviderBasic, FileProviderOperations, FileProviderReadWrite {
public protocol FileProvider: FileProviderBasic, FileProviderOperations, FileProviderReadWrite, NSCopying {
}
extension FileProviderBasic {
+57 -49
View File
@@ -104,28 +104,30 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
@discardableResult
open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(folderName) + "/")
operation_queue.async {
do {
try self.opFileManager.createDirectory(at: self.absoluteURL(atPath).appendingPathComponent(folderName), withIntermediateDirectories: true, attributes: [:])
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/"))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/"))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return LocalOperationHandle(operationType: .create(path: (atPath as NSString).appendingPathComponent(folderName)), baseURL: self.baseURL)
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
open func create(file fileName: String, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(fileName))
operation_queue.async {
let fileURL = self.absoluteURL(atPath).appendingPathComponent(fileAttribs.name)
var attributes = [String : Any]()
let fileURL = self.absoluteURL(atPath).appendingPathComponent(fileName)
/*var attributes = [String : Any]()
if let createdDate = fileAttribs.createdDate {
attributes[FileAttributeKey.creationDate.rawValue] = createdDate as NSDate
}
@@ -134,29 +136,29 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
}
if fileAttribs.isReadOnly {
attributes[FileAttributeKey.posixPermissions.rawValue] = NSNumber(value: 365 as Int16)
}
let success = self.opFileManager.createFile(atPath: fileURL.path, contents: data, attributes: attributes)
}*/
let success = self.opFileManager.createFile(atPath: fileURL.path, contents: data, attributes: nil/*attributes*/)
if success {
do {
/*do {
try (fileURL as NSURL).setResourceValue(fileAttribs.isHidden, forKey: URLResourceKey.isHiddenKey)
} catch _ {}
} catch _ {}*/
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name)))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} else {
completionHandler?(self.throwError(atPath, code: URLError.cannotCreateFile as FoundationErrorEnum))
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name)))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return LocalOperationHandle(operationType: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name)), baseURL: self.baseURL)
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
// FIXME: progress
let opType = FileOperationType.move(source: path, destination: toPath)
operation_queue.async {
if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) {
completionHandler?(self.throwError(toPath, code: URLError.cannotMoveFile as FoundationErrorEnum))
@@ -166,21 +168,21 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
try self.opFileManager.moveItem(at: self.absoluteURL(path), to: self.absoluteURL(toPath))
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .move(source: path, destination: toPath))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .move(source: path, destination: toPath))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return LocalOperationHandle(operationType: .move(source: path, destination: toPath), baseURL: self.baseURL)
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
// FIXME: progress, for files > 100mb, monitor file by another thread, for dirs check copied items count
let opType = FileOperationType.copy(source: path, destination: toPath)
operation_queue.async {
if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) {
completionHandler?(self.throwError(toPath, code: URLError.cannotWriteToFile as FoundationErrorEnum))
@@ -190,73 +192,76 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
try self.opFileManager.copyItem(at: self.absoluteURL(path), to: self.absoluteURL(toPath))
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .copy(source: path, destination: toPath))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toPath))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return LocalOperationHandle(operationType: .copy(source: path, destination: toPath), baseURL: self.baseURL)
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.remove(path: path)
operation_queue.async {
do {
try self.opFileManager.removeItem(at: self.absoluteURL(path))
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .remove(path: path))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .remove(path: path))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return LocalOperationHandle(operationType: .remove(path: path), baseURL: self.baseURL)
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
operation_queue.async {
do {
try self.opFileManager.copyItem(at: localFile, to: self.absoluteURL(toPath))
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .copy(source: localFile.absoluteString, destination: toPath))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .copy(source: localFile.absoluteString, destination: toPath))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return LocalOperationHandle(operationType: .move(source: localFile.absoluteString, destination: toPath), baseURL: self.baseURL)
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.copy(source: path, destination: toLocalURL.absoluteString)
operation_queue.async {
do {
try self.opFileManager.copyItem(at: self.absoluteURL(path), to: toLocalURL)
completionHandler?(nil)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .copy(source: path, destination: toLocalURL.absoluteString))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
} catch let e as NSError {
completionHandler?(e)
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toLocalURL.absoluteString))
self.delegate?.fileproviderFailed(self, operation: opType)
})
}
}
return LocalOperationHandle(operationType: .move(source: path, destination: toLocalURL.absoluteString), baseURL: self.baseURL)
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
@@ -270,49 +275,43 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
@discardableResult
open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
// Unfortunatlely there is no method provided in NSFileManager to read a segment of file.
// So we have to fallback to POSIX provided methods
let opType = FileOperationType.fetch(path: path)
dispatch_queue.async {
let aPath = self.absoluteURL(path).path
guard !self.attributesOfItem(url: self.absoluteURL(path)).isDirectory && self.fileManager.fileExists(atPath: aPath) else {
completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum))
return
}
let fd_from = open(aPath, O_RDONLY)
if fd_from < 0 {
guard let handle = FileHandle(forReadingAtPath: aPath) else {
completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum))
return
}
defer { precondition(close(fd_from) >= 0) }
lseek(fd_from, offset, SEEK_SET)
var buf = [UInt8](repeating: 0, count: length)
let nread = read(fd_from, &buf, buf.count)
if nread < 0 {
completionHandler(nil, self.throwError(path, code: URLError.noPermissionsToReadFile as FoundationErrorEnum))
} else if nread == 0 {
completionHandler(nil, nil)
} else {
let data = Data(bytesNoCopy: UnsafeMutablePointer<UInt8>(&buf), count: nread, deallocator: .free)
completionHandler(data, nil)
defer {
handle.closeFile()
}
handle.seek(toFileOffset: UInt64(offset))
let data = handle.readData(ofLength: length)
completionHandler(data, nil)
}
return LocalOperationHandle(operationType: .fetch(path: path), baseURL: self.baseURL)
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@discardableResult
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.modify(path: path)
operation_queue.async {
try? data.write(to: self.absoluteURL(path), options: atomically ? [.atomic] : [])
DispatchQueue.main.async(execute: {
self.delegate?.fileproviderSucceed(self, operation: .modify(path: path))
self.delegate?.fileproviderSucceed(self, operation: opType)
})
}
return LocalOperationHandle(operationType: .modify(path: path), baseURL: self.baseURL)
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
open func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
dispatch_queue.async {
let iterator = self.fileManager.enumerator(at: self.absoluteURL(path), includingPropertiesForKeys: nil, options: recursive ? FileManager.DirectoryEnumerationOptions() : .skipsSubdirectoryDescendants) { (url, e) -> Bool in
let iterator = self.fileManager.enumerator(at: self.absoluteURL(path), includingPropertiesForKeys: nil, options: recursive ? [] : [.skipsSubdirectoryDescendants, .skipsPackageDescendants]) { (url, e) -> Bool in
completionHandler([], e)
return true
}
@@ -362,6 +361,15 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
open func isRegisteredForNotification(path: String) -> Bool {
return monitors.map( { self.relativePathOf(url: $0.url) } ).contains(path)
}
open func copy(with zone: NSZone? = nil) -> Any {
let copy = LocalFileProvider(baseURL: self.baseURL!)
copy.currentPath = self.currentPath
copy.delegate = self.delegate
copy.fileOperationDelegate = self.fileOperationDelegate
copy.isPathRelative = self.isPathRelative
return copy
}
}
public extension LocalFileProvider {
+20 -11
View File
@@ -12,12 +12,24 @@ import Foundation
// For big-endian platforms like PowerPC, there must be a huge overhaul
protocol SMBProtocolClientDelegate: class {
func receivedSMB2Response(_ header: SMB2.Header, response: SMBResponse)
func receivedResponse(client: SMB2ProtocolClient, response: SMBResponse, for: SMBRequest)
}
class SMB2ProtocolClient: FPSStreamTask {
var currentMessageID: UInt64 = 0
var sessionId: UInt64 = 0
var timeout: TimeInterval = 30
private(set) var lastMessageID: UInt64 = 0
private(set) var sessionId: UInt64 = 0
private func messageId() -> UInt64 {
defer {
lastMessageID += 1
}
return lastMessageID
}
private(set) var establishedTrees = Array<SMB2.TreeConnectResponse>()
private(set) var requestStack = [Int: SMBRequest]()
private(set) var responseStack = [Int: SMBResponse]()
weak var delegate: SMBProtocolClientDelegate?
@@ -40,7 +52,7 @@ class SMB2ProtocolClient: FPSStreamTask {
let data = createSMB2Message(header: smbHeader, message: msg)
self.writeData(data, timeout: 0, completionHandler: { (e) in
if self.sessionId == 0 {
self.readData(OfMinLength: 64, maxLength: 65536, timeout: 30, completionHandler: { (data, eof, e2) in
self.readData(OfMinLength: 64, maxLength: 65536, timeout: self.timeout, completionHandler: { (data, eof, e2) in
// TODO: set session id
completionHandler?(e2 ?? e)
})
@@ -93,11 +105,8 @@ class SMB2ProtocolClient: FPSStreamTask {
return mId
}
func messageId() -> UInt64 {
defer {
currentMessageID += 1
}
return currentMessageID
func reset() {
}
}
@@ -145,7 +154,7 @@ extension SMB2ProtocolClient {
return (header, blocks)
}
func digestSMB2Message(_ data: Data) throws -> (header: SMB2.Header, message: SMBResponse?)? {
func digestSMB2Message(_ data: Data) throws -> SMBResponse? {
guard data.count > 65 else {
throw URLError(.badServerResponse)
}
@@ -219,7 +228,7 @@ extension SMB2ProtocolClient {
return result
}
func createSMB2Message(header: SMB2.Header, message: SMBRequest) -> Data {
func createSMB2Message(header: SMB2.Header, message: SMBRequestBody) -> Data {
var result = Data(value: header)
result.append(message.data())
return result
+9 -1
View File
@@ -48,7 +48,7 @@ class SMBFileProvider: FileProvider, FileProviderMonitor {
return nil
}
open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
open func create(file fileName: String, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
NotImplemented()
return nil
}
@@ -108,6 +108,14 @@ class SMBFileProvider: FileProvider, FileProviderMonitor {
open func isRegisteredForNotification(path: String) -> Bool {
return false
}
open func copy(with zone: NSZone? = nil) -> Any {
let copy = SMBFileProvider(baseURL: self.baseURL!, credential: self.credential!, afterInitialized: { _ in })!
copy.currentPath = self.currentPath
copy.delegate = self.delegate
copy.fileOperationDelegate = self.fileOperationDelegate
return copy
}
}
// MARK: basic CIFS interactivity
+8 -6
View File
@@ -8,21 +8,21 @@
import Foundation
protocol SMBRequest {
protocol SMBRequestBody {
func data() -> Data
}
extension SMBRequest {
extension SMBRequestBody {
func data() -> Data {
return Data(value: self)
}
}
protocol SMBResponse {
protocol SMBResponseBody {
init? (data: Data)
}
extension SMBResponse {
extension SMBResponseBody {
init? (data: Data) {
if let v: Self = data.scanValue() {
self = v
@@ -32,9 +32,11 @@ extension SMBResponse {
}
}
typealias SMBRequest = (header: SMB2.Header, body: SMBRequestBody?)
typealias SMBResponse = (header: SMB2.Header, body: SMBResponseBody?)
protocol IOCtlRequestProtocol: SMBRequest {}
protocol IOCtlResponseProtocol: SMBResponse {}
protocol IOCtlRequestProtocol: SMBRequestBody {}
protocol IOCtlResponseProtocol: SMBResponseBody {}
struct SMBTime {
+6 -6
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Create
struct CreateRequest: SMBRequest {
struct CreateRequest: SMBRequestBody {
let header: CreateRequest.Header
let name: String?
let contexts: [CreateContext]
@@ -158,7 +158,7 @@ extension SMB2 {
}
}
struct CreateResponse: SMBResponse {
struct CreateResponse: SMBResponseBody {
struct Header {
let size: UInt16
fileprivate let _oplockLevel: UInt8
@@ -358,7 +358,7 @@ extension SMB2 {
// MARK: SMB2 Close
struct CloseRequest: SMBRequest {
struct CloseRequest: SMBRequestBody {
let size: UInt16
let flags: CloseFlags
fileprivate let reserved2: UInt32
@@ -374,7 +374,7 @@ extension SMB2 {
}
}
struct CloseResponse: SMBResponse {
struct CloseResponse: SMBResponseBody {
let size: UInt16
let flags: CloseFlags
fileprivate let reserved: UInt32
@@ -399,7 +399,7 @@ extension SMB2 {
// MARK: SMB2 Flush
struct FlushRequest: SMBRequest {
struct FlushRequest: SMBRequestBody {
let size: UInt16
fileprivate let reserved: UInt16
fileprivate let reserved2: UInt32
@@ -415,7 +415,7 @@ extension SMB2 {
}
}
struct FlushResponse: SMBResponse {
struct FlushResponse: SMBResponseBody {
let size: UInt16
let reserved: UInt16
+9 -9
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Read
struct ReadRequest: SMBRequest {
struct ReadRequest: SMBRequestBody {
let size: UInt16
fileprivate let padding: UInt8
let flags: ReadRequest.Flags
@@ -54,7 +54,7 @@ extension SMB2 {
}
}
struct ReadRespone: SMBResponse {
struct ReadRespone: SMBResponseBody {
struct Header {
let size: UInt16
let offset: UInt8
@@ -85,7 +85,7 @@ extension SMB2 {
// MARK: SMB2 Write
struct WriteRequest: SMBRequest {
struct WriteRequest: SMBRequestBody {
let header: WriteRequest.Header
let channelInfo: ChannelInfo?
let fileData: Data
@@ -140,7 +140,7 @@ extension SMB2 {
}
}
struct WriteResponse: SMBResponse {
struct WriteResponse: SMBResponseBody {
let size: UInt16
fileprivate let reserved: UInt16
let writtenBytes: UInt32
@@ -149,7 +149,7 @@ extension SMB2 {
fileprivate let channelInfoLength: UInt16
}
struct ChannelInfo: SMBRequest {
struct ChannelInfo: SMBRequestBody {
let offset: UInt64
let token: UInt32
let length: UInt32
@@ -157,7 +157,7 @@ extension SMB2 {
// MARK: SMB2 Lock
struct LockElement: SMBRequest {
struct LockElement: SMBRequestBody {
let offset: UInt64
let length: UInt64
let flags: LockElement.Flags
@@ -177,7 +177,7 @@ extension SMB2 {
}
}
struct LockRequest: SMBRequest {
struct LockRequest: SMBRequestBody {
let header: LockRequest.Header
let locks: [LockElement]
@@ -202,7 +202,7 @@ extension SMB2 {
}
}
struct LockResponse: SMBResponse {
struct LockResponse: SMBResponseBody {
let size: UInt16
let reserved: UInt16
@@ -214,7 +214,7 @@ extension SMB2 {
// MARK: SMB2 Cancel
struct CancelRequest: SMBRequest {
struct CancelRequest: SMBRequestBody {
let size: UInt16
let reserved: UInt16
+2 -2
View File
@@ -15,7 +15,7 @@ extension SMB2 {
* IOCtl usage is usually limited in SMB to pipe requests and duplicating file inside server
*/
struct IOCtlRequest: SMBRequest {
struct IOCtlRequest: SMBRequestBody {
let header: Header
let requestData: IOCtlRequestProtocol?
@@ -63,7 +63,7 @@ extension SMB2 {
}
}
struct IOCtlResponse: SMBResponse {
struct IOCtlResponse: SMBResponseBody {
let header: Header
let responseData: IOCtlResponseProtocol?
+2 -2
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Change Notify
struct ChangeNotifyRequest: SMBRequest {
struct ChangeNotifyRequest: SMBRequestBody {
let size: UInt16
let flags: ChangeNotifyRequest.Flags
let outputBufferLength: UInt32
@@ -75,7 +75,7 @@ extension SMB2 {
}
}
struct ChangeNotifyResponse: SMBResponse {
struct ChangeNotifyResponse: SMBResponseBody {
let notifications: [(action: FileNotifyAction, fileName: String)]
init?(data: Data) {
+4 -4
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Query Directory
struct QueryDirectoryRequest: SMBRequest {
struct QueryDirectoryRequest: SMBRequestBody {
let header: QueryDirectoryRequest.Header
let searchPattern: String?
@@ -60,7 +60,7 @@ extension SMB2 {
}
}
struct QueryDirectoryResponse: SMBResponse {
struct QueryDirectoryResponse: SMBResponseBody {
let buffer: Data
func parseAs(type: FileInformationEnum) -> [(header: SMB2FilesInformationHeader, fileName: String)] {
@@ -107,7 +107,7 @@ extension SMB2 {
// MARK: SMB2 Query Info
struct QueryInfoRequest: SMBRequest {
struct QueryInfoRequest: SMBRequestBody {
let header: Header
let buffer: Data?
@@ -184,7 +184,7 @@ extension SMB2 {
}
}
struct QueryInfoResponse: SMBResponse {
struct QueryInfoResponse: SMBResponseBody {
let buffer: Data
init?(data: Data) {
+1 -1
View File
@@ -8,7 +8,7 @@
import Foundation
protocol SMB2FilesInformationHeader: SMBResponse {
protocol SMB2FilesInformationHeader: SMBResponseBody {
var nextEntryOffset: UInt32 { get }
var fileIndex: UInt32 { get }
var fileNameLength : UInt32 { get }
+6 -6
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Negotiating
struct NegotiateRequest: SMBRequest {
struct NegotiateRequest: SMBRequestBody {
let header: NegotiateRequest.Header
let dialects: [UInt16]
let contexts: [(type: NegotiateContextType, data: Data)]
@@ -89,7 +89,7 @@ extension SMB2 {
}
}
struct NegotiateResponse: SMBResponse {
struct NegotiateResponse: SMBResponseBody {
let header: NegotiateResponse.Header
let buffer: Data?
let contexts: [(type: NegotiateContextType, data: Data)]
@@ -174,7 +174,7 @@ extension SMB2 {
// MARK: SMB2 Session Setup
struct SessionSetupRequest: SMBRequest {
struct SessionSetupRequest: SMBRequestBody {
let header: SessionSetupRequest.Header
let buffer: Data?
@@ -233,7 +233,7 @@ extension SMB2 {
}
}
struct SessionSetupResponse: SMBResponse {
struct SessionSetupResponse: SMBResponseBody {
let header: SessionSetupResponse.Header
let buffer: Data?
@@ -287,7 +287,7 @@ extension SMB2 {
// MARK: SMB2 Log off
struct LogOff: SMBRequest, SMBResponse {
struct LogOff: SMBRequestBody, SMBResponseBody {
let size: UInt16
let reserved: UInt16
@@ -299,7 +299,7 @@ extension SMB2 {
// MARK: SMB2 Echo
struct Echo: SMBRequest, SMBResponse {
struct Echo: SMBRequestBody, SMBResponseBody {
let size: UInt16
let reserved: UInt16
+2 -2
View File
@@ -10,7 +10,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Set Info
struct SetInfoRequest: SMBRequest {
struct SetInfoRequest: SMBRequestBody {
let header: Header
let buffer: Data?
@@ -32,7 +32,7 @@ extension SMB2 {
}
}
struct SetInfoResponse: SMBResponse {
struct SetInfoResponse: SMBResponseBody {
let size: UInt16
init() {
+3 -3
View File
@@ -11,7 +11,7 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Tree Connect
struct TreeConnectRequest: SMBRequest {
struct TreeConnectRequest: SMBRequestBody {
let header: TreeConnectRequest.Header
let buffer: Data?
var path: String {
@@ -66,7 +66,7 @@ extension SMB2 {
}
}
struct TreeConnectResponse: SMBResponse {
struct TreeConnectResponse: SMBResponseBody {
let size: UInt16 // = 16
fileprivate let _type: UInt8
var type: ShareType {
@@ -124,7 +124,7 @@ extension SMB2 {
// MARK: SMB2 Tree Disconnect
struct TreeDisconnect: SMBRequest, SMBResponse {
struct TreeDisconnect: SMBRequestBody, SMBResponseBody {
let size: UInt16
let reserved: UInt16
+37 -27
View File
@@ -21,35 +21,41 @@ public final class WebDavFileObject: FileObject {
// codebeat:enable[ARITY]
}
// Because this class uses NSURLSession, it's necessary to disable App Transport Security
// in case of using this class with unencrypted HTTP connection.
/// Because this class uses NSURLSession, it's necessary to disable App Transport Security
/// in case of using this class with unencrypted HTTP connection.
open class WebDAVFileProvider: NSObject, FileProviderBasic {
open class WebDAVFileProvider: NSObject, FileProviderBasicRemote {
open static let type: String = "WebDAV"
open let isPathRelative: Bool = true
open let baseURL: URL?
open var currentPath: String = ""
open var dispatch_queue: DispatchQueue {
public var dispatch_queue: DispatchQueue {
willSet {
assert(_session == nil, "It's not effective to change dispatch_queue property after session is initialized.")
}
}
open weak var delegate: FileProviderDelegate?
public weak var delegate: FileProviderDelegate?
open let credential: URLCredential?
open private(set) var cache: URLCache?
public var useCache: Bool = false
public var validatingCache: Bool = true
fileprivate var _session: URLSession?
fileprivate var sessionDelegate: SessionDelegate?
fileprivate var session: URLSession {
public var session: URLSession {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
let queue = OperationQueue()
//queue.underlyingQueue = dispatch_queue
_session = URLSession(configuration: URLSessionConfiguration.default, delegate: sessionDelegate as URLSessionDownloadDelegate?, delegateQueue: queue)
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDownloadDelegate?, delegateQueue: queue)
}
return _session!
}
public init? (baseURL: URL, credential: URLCredential?) {
public init? (baseURL: URL, credential: URLCredential?, cache: URLCache? = nil) {
if !["http", "https"].contains(baseURL.uw_scheme.lowercased()) {
return nil
}
@@ -57,6 +63,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
dispatch_queue = DispatchQueue(label: "FileProvider.\(WebDAVFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent)
//let url = baseURL.uw_absoluteString
self.credential = credential
self.cache = cache
}
deinit {
@@ -72,7 +79,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
runDataTask(with: request, operationHandle: RemoteOperationHandle(operationType: opType, tasks: []), completionHandler: { (data, response, error) in
var responseError: FileProviderWebDavError?
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
@@ -89,8 +96,6 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
}
completionHandler(fileObjects, responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
}
open func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void)) {
@@ -101,7 +106,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
runDataTask(with: request, completionHandler: { (data, response, error) in
var responseError: FileProviderWebDavError?
if let code = (response as? HTTPURLResponse)?.statusCode, code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
@@ -115,7 +120,6 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
}
completionHandler(nil, responseError ?? error)
})
task.resume()
}
open func storageProperties(completionHandler: @escaping ((_ total: Int64, _ used: Int64) -> Void)) {
@@ -131,7 +135,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop><D:quota-available-bytes/><D:quota-used-bytes/></D:prop>\n</D:propfind>".data(using: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
runDataTask(with: request, completionHandler: { (data, response, error) in
var totalSize: Int64 = -1
var usedSize: Int64 = 0
if let data = data {
@@ -142,8 +146,7 @@ open class WebDAVFileProvider: NSObject, FileProviderBasic {
}
}
completionHandler(totalSize, usedSize)
})
task.resume()
})
}
open weak var fileOperationDelegate: FileOperationDelegate?
@@ -173,8 +176,8 @@ extension WebDAVFileProvider: FileProviderOperations {
}
@discardableResult
public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.create(path: (path as NSString).appendingPathComponent(fileAttribs.name))
public func create(file fileName: String, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.create(path: (path as NSString).appendingPathComponent(fileName))
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
@@ -347,16 +350,15 @@ extension WebDAVFileProvider: FileProviderReadWrite {
} else if offset > 0 && length < 0 {
request.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
}
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
let handle = RemoteOperationHandle(operationType: opType, tasks: [])
runDataTask(with: request, operationHandle: handle, completionHandler: { (data, response, error) in
var responseError: FileProviderWebDavError?
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
completionHandler(data, responseError ?? error)
})
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(operationType: opType, tasks: [task])
return handle
}
@discardableResult
@@ -397,8 +399,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
//request.setValue("1", forHTTPHeaderField: "Depth")
request.setValue("text/xml; charset=\"utf-8\"", forHTTPHeaderField: "Content-Type")
request.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/></D:propfind>".data(using: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
runDataTask(with: request, completionHandler: { (data, response, error) in
// FIXME: paginating results
var responseError: FileProviderWebDavError?
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
@@ -420,8 +421,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
return
}
completionHandler([], responseError ?? error)
})
task.resume()
})
}
fileprivate func registerNotifcation(path: String, eventHandler: (() -> Void)) {
@@ -438,7 +438,17 @@ extension WebDAVFileProvider: FileProviderReadWrite {
// TODO: implements methods for lock mechanism
}
extension WebDAVFileProvider: FileProvider {}
extension WebDAVFileProvider: FileProvider {
open func copy(with zone: NSZone? = nil) -> Any {
let copy = WebDAVFileProvider(baseURL: self.baseURL!, credential: self.credential, cache: self.cache)!
copy.currentPath = self.currentPath
copy.delegate = self.delegate
copy.fileOperationDelegate = self.fileOperationDelegate
copy.useCache = self.useCache
copy.validatingCache = self.validatingCache
return copy
}
}
// MARK: WEBDAV XML response implementation