Compare commits

...

9 Commits

Author SHA1 Message Date
Amir Abbas ff5e13931f Fixed WebDAVProvider.contents bug, refactored FTP Error 2017-04-09 14:09:03 +04:30
Amir Abbas 75af738d2e Made SessionDelegate init public, fixed pod issue 2017-04-05 02:05:28 +04:30
Amir Abbas f54a1253e4 Throwing error when trying to upload a directory 2017-04-05 00:24:24 +04:30
Amir Abbas 1394a92662 Add Documentation 2017-04-03 21:03:48 +04:30
Amir Abbas dab171c755 Setting sessionDelgate credential to updated one. 2017-04-03 18:59:50 +04:30
Amir Abbas ea5de2e2aa Added progress for content(path:) method
- Fixed issue with colliding handlers between sessions.
- Sessions can be set.
- SessionDelegate class is now public.
2017-04-03 18:50:13 +04:30
Amir Abbas 2253cca086 Fixed deprecated URLResourceKey items 2017-04-03 12:52:27 +04:30
Amir Abbas e15a900ade Renamed URLResourceKey additions to have Key prefix 2017-04-03 12:49:35 +04:30
Amir Abbas 5c2c56c44c Fixed: Calling completion handler for upload task
- Added including (file object properties) argument to WebDAV provider (resolves #31)
2017-04-03 12:41:11 +04:30
18 changed files with 519 additions and 381 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ script:
after_success:
# Run `pod trunk push` if specified
- if [ $POD == "YES" ] && [ -n "$TRAVIS_TAG" ]; then
pod trunk push;
pod trunk push --allow-warnings;
fi
# - bash <(curl -s https://codecov.io/bash)
+2 -2
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.15.0"
s.version = "0.15.3"
s.summary = "FileManager replacement for Local and Remote (WebDAV/FTP/Dropbox/OneDrive/SMB2) files on iOS and macOS."
# This description is used to generate tags and improve search results.
@@ -58,7 +58,7 @@ Pod::Spec.new do |s|
s.author = { "Amir Abbas Mousavian" => "a.mosavian@gmail.com" }
# Or just: s.author = "Amir Abbas Mousavian"
# s.authors = { "Amir Abbas Mousavian" => "a.mosavian@gmail.com" }
s.social_media_url = "https://twitter.com/amosavian"
# s.social_media_url = "https://twitter.com/amosavian"
# ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
+11 -10
View File
@@ -116,9 +116,9 @@
79BD63BE1E2CC3C20035128C /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63BD1E2CC3C20035128C /* ImageIO.framework */; };
79BD63C01E2CC3CD0035128C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63BF1E2CC3CD0035128C /* CoreGraphics.framework */; };
79BD63C21E2CC3D30035128C /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63C11E2CC3D30035128C /* AVFoundation.framework */; };
79BD63C51E2D17880035128C /* OneDriveFileProvide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD63C31E2D17880035128C /* OneDriveFileProvide.swift */; };
79BD63C61E2D17880035128C /* OneDriveFileProvide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD63C31E2D17880035128C /* OneDriveFileProvide.swift */; };
79BD63C71E2D17880035128C /* OneDriveFileProvide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD63C31E2D17880035128C /* OneDriveFileProvide.swift */; };
79BD63C51E2D17880035128C /* OneDriveFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD63C31E2D17880035128C /* OneDriveFileProvider.swift */; };
79BD63C61E2D17880035128C /* OneDriveFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD63C31E2D17880035128C /* OneDriveFileProvider.swift */; };
79BD63C71E2D17880035128C /* OneDriveFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD63C31E2D17880035128C /* OneDriveFileProvider.swift */; };
79BD63C81E2D17880035128C /* OneDriveHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD63C41E2D17880035128C /* OneDriveHelper.swift */; };
79BD63C91E2D17880035128C /* OneDriveHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD63C41E2D17880035128C /* OneDriveHelper.swift */; };
79BD63CA1E2D17880035128C /* OneDriveHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD63C41E2D17880035128C /* OneDriveHelper.swift */; };
@@ -182,7 +182,7 @@
79BD63BD1E2CC3C20035128C /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.1.sdk/System/Library/Frameworks/ImageIO.framework; sourceTree = DEVELOPER_DIR; };
79BD63BF1E2CC3CD0035128C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.1.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
79BD63C11E2CC3D30035128C /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.1.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; };
79BD63C31E2D17880035128C /* OneDriveFileProvide.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OneDriveFileProvide.swift; sourceTree = "<group>"; };
79BD63C31E2D17880035128C /* OneDriveFileProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OneDriveFileProvider.swift; sourceTree = "<group>"; };
79BD63C41E2D17880035128C /* OneDriveHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OneDriveHelper.swift; sourceTree = "<group>"; };
79F5745A1DFDB10A00179ABF /* FileObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileObject.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -304,7 +304,7 @@
794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */,
7936BC111E880F5700A6C81C /* FTPFileProvider.swift */,
798654321E8874BC002FA550 /* FTPHelper.swift */,
79BD63C31E2D17880035128C /* OneDriveFileProvide.swift */,
79BD63C31E2D17880035128C /* OneDriveFileProvider.swift */,
79BD63C41E2D17880035128C /* OneDriveHelper.swift */,
7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */,
799396971D48C02300086753 /* SMBClient.swift */,
@@ -498,7 +498,7 @@
7936BC121E880F5700A6C81C /* FTPFileProvider.swift in Sources */,
79480FF61E3ABDD0007E7275 /* CloudFileProvider.swift in Sources */,
79F5745B1DFDB10B00179ABF /* FileObject.swift in Sources */,
79BD63C51E2D17880035128C /* OneDriveFileProvide.swift in Sources */,
79BD63C51E2D17880035128C /* OneDriveFileProvider.swift in Sources */,
7924B1991D89DAE000589DB7 /* Element.swift in Sources */,
799396C81D48C02300086753 /* SMB2IOCtl.swift in Sources */,
799396D71D48C02300086753 /* SMB2Types.swift in Sources */,
@@ -541,7 +541,7 @@
7936BC131E880F5700A6C81C /* FTPFileProvider.swift in Sources */,
79480FF71E3ABDD0007E7275 /* CloudFileProvider.swift in Sources */,
79F5745C1DFDB10B00179ABF /* FileObject.swift in Sources */,
79BD63C61E2D17880035128C /* OneDriveFileProvide.swift in Sources */,
79BD63C61E2D17880035128C /* OneDriveFileProvider.swift in Sources */,
7924B1B01D89F7DE00589DB7 /* FPSStreamTask.swift in Sources */,
7924B19A1D89DAE000589DB7 /* Element.swift in Sources */,
799396C91D48C02300086753 /* SMB2IOCtl.swift in Sources */,
@@ -584,7 +584,7 @@
7936BC141E880F5700A6C81C /* FTPFileProvider.swift in Sources */,
79480FF81E3ABDD0007E7275 /* CloudFileProvider.swift in Sources */,
79F5745D1DFDB10B00179ABF /* FileObject.swift in Sources */,
79BD63C71E2D17880035128C /* OneDriveFileProvide.swift in Sources */,
79BD63C71E2D17880035128C /* OneDriveFileProvider.swift in Sources */,
7924B1B11D89F7DF00589DB7 /* FPSStreamTask.swift in Sources */,
7924B19B1D89DAE000589DB7 /* Element.swift in Sources */,
799396CA1D48C02300086753 /* SMB2IOCtl.swift in Sources */,
@@ -621,7 +621,7 @@
799396601D48B7BF00086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.15.0;
BUNDLE_VERSION_STRING = 0.15.3;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -631,6 +631,7 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_BITCODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -651,7 +652,7 @@
799396611D48B7BF00086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.15.0;
BUNDLE_VERSION_STRING = 0.15.3;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
+3
View File
@@ -717,8 +717,11 @@ public enum UbiquitousScope: RawRepresentable {
}
}
/// Get progress of CloudFileProvider operations
open class CloudOperationHandle: OperationHandle {
/// Url of file which operation is doing on
public let baseURL: URL?
/// Type of operation
public let operationType: FileOperationType
init (operationType: FileOperationType, baseURL: URL?) {
+61 -33
View File
@@ -34,7 +34,11 @@ open class DropboxFileProvider: FileProviderBasicRemote {
}
open weak var delegate: FileProviderDelegate?
open var credential: URLCredential?
open var credential: URLCredential? {
didSet {
sessionDelegate?.credential = self.credential
}
}
open private(set) var cache: URLCache?
public var useCache: Bool
public var validatingCache: Bool
@@ -42,16 +46,34 @@ open class DropboxFileProvider: FileProviderBasicRemote {
fileprivate var _session: URLSession?
fileprivate var sessionDelegate: SessionDelegate?
public var session: URLSession {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: self.operation_queue)
get {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self)
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: self.operation_queue)
_session!.sessionDescription = UUID().uuidString
initEmptySessionHandler(_session!.sessionDescription!)
}
return _session!
}
set {
assert(newValue.delegate is SessionDelegate, "session instances should have a SessionDelegate instance as delegate.")
_session = newValue
if session.sessionDescription?.isEmpty ?? true {
_session?.sessionDescription = UUID().uuidString
}
self.sessionDelegate = newValue.delegate as? SessionDelegate
initEmptySessionHandler(_session!.sessionDescription!)
}
return _session!
}
internal var completionHandlersForTasks = [Int: SimpleCompletionHandler]()
internal var downloadCompletionHandlersForTasks = [Int: (URL) -> Void]()
internal var dataCompletionHandlersForTasks = [Int: (Data) -> Void]()
fileprivate var _longpollSession: URLSession?
internal var longpollSession: URLSession {
if _longpollSession == nil {
@@ -117,11 +139,16 @@ open class DropboxFileProvider: FileProviderBasicRemote {
}
deinit {
if let sessionuuid = _session?.sessionDescription {
removeSessionHandler(for: sessionuuid)
}
if fileProviderCancelTasksOnInvalidating {
_session?.invalidateAndCancel()
} else {
_session?.finishTasksAndInvalidate()
}
}
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
@@ -269,6 +296,14 @@ extension DropboxFileProvider: FileProviderOperations {
}
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
// check file is not a folder
guard (try? localFile.resourceValues(forKeys: [.fileResourceTypeKey]))?.fileResourceType ?? .unknown == .regular else {
dispatch_queue.async {
completionHandler?(self.throwError(localFile.path, code: URLError.fileIsDirectory))
}
return nil
}
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
@@ -283,7 +318,6 @@ extension DropboxFileProvider: FileProviderOperations {
}
let url = URL(string: "files/download", relativeTo: contentURL)!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
let requestDictionary: [String: AnyObject] = ["path": path as NSString]
let requestJson = String(jsonDictionary: requestDictionary) ?? ""
@@ -323,7 +357,6 @@ extension DropboxFileProvider: FileProviderReadWrite {
let opType = FileOperationType.fetch(path: path)
let url = URL(string: "files/download", relativeTo: contentURL)!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
if length > 0 {
request.setValue("bytes=\(offset)-\(offset + Int64(length) - 1)", forHTTPHeaderField: "Range")
@@ -332,14 +365,25 @@ extension DropboxFileProvider: FileProviderReadWrite {
}
let requestDictionary: [String: AnyObject] = ["path": correctPath(path)! as NSString]
request.setValue(String(jsonDictionary: requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var serverError: FileProviderDropboxError?
if let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: httpResponse.statusCode) {
serverError = FileProviderDropboxError(code: code, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8))
let task = session.downloadTask(with: request)
completionHandlersForTasks[task.taskIdentifier] = { error in
completionHandler(nil, error)
}
downloadCompletionHandlersForTasks[task.taskIdentifier] = { tempURL in
guard let httpResponse = task.response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
let code = FileProviderHTTPErrorCode(rawValue: (task.response as? HTTPURLResponse)?.statusCode ?? -1)
let errorData : Data? = nil //Data(contentsOf:cacheURL) // TODO: Figure out how to get error response data for the error description
let serverError : FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: errorData ?? Data(), encoding: .utf8)) : nil
completionHandler(nil, serverError)
return
}
let filedata = serverError ?? error == nil ? data : nil
completionHandler(filedata, serverError ?? error)
})
do {
let data = try Data(contentsOf: tempURL)
completionHandler(data, nil)
} catch let e {
completionHandler(nil, e)
}
}
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(operationType: opType, tasks: [task])
@@ -372,22 +416,6 @@ extension DropboxFileProvider: FileProviderReadWrite {
}
extension DropboxFileProvider {
/// *OBSOLETED:* Use `publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?))` function instead.
@available(*, obsoleted: 1.0, renamed: "publicLink(to:completionHandler:)", message: "Use publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?)) function instead.")
open func temporaryLink(to path: String, completionHandler: @escaping ((_ link: URL?, _ attribute: DropboxFileObject?, _ error: Error?) -> Void)) {
self.publicLink(to: path) { (url, file, _, error) in
completionHandler(url, file, error)
}
}
/// *OBSOLETED:* Use `publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?))` function instead.
@available(*, obsoleted: 1.0, renamed: "publicLink(to:completionHandler:)", message: "Use publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?)) function instead.")
open func temporaryLink(to path: String, completionHandler: @escaping ((_ link: URL?, _ attribute: DropboxFileObject?, _ expiration: Date?, _ error: Error?) -> Void)) {
self.publicLink(to: path) { (url, file, expiration, error) in
completionHandler(url, file, expiration, error)
}
}
/**
Genrates a public url to a file to be shared with other users and can be downloaded without authentication.
+18 -44
View File
@@ -42,10 +42,10 @@ public final class DropboxFileObject: FileObject {
/// The time contents of file has been modified on server, returns nil if not set
open internal(set) var serverTime: Date? {
get {
return allValues[.serverDate] as? Date
return allValues[.serverDateKey] as? Date
}
set {
allValues[.serverDate] = newValue
allValues[.serverDateKey] = newValue
}
}
@@ -121,42 +121,8 @@ internal extension DropboxFileProvider {
task.resume()
}
func upload_simple(_ targetPath: String, data: Data, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
if data.count > 150 * 1024 * 1024 {
let error = FileProviderDropboxError(code: .payloadTooLarge, path: targetPath, errorDescription: nil)
completionHandler?(error)
self.delegateNotify(.create(path: targetPath), error: error)
return nil
}
var requestDictionary = [String: AnyObject]()
let url: URL
url = URL(string: "files/upload", relativeTo: contentURL)!
requestDictionary["path"] = correctPath(targetPath) as NSString?
requestDictionary["mode"] = (overwrite ? "overwrite" : "add") as NSString
requestDictionary["client_modified"] = modifiedDate.rfc3339utc() as NSString
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
request.setValue(String(jsonDictionary: requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
request.httpBody = data
let task = session.uploadTask(with: request, from: data)
completionHandlersForTasks[task.taskIdentifier] = completionHandler
dataCompletionHandlersForTasks[task.taskIdentifier] = { [weak self] data in
var responseError: FileProviderDropboxError?
if let code = (task.response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: String(data: data, encoding: .utf8))
}
completionHandler?(responseError)
self?.delegateNotify(.create(path: targetPath), error: responseError)
}
task.taskDescription = operation.json
task.resume()
return RemoteOperationHandle(operationType: operation, tasks: [task])
}
func upload_simple(_ targetPath: String, localFile: URL, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let size = (try? localFile.resourceValues(forKeys: [.fileSizeKey]))?.fileSize ?? -1
func upload_simple(_ targetPath: String, data: Data? = nil, localFile: URL? = nil, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let size = data?.count ?? Int((try? localFile?.resourceValues(forKeys: [.fileSizeKey]))??.fileSize ?? -1)
if size > 150 * 1024 * 1024 {
let error = FileProviderDropboxError(code: .payloadTooLarge, path: targetPath, errorDescription: nil)
completionHandler?(error)
@@ -174,15 +140,23 @@ internal extension DropboxFileProvider {
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
request.setValue(String(jsonDictionary: requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.uploadTask(with: request, fromFile: localFile)
completionHandlersForTasks[task.taskIdentifier] = completionHandler
dataCompletionHandlersForTasks[task.taskIdentifier] = { [weak self] data in
let task: URLSessionUploadTask
if let data = data {
task = session.uploadTask(with: request, from: data)
} else if let localFile = localFile {
task = session.uploadTask(with: request, fromFile: localFile)
} else {
return nil
}
completionHandlersForTasks[task.taskIdentifier] = { [weak self] error in
var responseError: FileProviderDropboxError?
if let code = (task.response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: String(data: data, encoding: .utf8))
// We can't fetch server result from delegate!
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: nil)
}
completionHandler?(responseError)
self?.delegateNotify(.create(path: targetPath), error: responseError)
completionHandler?(responseError ?? error)
self?.delegateNotify(.create(path: targetPath), error: responseError ?? error)
}
task.taskDescription = operation.json
task.resume()
+68 -74
View File
@@ -25,7 +25,11 @@ open class FTPFileProvider: FileProviderBasicRemote {
}
open weak var delegate: FileProviderDelegate?
open var credential: URLCredential?
open var credential: URLCredential? {
didSet {
sessionDelegate?.credential = self.credential
}
}
open private(set) var cache: URLCache?
public var useCache: Bool
public var validatingCache: Bool
@@ -41,35 +45,28 @@ open class FTPFileProvider: FileProviderBasicRemote {
fileprivate var _session: URLSession?
internal var sessionDelegate: SessionDelegate?
public var session: URLSession {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: self.operation_queue)
sessionDelegate?.didReceivedData = { [weak self] (session: URLSession, downloadTask: URLSessionDownloadTask, bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) -> Void in
guard let `self` = self else { return }
guard let opDic = downloadTask.taskDescription?.deserializeJSON(),
let opType = FileOperationType(json: opDic) else { return }
DispatchQueue.main.async {
let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
self.delegate?.fileproviderProgress(self, operation: opType, progress: Float(progress))
}
}
sessionDelegate?.didSendDataHandler = { [weak self] (session: Foundation.URLSession, task: URLSessionTask, bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) -> Void in
guard let `self` = self else { return }
guard let opDic = task.taskDescription?.deserializeJSON(),
let opType = FileOperationType(json: opDic) else { return }
DispatchQueue.main.async {
let progress = Double(totalBytesSent) / Double(totalBytesExpectedToSend)
self.delegate?.fileproviderProgress(self, operation: opType, progress: Float(progress))
}
get {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self)
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: self.operation_queue)
_session?.sessionDescription = UUID().uuidString
initEmptySessionHandler(_session!.sessionDescription!)
}
return _session!
}
set {
assert(newValue.delegate is SessionDelegate, "session instances should have a SessionDelegate instance as delegate.")
_session = newValue
if session.sessionDescription?.isEmpty ?? true {
_session?.sessionDescription = UUID().uuidString
}
self.sessionDelegate = newValue.delegate as? SessionDelegate
initEmptySessionHandler(_session!.sessionDescription!)
}
return _session!
}
/**
@@ -127,10 +124,15 @@ open class FTPFileProvider: FileProviderBasicRemote {
copy.fileOperationDelegate = self.fileOperationDelegate
copy.useCache = self.useCache
copy.validatingCache = self.validatingCache
copy.useAppleImplementation = self.useAppleImplementation
return copy
}
deinit {
if let sessionuuid = _session?.sessionDescription {
removeSessionHandler(for: sessionuuid)
}
if fileProviderCancelTasksOnInvalidating {
_session?.invalidateAndCancel()
} else {
@@ -229,9 +231,8 @@ open class FTPFileProvider: FileProviderBasicRemote {
}
guard let response = response, response.hasPrefix("250") || (response.hasPrefix("50") && rfc3659enabled) else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
self.dispatch_queue.async {
completionHandler(nil, error)
completionHandler(nil, self.throwError(path, code: URLError.badServerResponse))
}
return
}
@@ -242,9 +243,8 @@ open class FTPFileProvider: FileProviderBasicRemote {
let lines = response.components(separatedBy: "\n").flatMap { $0.isEmpty ? nil : $0.trimmingCharacters(in: .whitespacesAndNewlines) }
guard lines.count > 2 else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
self.dispatch_queue.async {
completionHandler(nil, error)
completionHandler(nil, self.throwError(path, code: URLError.badServerResponse))
}
return
}
@@ -346,10 +346,9 @@ extension FTPFileProvider: FileProviderOperations {
}
guard let response = response else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
self.delegateNotify(opType, error: self.throwError(sourcePath, code: URLError.badServerResponse))
}
return
}
@@ -381,9 +380,7 @@ extension FTPFileProvider: FileProviderOperations {
default:
errorCode = URLError.cannotOpenFile
}
let escapedPath = sourcePath.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? sourcePath
let url = NSURL(string: escapedPath, relativeTo: self.baseURL) ?? self.baseURL! as NSURL
let error = NSError(domain: URLError.errorDomain, code: errorCode.rawValue, userInfo: [NSURLErrorFailingURLErrorKey: url])
let error = self.throwError(sourcePath, code: errorCode)
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
@@ -441,7 +438,7 @@ extension FTPFileProvider: FileProviderOperations {
}
guard let response = response else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
let error = self.throwError(sourcePath, code: URLError.badServerResponse)
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
@@ -454,9 +451,7 @@ extension FTPFileProvider: FileProviderOperations {
return
}
let escapedPath = sourcePath.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? sourcePath
let url = NSURL(string: escapedPath, relativeTo: self.baseURL) ?? self.baseURL! as NSURL
let error = NSError(domain: URLError.errorDomain, code: URLError.cannotRemoveFile.rawValue, userInfo: [NSURLErrorFailingURLErrorKey: url])
let error = self.throwError(sourcePath, code: URLError.cannotRemoveFile)
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
@@ -467,6 +462,14 @@ extension FTPFileProvider: FileProviderOperations {
}
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
// check file is not a folder
guard (try? localFile.resourceValues(forKeys: [.fileResourceTypeKey]))?.fileResourceType ?? .unknown == .regular else {
dispatch_queue.async {
completionHandler?(self.throwError(localFile.path, code: URLError.fileIsDirectory))
}
return nil
}
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
@@ -485,6 +488,7 @@ extension FTPFileProvider: FileProviderOperations {
self.ftpStore(task, filePath: self.ftpPath(toPath), fromData: nil, fromFile: localFile, onTask: {
operation.add(task: $0)
$0.taskDescription = opType.json
}, completionHandler: { (error) in
self.ftpQuit(task)
self.dispatch_queue.async {
@@ -505,28 +509,18 @@ extension FTPFileProvider: FileProviderOperations {
let operation = RemoteOperationHandle(operationType: opType, tasks: [])
if self.useAppleImplementation {
let task = session.downloadTask(with: url(of: path)) { (tempDest, response, error) in
if let error = error {
self.dispatch_queue.async {
completionHandler?(error)
}
return
}
if let tempDest = tempDest {
do {
try FileManager.default.moveItem(at: tempDest, to: destURL)
self.dispatch_queue.async {
completionHandler?(nil)
}
} catch let error {
self.dispatch_queue.async {
completionHandler?(error)
}
}
let task = session.downloadTask(with: url(of: path))
completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = completionHandler
downloadCompletionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { tempURL in
do {
try FileManager.default.moveItem(at: tempURL, to: destURL)
completionHandler?(nil)
} catch let e {
completionHandler?(e)
}
}
operation.add(task: task)
task.taskDescription = opType.json
task.resume()
} else {
let task = session.fpstreamTask(withHostName: baseURL!.host!, port: baseURL!.port!)
@@ -540,6 +534,7 @@ extension FTPFileProvider: FileProviderOperations {
self.ftpRetrieveFile(task, filePath: self.ftpPath(path), onTask: {
operation.add(task: $0)
$0.taskDescription = opType.json
}, onProgress: { recevied, totalReceived, totalSize in
let progress = Double(totalReceived) / Double(totalSize)
self.delegate?.fileproviderProgress(self, operation: opType, progress: Float(progress))
@@ -574,22 +569,19 @@ extension FTPFileProvider: FileProviderReadWrite {
}
if self.useAppleImplementation {
let task = session.dataTask(with: url(of: path)) { (data, response, error) in
if let error = error {
self.dispatch_queue.async {
completionHandler(nil, error)
self.delegateNotify(opType, error: error)
}
return
}
if let data = data {
self.dispatch_queue.async {
completionHandler(data, nil)
self.delegateNotify(opType, error: nil)
}
let task = session.downloadTask(with: url(of: path))
completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { error in
completionHandler(nil, error)
}
downloadCompletionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { tempURL in
do {
let data = try Data(contentsOf: tempURL)
completionHandler(data, nil)
} catch let e {
completionHandler(nil, e)
}
}
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(operationType: opType, tasks: [task])
} else {
@@ -619,6 +611,7 @@ extension FTPFileProvider: FileProviderReadWrite {
self.ftpRetrieveData(task, filePath: self.ftpPath(path), from: offset, length: length, onTask: {
operation.add(task: $0)
$0.taskDescription = opType.json
}, onProgress: { recevied, totalReceived, totalSize in
let progress = Double(totalReceived) / Double(totalSize)
self.delegate?.fileproviderProgress(self, operation: opType, progress: Float(progress))
@@ -663,6 +656,7 @@ extension FTPFileProvider: FileProviderReadWrite {
let storeHandler = {
self.ftpStore(task, filePath: self.ftpPath(path), fromData: data ?? Data(), fromFile: nil, onTask: {
operation.add(task: $0)
$0.taskDescription = opType.json
}, completionHandler: { (error) in
self.ftpQuit(task)
self.dispatch_queue.async {
+23 -43
View File
@@ -69,8 +69,7 @@ extension FTPFileProvider {
if let data = data, let response = String(data: data, encoding: .utf8) {
completionHandler(response.trimmingCharacters(in: CharacterSet(charactersIn: FTPFileProvider.carriage)), nil)
} else {
let badResponseError = NSError(domain: URLError.errorDomain, code: URLError.cannotParseResponse.rawValue, userInfo: nil)
completionHandler(nil, badResponseError)
completionHandler(nil, self.throwError("", code: URLError.cannotParseResponse))
return
}
}
@@ -92,8 +91,7 @@ extension FTPFileProvider {
}
guard let data = data, let response = String(data: data, encoding: .utf8) else {
let error = NSError(domain: URLError.errorDomain, code: URLError.cannotParseResponse.rawValue, userInfo: nil)
completionHandler(error)
completionHandler(self.throwError("", code: URLError.cannotParseResponse))
return
}
@@ -114,8 +112,7 @@ extension FTPFileProvider {
}
guard let response = response else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler(error)
completionHandler(self.throwError("", code: URLError.badServerResponse))
return
}
@@ -131,8 +128,7 @@ extension FTPFileProvider {
if response?.hasPrefix("2") ?? false {
completionHandler(nil)
} else {
let error = NSError(domain: URLError.errorDomain, code: URLError.userAuthenticationRequired.rawValue, userInfo: nil)
completionHandler(error)
completionHandler(self.throwError("", code: URLError.userAuthenticationRequired))
}
}
return
@@ -166,8 +162,7 @@ extension FTPFileProvider {
}
guard let response = response else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler(error)
completionHandler(self.throwError(path, code: URLError.badServerResponse))
return
}
@@ -200,15 +195,13 @@ extension FTPFileProvider {
}
guard let response = response, let destString = response.components(separatedBy: " ").flatMap({ $0 }).last else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler(nil, error)
completionHandler(nil, self.throwError("", code: URLError.badServerResponse))
return
}
let destArray = destString.components(separatedBy: ",").flatMap({ UInt32(trimmedNumber($0)) })
guard destArray.count == 6 else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler(nil, error)
completionHandler(nil, self.throwError("", code: URLError.badServerResponse))
return
}
@@ -239,14 +232,12 @@ extension FTPFileProvider {
}
guard let response = response else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler(nil, error)
completionHandler(nil, self.throwError("", code: URLError.badServerResponse))
return
}
guard !response.hasPrefix("5") else {
let error = NSError(domain: URLError.errorDomain, code: URLError.cannotConnectToHost.rawValue, userInfo: nil)
completionHandler(nil, error)
completionHandler(nil, self.throwError("", code: URLError.cannotConnectToHost))
return
}
@@ -296,8 +287,7 @@ extension FTPFileProvider {
}
guard let dataTask = dataTask else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler([], error)
completionHandler([], self.throwError(path, code: URLError.badServerResponse))
return
}
@@ -329,15 +319,13 @@ extension FTPFileProvider {
}
if waitResult == .timedOut {
error = NSError(domain: URLError.errorDomain, code: URLError.timedOut.rawValue, userInfo: nil)
completionHandler([], error)
completionHandler([], self.throwError(path, code: URLError.timedOut))
return
}
}
guard let response = String(data: finalData, encoding: .utf8) else {
error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler([], error)
completionHandler([], self.throwError(path, code: URLError.badServerResponse))
return
}
@@ -353,8 +341,7 @@ extension FTPFileProvider {
}
guard let response = response else {
let badResponseError = NSError(domain: URLError.errorDomain, code: URLError.cannotParseResponse.rawValue, userInfo: nil)
completionHandler([], badResponseError)
completionHandler([], self.throwError(path, code: URLError.cannotParseResponse))
return
}
@@ -402,8 +389,7 @@ extension FTPFileProvider {
}
guard let dataTask = dataTask else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler(nil, error)
completionHandler(nil, self.throwError(filePath, code: URLError.badServerResponse))
return
}
@@ -440,8 +426,7 @@ extension FTPFileProvider {
}
if waitResult == .timedOut {
error = NSError(domain: URLError.errorDomain, code: URLError.timedOut.rawValue, userInfo: nil)
completionHandler(nil, error)
completionHandler(nil, self.throwError(filePath, code: URLError.timedOut))
return
}
}
@@ -463,8 +448,7 @@ extension FTPFileProvider {
}
guard let response = response else {
let badResponseError = NSError(domain: URLError.errorDomain, code: URLError.cannotParseResponse.rawValue, userInfo: nil)
completionHandler(nil, badResponseError)
completionHandler(nil, self.throwError(filePath, code: URLError.cannotParseResponse))
return
}
@@ -511,8 +495,7 @@ extension FTPFileProvider {
}
guard let dataTask = dataTask else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler(nil, error)
completionHandler(nil, self.throwError(filePath, code: URLError.badServerResponse))
return
}
@@ -549,7 +532,7 @@ extension FTPFileProvider {
}
if waitResult == .timedOut {
error = NSError(domain: URLError.errorDomain, code: URLError.timedOut.rawValue, userInfo: nil)
error = self.throwError("", code: URLError.timedOut)
completionHandler(nil, error)
return
}
@@ -580,8 +563,7 @@ extension FTPFileProvider {
}
guard let response = response else {
let badResponseError = NSError(domain: URLError.errorDomain, code: URLError.cannotParseResponse.rawValue, userInfo: nil)
completionHandler(nil, badResponseError)
completionHandler(nil, self.throwError(filePath, code: URLError.cannotParseResponse))
return
}
@@ -610,8 +592,7 @@ extension FTPFileProvider {
}
guard let dataTask = dataTask else {
let error = NSError(domain: URLError.errorDomain, code: URLError.badServerResponse.rawValue, userInfo: nil)
completionHandler(error)
completionHandler(self.throwError(filePath, code: URLError.badServerResponse))
return
}
@@ -658,7 +639,7 @@ extension FTPFileProvider {
}
if waitResult == .timedOut {
error = NSError(domain: URLError.errorDomain, code: URLError.timedOut.rawValue, userInfo: nil)
error = self.throwError(filePath, code: URLError.timedOut)
completionHandler(error)
return
}
@@ -674,8 +655,7 @@ extension FTPFileProvider {
}
guard let response = response else {
let badResponseError = NSError(domain: URLError.errorDomain, code: URLError.cannotParseResponse.rawValue, userInfo: nil)
completionHandler(badResponseError)
completionHandler(self.throwError(filePath, code: URLError.cannotParseResponse))
return
}
@@ -828,7 +808,7 @@ extension FTPFileProvider {
file.size = Int64(attribute) ?? -1
case "media-type":
file.allValues[.mimeType] = attribute
file.allValues[.mimeTypeKey] = attribute
default:
break
+6 -12
View File
@@ -24,20 +24,14 @@ open class FileObject: Equatable {
self.path = path
}
/// url to access the resource, not supported by Dropbox provider
@available(*, obsoleted: 1.0, renamed: "url", message: "Use url.absoluteURL instead.")
open var absoluteURL: URL? {
return url?.absoluteURL
}
/// URL to access the resource, can be a relative URL against base URL.
/// not supported by Dropbox provider.
open internal(set) var url: URL? {
get {
return allValues[.fileURL] as? URL
return allValues[.fileURLKey] as? URL
}
set {
allValues[.fileURL] = newValue
allValues[.fileURLKey] = newValue
}
}
@@ -152,8 +146,8 @@ open class FileObject: Equatable {
}
internal func mapPredicate() -> [String: Any] {
let mapDict: [URLResourceKey: String] = [.fileURL: "url", .nameKey: "name", .pathKey: "path", .fileSizeKey: "filesize", .creationDateKey: "creationDate",
.contentModificationDateKey: "modifiedDate", .isHiddenKey: "isHidden", .isWritableKey: "isWritable", .serverDate: "serverDate", .entryTag: "entryTag", .mimeType: "mimeType"]
let mapDict: [URLResourceKey: String] = [.fileURLKey: "url", .nameKey: "name", .pathKey: "path", .fileSizeKey: "filesize", .creationDateKey: "creationDate",
.contentModificationDateKey: "modifiedDate", .isHiddenKey: "isHidden", .isWritableKey: "isWritable", .serverDateKey: "serverDate", .entryTagKey: "entryTag", .mimeTypeKey: "mimeType"]
let typeDict: [URLFileResourceType: String] = [.directory: "directory", .regular: "regular", .symbolicLink: "symbolicLink", .unknown: "unknown"]
var result = [String: Any]()
for (key, value) in allValues {
@@ -172,9 +166,9 @@ open class FileObject: Equatable {
/// Converts macOS spotlight query for searching files to a query that can be used for `searchFiles()` method
static public func convertPredicate(fromSpotlight query: NSPredicate) -> NSPredicate {
let mapDict: [String: URLResourceKey] = [NSMetadataItemURLKey: .fileURL, NSMetadataItemFSNameKey: .nameKey, NSMetadataItemPathKey: .pathKey,
let mapDict: [String: URLResourceKey] = [NSMetadataItemURLKey: .fileURLKey, NSMetadataItemFSNameKey: .nameKey, NSMetadataItemPathKey: .pathKey,
NSMetadataItemFSSizeKey: .fileSizeKey, NSMetadataItemFSCreationDateKey: .creationDateKey,
NSMetadataItemFSContentChangeDateKey: .contentModificationDateKey, "kMDItemFSInvisible": .isHiddenKey, "kMDItemFSIsWriteable": .isWritableKey, "kMDItemKind": .mimeType]
NSMetadataItemFSContentChangeDateKey: .contentModificationDateKey, "kMDItemFSInvisible": .isHiddenKey, "kMDItemFSIsWriteable": .isWritableKey, "kMDItemKind": .mimeTypeKey]
if let cQuery = query as? NSCompoundPredicate {
let newSub = cQuery.subpredicates.map { convertPredicate(fromSpotlight: $0 as! NSPredicate) }
+8 -5
View File
@@ -195,6 +195,12 @@ public protocol FileProviderBasicRemote: FileProviderBasic {
var validatingCache: Bool { get set }
}
internal protocol FileProviderBasicRemoteInternal: FileProviderBasic {
var completionHandlersForTasks: [Int: SimpleCompletionHandler] { get set }
var downloadCompletionHandlersForTasks: [Int: (URL) -> Void] { get set }
var dataCompletionHandlersForTasks: [Int: (Data) -> Void] { 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 }
@@ -606,11 +612,6 @@ extension FileProviderBasic {
return type(of: self).type
}
/// **OBSOLETED** This property never worked as expected and is redundant as only supported by `LocalFileProvider`.
/// To simulate `false` value, assign `URL(fileURLWithPath: "/")` to `baseURL`.
@available(*, obsoleted: 1.0, message: "Redundant property, now is always true.")
var isPathRelative: Bool { return true }
public func url(of path: String? = nil) -> URL {
var rpath: String = path ?? self.currentPath
rpath = rpath.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? rpath
@@ -938,6 +939,8 @@ public enum FileOperationType: CustomStringConvertible {
}
let dest = json["dest"] as? String
switch type {
case "Fetch":
self = .fetch(path: source)
case "Create":
self = .create(path: source)
case "Modify":
+13 -4
View File
@@ -38,10 +38,19 @@ extension URLFileResourceType {
}
internal extension URLResourceKey {
static let fileURL = URLResourceKey(rawValue: "NSURLFileURLKey")
static let serverDate = URLResourceKey(rawValue: "NSURLServerDateKey")
static let entryTag = URLResourceKey(rawValue: "NSURLEntryTagKey")
static let mimeType = URLResourceKey(rawValue: "NSURLMIMETypeIdentifierKey")
static let fileURLKey = URLResourceKey(rawValue: "NSURLFileURLKey")
static let serverDateKey = URLResourceKey(rawValue: "NSURLServerDateKey")
static let entryTagKey = URLResourceKey(rawValue: "NSURLEntryTagKey")
static let mimeTypeKey = URLResourceKey(rawValue: "NSURLMIMETypeIdentifierKey")
@available(*, deprecated, renamed: "fileURLKey")
static let fileURL = fileURLKey
@available(*, deprecated, renamed: "serverDateKey")
static let serverDate = serverDateKey
@available(*, deprecated, renamed: "entryTagKey")
static let entryTag = entryTagKey
@available(*, deprecated, renamed: "mimeTypeKey")
static let mimeType = mimeTypeKey
}
internal extension URL {
-6
View File
@@ -140,12 +140,6 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo
return copy
}
/// **OBSOLETED:** No longer is in use and overriding this method has no effect anymore.
@available(*, obsoleted: 1.0, message: "Overriding this method has no effect anymore.")
open class func defaultBaseURL() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
dispatch_queue.async {
do {
+4 -2
View File
@@ -37,7 +37,7 @@ public final class LocalFileObject: FileObject {
/// Initiates a `LocalFileObject` with attributes of file in url.
public convenience init?(fileWithURL fileURL: URL) {
do {
let values = try fileURL.resourceValues(forKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .fileResourceTypeKey, .isHiddenKey, .isWritableKey, .typeIdentifierKey, .generationIdentifierKey, .documentIdentifierKey])
let values = try fileURL.resourceValues(forKeys: [.nameKey, .fileSizeKey, .totalFileSizeKey, .fileAllocatedSizeKey, .totalFileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .fileResourceTypeKey, .isHiddenKey, .isWritableKey, .typeIdentifierKey, .generationIdentifierKey, .documentIdentifierKey])
let path = fileURL.relativePath.hasPrefix("/") ? fileURL.relativePath : "/" + fileURL.relativePath
self.init(url: fileURL, name: values.name ?? fileURL.lastPathComponent, path: path)
@@ -216,9 +216,11 @@ internal class LocalFileProviderManagerDelegate: NSObject, FileManagerDelegate {
}
}
/// Local operation handling is limited. Please don't use as much as possible.
/// - Note: Local operation handling is limited. Please don't use as much as possible.
open class LocalOperationHandle: OperationHandle {
/// Url of file which operation is doing on
public let baseURL: URL
/// Type of operation
public let operationType: FileOperationType
init (operationType: FileOperationType, baseURL: URL?) {
@@ -33,7 +33,11 @@ open class OneDriveFileProvider: FileProviderBasicRemote {
}
open weak var delegate: FileProviderDelegate?
open var credential: URLCredential?
open var credential: URLCredential? {
didSet {
sessionDelegate?.credential = self.credential
}
}
open private(set) var cache: URLCache?
public var useCache: Bool
public var validatingCache: Bool
@@ -41,16 +45,30 @@ open class OneDriveFileProvider: FileProviderBasicRemote {
fileprivate var _session: URLSession?
fileprivate var sessionDelegate: SessionDelegate?
public var session: URLSession {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
let queue = OperationQueue()
//queue.underlyingQueue = dispatch_queue
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: queue)
get {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self)
let queue = OperationQueue()
//queue.underlyingQueue = dispatch_queue
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: queue)
_session?.sessionDescription = UUID().uuidString
initEmptySessionHandler(_session!.sessionDescription!)
}
return _session!
}
set {
assert(newValue.delegate is SessionDelegate, "session instances should have a SessionDelegate instance as delegate.")
_session = newValue
if session.sessionDescription?.isEmpty ?? true {
_session?.sessionDescription = UUID().uuidString
}
self.sessionDelegate = newValue.delegate as? SessionDelegate
initEmptySessionHandler(_session!.sessionDescription!)
}
return _session!
}
/**
@@ -114,6 +132,10 @@ open class OneDriveFileProvider: FileProviderBasicRemote {
}
deinit {
if let sessionuuid = _session?.sessionDescription {
removeSessionHandler(for: sessionuuid)
}
if fileProviderCancelTasksOnInvalidating {
_session?.invalidateAndCancel()
} else {
@@ -279,6 +301,14 @@ extension OneDriveFileProvider: FileProviderOperations {
}
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
// check file is not a folder
guard (try? localFile.resourceValues(forKeys: [.fileResourceTypeKey]))?.fileResourceType ?? .unknown == .regular else {
dispatch_queue.async {
completionHandler?(self.throwError(localFile.path, code: URLError.fileIsDirectory))
}
return nil
}
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
@@ -292,11 +322,10 @@ extension OneDriveFileProvider: FileProviderOperations {
return nil
}
var request = URLRequest(url: self.url(of: path, modifier: "content"))
request.httpMethod = "GET"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
let task = session.downloadTask(with: request)
completionHandlersForTasks[task.taskIdentifier] = completionHandler
downloadCompletionHandlersForTasks[task.taskIdentifier] = { tempURL in
completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = completionHandler
downloadCompletionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { tempURL in
guard let httpResponse = task.response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
let code = FileProviderHTTPErrorCode(rawValue: (task.response as? HTTPURLResponse)?.statusCode ?? -1)
let errorData : Data? = nil //Data(contentsOf: cacheURL) // TODO: Figure out how to get error response data for the error description
@@ -335,14 +364,25 @@ extension OneDriveFileProvider: FileProviderReadWrite {
} else if offset > 0 && length < 0 {
request.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
}
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var serverError: FileProviderOneDriveError?
if let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: httpResponse.statusCode) {
serverError = FileProviderOneDriveError(code: code, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8))
let task = session.downloadTask(with: request)
completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { error in
completionHandler(nil, error)
}
downloadCompletionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { tempURL in
guard let httpResponse = task.response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
let code = FileProviderHTTPErrorCode(rawValue: (task.response as? HTTPURLResponse)?.statusCode ?? -1)
let errorData : Data? = nil //Data(contentsOf: cacheURL) // TODO: Figure out how to get error response data for the error description
let serverError : FileProviderOneDriveError? = code != nil ? FileProviderOneDriveError(code: code!, path: path, errorDescription: String(data: errorData ?? Data(), encoding: .utf8)) : nil
completionHandler(nil, serverError)
return
}
let filedata = serverError ?? error == nil ? data : nil
completionHandler(filedata, serverError ?? error)
})
do {
let data = try Data(contentsOf: tempURL)
completionHandler(data, nil)
} catch let e {
completionHandler(nil, e)
}
}
task.taskDescription = opType.json
task.resume()
return RemoteOperationHandle(operationType: opType, tasks: [task])
+20 -41
View File
@@ -61,20 +61,20 @@ public final class OneDriveFileObject: FileObject {
/// MIME type of file contents returned by OneDrive server.
open internal(set) var contentType: String {
get {
return allValues[.mimeType] as? String ?? ""
return allValues[.mimeTypeKey] as? String ?? ""
}
set {
allValues[.mimeType] = newValue
allValues[.mimeTypeKey] = newValue
}
}
/// HTTP E-Tag, can be used to mark changed files.
open internal(set) var entryTag: String? {
get {
return allValues[.entryTag] as? String
return allValues[.entryTagKey] as? String
}
set {
allValues[.entryTag] = newValue
allValues[.entryTagKey] = newValue
}
}
}
@@ -113,37 +113,8 @@ internal extension OneDriveFileProvider {
task.resume()
}
func upload_simple(_ targetPath: String, data: Data, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
if data.count > 100 * 1024 * 1024 {
let error = FileProviderOneDriveError(code: .payloadTooLarge, path: targetPath, errorDescription: nil)
completionHandler?(error)
self.delegateNotify(.create(path: targetPath), error: error)
return nil
}
let queryStr = overwrite ? "" : "?@name.conflictBehavior=fail"
let url = self.url(of: targetPath, modifier: "content\(queryStr)")
var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
request.httpBody = data
let task = session.uploadTask(with: request, from: data)
completionHandlersForTasks[task.taskIdentifier] = completionHandler
dataCompletionHandlersForTasks[task.taskIdentifier] = { [weak self] data in
var responseError: FileProviderOneDriveError?
if let code = (task.response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderOneDriveError(code: rCode, path: targetPath, errorDescription: String(data: data, encoding: .utf8))
}
completionHandler?(responseError)
self?.delegateNotify(.create(path: targetPath), error: responseError)
}
task.taskDescription = operation.json
task.resume()
return RemoteOperationHandle(operationType: operation, tasks: [task])
}
func upload_simple(_ targetPath: String, localFile: URL, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let size = (try? localFile.resourceValues(forKeys: [.fileSizeKey]))?.fileSize ?? -1
func upload_simple(_ targetPath: String, data: Data? = nil , localFile: URL? = nil, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let size = data?.count ?? (try? localFile?.resourceValues(forKeys: [.fileSizeKey]))??.fileSize ?? -1
if size > 100 * 1024 * 1024 {
let error = FileProviderOneDriveError(code: .payloadTooLarge, path: targetPath, errorDescription: nil)
completionHandler?(error)
@@ -156,15 +127,23 @@ internal extension OneDriveFileProvider {
request.httpMethod = "PUT"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
let task = session.uploadTask(with: request, fromFile: localFile)
completionHandlersForTasks[task.taskIdentifier] = completionHandler
dataCompletionHandlersForTasks[task.taskIdentifier] = { [weak self] data in
let task: URLSessionUploadTask
if let data = data {
task = session.uploadTask(with: request, from: data)
} else if let localFile = localFile {
task = session.uploadTask(with: request, fromFile: localFile)
} else {
return nil
}
completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { [weak self] error in
var responseError: FileProviderOneDriveError?
if let code = (task.response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderOneDriveError(code: rCode, path: targetPath, errorDescription: String(data: data, encoding: .utf8))
// We can't fetch server result from delegate!
responseError = FileProviderOneDriveError(code: rCode, path: targetPath, errorDescription: nil)
}
completionHandler?(responseError)
self?.delegateNotify(.create(path: targetPath), error: responseError)
completionHandler?(responseError ?? error)
self?.delegateNotify(.create(path: targetPath), error: responseError ?? error)
}
task.taskDescription = operation.json
task.resume()
+69 -35
View File
@@ -90,58 +90,83 @@ extension FileProviderHTTPError {
}
}
internal var completionHandlersForTasks = [Int: SimpleCompletionHandler]()
internal var downloadCompletionHandlersForTasks = [Int: (URL) -> Void]()
internal var dataCompletionHandlersForTasks = [Int: (Data) -> Void]()
internal var completionHandlersForTasks = [String: [Int: SimpleCompletionHandler]]()
internal var downloadCompletionHandlersForTasks = [String: [Int: (URL) -> Void]]()
internal var dataCompletionHandlersForTasks = [String: [Int: (Data) -> Void]]()
class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDelegate, URLSessionStreamDelegate {
internal func initEmptySessionHandler(_ uuid: String) {
completionHandlersForTasks[uuid] = [:]
downloadCompletionHandlersForTasks[uuid] = [:]
dataCompletionHandlersForTasks[uuid] = [:]
}
internal func removeSessionHandler(for uuid: String) {
_ = completionHandlersForTasks.removeValue(forKey: uuid)
_ = downloadCompletionHandlersForTasks.removeValue(forKey: uuid)
_ = dataCompletionHandlersForTasks.removeValue(forKey: uuid)
}
/// All objects set to `FileProviderRemote.session` must be an instance of this class
final public class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDelegate, URLSessionStreamDelegate {
weak var fileProvider: (FileProviderBasicRemote & FileProviderOperations)?
var credential: URLCredential?
var finishDownloadHandler: ((_ session: URLSession, _ downloadTask: URLSessionDownloadTask, _ didFinishDownloadingToURL: URL) -> Void)?
var didSendDataHandler: ((_ session: URLSession, _ task: URLSessionTask, _ bytesSent: Int64, _ totalBytesSent: Int64, _ totalBytesExpectedToSend: Int64) -> Void)?
var didReceivedData: ((_ session: URLSession, _ downloadTask: URLSessionDownloadTask, _ bytesWritten: Int64, _ totalBytesWritten: Int64, _ totalBytesExpectedToWrite: Int64) -> Void)?
var didBecomeStream :((_ session: URLSession, _ taskId: Int, _ didBecome: InputStream, _ outputStream: OutputStream) -> Void)?
/// Forwardng URLSessionDownloadTaskDelegate call
public var finishDownloadHandler: ((_ session: URLSession, _ downloadTask: URLSessionDownloadTask, _ didFinishDownloadingToURL: URL) -> Void)?
/// Forwardng URLSessionTaskDelegate call
public var didSendDataHandler: ((_ session: URLSession, _ task: URLSessionTask, _ bytesSent: Int64, _ totalBytesSent: Int64, _ totalBytesExpectedToSend: Int64) -> Void)?
/// Forwardng URLSessionDownloadTaskDelegate call
public var didReceivedData: ((_ session: URLSession, _ downloadTask: URLSessionDownloadTask, _ bytesWritten: Int64, _ totalBytesWritten: Int64, _ totalBytesExpectedToWrite: Int64) -> Void)?
/// Forwardng URLSessionStreamTaskDelegate call
public var didBecomeStream :((_ session: URLSession, _ taskId: Int, _ didBecome: InputStream, _ outputStream: OutputStream) -> Void)?
init(fileProvider: FileProviderBasicRemote & FileProviderOperations, credential: URLCredential?) {
public init(fileProvider: FileProviderBasicRemote & FileProviderOperations) {
self.fileProvider = fileProvider
self.credential = credential
self.credential = fileProvider.credential
}
// codebeat:disable[ARITY]
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil {
let completionHandler = completionHandlersForTasks[task.taskIdentifier] ?? nil
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if !(error == nil && task is URLSessionDownloadTask) {
let completionHandler = completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] ?? nil
completionHandler?(error)
completionHandlersForTasks.removeValue(forKey: task.taskIdentifier)
_ = completionHandlersForTasks[session.sessionDescription!]?.removeValue(forKey: task.taskIdentifier)
}
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
let completionHandler = dataCompletionHandlersForTasks[dataTask.taskIdentifier] ?? nil
completionHandler?(data)
completionHandlersForTasks.removeValue(forKey: dataTask.taskIdentifier)
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
self.finishDownloadHandler?(session, downloadTask, location)
let dcompletionHandler = downloadCompletionHandlersForTasks[downloadTask.taskIdentifier]
dcompletionHandler?(location)
completionHandlersForTasks.removeValue(forKey: downloadTask.taskIdentifier)
guard let json = downloadTask.taskDescription?.deserializeJSON(),
guard let json = task.taskDescription?.deserializeJSON(),
let op = FileOperationType(json: json), let fileProvider = fileProvider else {
return
}
if !(task is URLSessionDownloadTask), case FileOperationType.fetch = op {
return
}
DispatchQueue.main.async {
fileProvider.delegate?.fileproviderSucceed(fileProvider, operation: op)
if error != nil {
fileProvider.delegate?.fileproviderFailed(fileProvider, operation: op)
} else {
fileProvider.delegate?.fileproviderSucceed(fileProvider, operation: op)
}
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
let completionHandler = dataCompletionHandlersForTasks[session.sessionDescription!]?[dataTask.taskIdentifier] ?? nil
completionHandler?(data)
_ = dataCompletionHandlersForTasks[session.sessionDescription!]?.removeValue(forKey: dataTask.taskIdentifier)
}
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
self.finishDownloadHandler?(session, downloadTask, location)
let dcompletionHandler = downloadCompletionHandlersForTasks[session.sessionDescription!]?[downloadTask.taskIdentifier]
dcompletionHandler?(location)
_ = downloadCompletionHandlersForTasks[session.sessionDescription!]?.removeValue(forKey: downloadTask.taskIdentifier)
_ = completionHandlersForTasks[session.sessionDescription!]?.removeValue(forKey: downloadTask.taskIdentifier)
}
public func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
self.didSendDataHandler?(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
guard let json = task.taskDescription?.deserializeJSON(),
@@ -149,6 +174,15 @@ class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDeleg
return
}
switch op {
case .create(path: let path):
if path.hasSuffix("/") { return }
case .modify:
break
default:
return
}
let progress = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
DispatchQueue.main.async {
@@ -156,7 +190,7 @@ class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDeleg
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
self.didReceivedData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
guard let json = downloadTask.taskDescription?.deserializeJSON(),
@@ -169,11 +203,11 @@ class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDeleg
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
public func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
authenticate(didReceive: challenge, completionHandler: completionHandler)
}
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
authenticate(didReceive: challenge, completionHandler: completionHandler)
}
@@ -189,7 +223,7 @@ class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDeleg
}
@available(iOS 9.0, macOS 10.11, *)
func urlSession(_ session: URLSession, streamTask: URLSessionStreamTask, didBecome inputStream: InputStream, outputStream: OutputStream) {
public func urlSession(_ session: URLSession, streamTask: URLSessionStreamTask, didBecome inputStream: InputStream, outputStream: OutputStream) {
self.didBecomeStream?(session, streamTask.taskIdentifier, inputStream, outputStream)
}
}
+10 -10
View File
@@ -11,11 +11,11 @@ import Foundation
// This client implementation is for little-endian platform, namely x86, x64 & arm
// For big-endian platforms like PowerPC, there must be a huge overhaul
protocol SMBProtocolClientDelegate: class {
func receivedResponse(client: SMB2ProtocolClient, response: SMBResponse, for: SMBRequest)
protocol FileProviderSMBTaskDelegate: class {
func receivedResponse(client: FileProviderSMBTask, response: SMBResponse, for: SMBRequest)
}
class SMB2ProtocolClient: FileProviderStreamTask {
class FileProviderSMBTask: FileProviderStreamTask {
var timeout: TimeInterval = 30
private(set) var lastMessageID: UInt64 = 0
@@ -31,14 +31,14 @@ class SMB2ProtocolClient: FileProviderStreamTask {
private(set) var requestStack = [Int: SMBRequest]()
private(set) var responseStack = [Int: SMBResponse]()
weak var delegate: SMBProtocolClientDelegate?
weak var delegate: FileProviderSMBTaskDelegate?
func sendNegotiate(completionHandler: SimpleCompletionHandler) -> UInt64 {
let mId = messageId()
let smbHeader = SMB2.Header(command: .NEGOTIATE, creditRequestResponse: UInt16(126), messageId: mId, treeId: UInt32(0), sessionId: UInt64(0))
let msg = SMB2.NegotiateRequest()
let data = createSMB2Message(header: smbHeader, message: msg)
self.write(data, timeout: 0, completionHandler: { (e) in
self.write(data, timeout: timeout, completionHandler: { (e) in
completionHandler?(e)
})
return mId
@@ -50,7 +50,7 @@ class SMB2ProtocolClient: FileProviderStreamTask {
let smbHeader = SMB2.Header(command: SMB2.Command.SESSION_SETUP, creditRequestResponse: credit, messageId: mId, treeId: UInt32(0), sessionId: sessionId)
let msg = SMB2.SessionSetupRequest(singing: [])
let data = createSMB2Message(header: smbHeader, message: msg)
self.write(data, timeout: 0, completionHandler: { (e) in
self.write(data, timeout: timeout, completionHandler: { (e) in
if self.sessionId == 0 {
self.readData(ofMinLength: 64, maxLength: 65536, timeout: self.timeout, completionHandler: { (data, eof, e2) in
// TODO: set session id
@@ -73,7 +73,7 @@ class SMB2ProtocolClient: FileProviderStreamTask {
let tcHeader = SMB2.TreeConnectRequest.Header(flags: [])
let msg = SMB2.TreeConnectRequest(header: tcHeader, host: host, share: share)
let data = createSMB2Message(header: smbHeader, message: msg!)
self.write(data, timeout: 0, completionHandler: { (e) in
self.write(data, timeout: timeout, completionHandler: { (e) in
completionHandler?(e)
})
@@ -85,7 +85,7 @@ class SMB2ProtocolClient: FileProviderStreamTask {
let smbHeader = SMB2.Header(command: .TREE_DISCONNECT, creditRequestResponse: 111, messageId: mId, treeId: treeId, sessionId: sessionId)
let msg = SMB2.TreeDisconnect()
let data = createSMB2Message(header: smbHeader, message: msg)
self.write(data, timeout: 0, completionHandler: { (e) in
self.write(data, timeout: timeout, completionHandler: { (e) in
completionHandler?(e)
})
return mId
@@ -96,7 +96,7 @@ class SMB2ProtocolClient: FileProviderStreamTask {
let smbHeader = SMB2.Header(command: .LOGOFF, creditRequestResponse: 0, messageId: mId, treeId: 0, sessionId: sessionId)
let msg = SMB2.LogOff()
let data = createSMB2Message(header: smbHeader, message: msg)
self.write(data, timeout: 0, completionHandler: { (e) in
self.write(data, timeout: timeout, completionHandler: { (e) in
completionHandler?(e)
})
return mId
@@ -108,7 +108,7 @@ class SMB2ProtocolClient: FileProviderStreamTask {
}
// MARK: create and analyse messages
extension SMB2ProtocolClient {
extension FileProviderSMBTask {
func determineSMBVersion(_ data: Data) -> Float {
let smbverChar: Int8 = Int8(bitPattern: data.first ?? 0)
let version = 0 - smbverChar
+142 -39
View File
@@ -44,16 +44,30 @@ open class WebDAVFileProvider: FileProviderBasicRemote {
fileprivate var _session: URLSession?
fileprivate var sessionDelegate: SessionDelegate?
public var session: URLSession {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self, credential: credential)
let queue = OperationQueue()
//queue.underlyingQueue = dispatch_queue
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDownloadDelegate?, delegateQueue: queue)
get {
if _session == nil {
self.sessionDelegate = SessionDelegate(fileProvider: self)
let queue = OperationQueue()
//queue.underlyingQueue = dispatch_queue
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
_session = URLSession(configuration: config, delegate: sessionDelegate as URLSessionDownloadDelegate?, delegateQueue: queue)
_session?.sessionDescription = UUID().uuidString
initEmptySessionHandler(_session!.sessionDescription!)
}
return _session!
}
set {
assert(newValue.delegate is SessionDelegate, "session instances should have a SessionDelegate instance as delegate.")
_session = newValue
if session.sessionDescription?.isEmpty ?? true {
_session?.sessionDescription = UUID().uuidString
}
self.sessionDelegate = newValue.delegate as? SessionDelegate
initEmptySessionHandler(_session!.sessionDescription!)
}
return _session!
}
/**
@@ -94,8 +108,8 @@ open class WebDAVFileProvider: FileProviderBasicRemote {
aCoder.encode(self.baseURL, forKey: "baseURL")
aCoder.encode(self.credential, forKey: "credential")
aCoder.encode(self.currentPath, forKey: "currentPath")
aCoder.encode(self.useCache, forKey: "isCoorinating")
aCoder.encode(self.validatingCache, forKey: "undoManager")
aCoder.encode(self.useCache, forKey: "useCache")
aCoder.encode(self.validatingCache, forKey: "validatingCache")
}
public static var supportsSecureCoding: Bool {
@@ -113,6 +127,10 @@ open class WebDAVFileProvider: FileProviderBasicRemote {
}
deinit {
if let sessionuuid = _session?.sessionDescription {
removeSessionHandler(for: sessionuuid)
}
if fileProviderCancelTasksOnInvalidating {
_session?.invalidateAndCancel()
} else {
@@ -120,14 +138,29 @@ open class WebDAVFileProvider: FileProviderBasicRemote {
}
}
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
public func contentsOfDirectory(path: String, completionHandler: @escaping (([FileObject], Error?) -> Void)) {
self.contentsOfDirectory(path: path, including: [], completionHandler: completionHandler)
}
/**
Returns an Array of `FileObject`s identifying the the directory entries via asynchronous completion handler.
If the directory contains no entries or an error is occured, this method will return the empty array.
- Parameter path: path to target directory. If empty, `currentPath` value will be used.
- Parameter including: An array which determines which file properties should be considered to fetch.
- Parameter completionHandler: a closure with result of directory entries or error.
- `contents`: An array of `FileObject` identifying the the directory entries.
- `error`: Error returned by system.
*/
open func contentsOfDirectory(path: String, including: [URLResourceKey], completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
let opType = FileOperationType.fetch(path: path)
let url = self.url(of: path).appendingPathComponent("")
var request = URLRequest(url: url)
request.httpMethod = "PROPFIND"
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.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n\(WebDavFileObject.propString(including))\n</D:propfind>".data(using: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
runDataTask(with: request, operationHandle: RemoteOperationHandle(operationType: opType, tasks: []), completionHandler: { (data, response, error) in
var responseError: FileProviderWebDavError?
@@ -149,12 +182,27 @@ open class WebDAVFileProvider: FileProviderBasicRemote {
}
open func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void)) {
self.attributesOfItem(path: path, including: [], completionHandler: completionHandler)
}
/**
Returns a `FileObject` containing the attributes of the item (file, directory, symlink, etc.) at the path in question via asynchronous completion handler.
If the directory contains no entries or an error is occured, this method will return the empty `FileObject`.
- Parameter path: path to target directory. If empty, `currentPath` value will be used.
- Parameter including: An array which determines which file properties should be considered to fetch.
- Parameter completionHandler: a closure with result of directory entries or error.
- `attributes`: A `FileObject` containing the attributes of the item.
- `error`: Error returned by system.
*/
open func attributesOfItem(path: String, including: [URLResourceKey], completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void)) {
let url = self.url(of: path)
var request = URLRequest(url: url)
request.httpMethod = "PROPFIND"
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.httpBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n\(WebDavFileObject.propString(including))\n</D:propfind>".data(using: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
runDataTask(with: request, completionHandler: { (data, response, error) in
var responseError: FileProviderWebDavError?
@@ -345,6 +393,14 @@ extension WebDAVFileProvider: FileProviderOperations {
@discardableResult
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
// check file is not a folder
guard (try? localFile.resourceValues(forKeys: [.fileResourceTypeKey]))?.fileResourceType ?? .unknown == .regular else {
dispatch_queue.async {
completionHandler?(self.throwError(localFile.path, code: URLError.fileIsDirectory))
}
return nil
}
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
@@ -356,14 +412,14 @@ extension WebDAVFileProvider: FileProviderOperations {
}
request.httpMethod = "PUT"
let task = session.uploadTask(with: request, fromFile: localFile)
completionHandlersForTasks[task.taskIdentifier] = completionHandler
dataCompletionHandlersForTasks[task.taskIdentifier] = { [weak self] data in
completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { [weak self] error in
var responseError: FileProviderWebDavError?
if let code = (task.response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, path: toPath, errorDescription: String(data: data, encoding: .utf8), url: url)
// We can't fetch server result from delegate!
responseError = FileProviderWebDavError(code: rCode, path: toPath, errorDescription: nil, url: url)
}
completionHandler?(responseError)
self?.delegateNotify(.create(path: toPath), error: responseError)
completionHandler?(responseError ?? error)
self?.delegateNotify(.create(path: toPath), error: responseError ?? error)
}
task.taskDescription = opType.json
task.resume()
@@ -379,8 +435,8 @@ extension WebDAVFileProvider: FileProviderOperations {
let url = self.url(of:path)
let request = URLRequest(url: url)
let task = session.downloadTask(with: request)
completionHandlersForTasks[task.taskIdentifier] = completionHandler
downloadCompletionHandlersForTasks[task.taskIdentifier] = { tempURL in
completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = completionHandler
downloadCompletionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { tempURL in
guard let httpResponse = task.response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
let code = FileProviderHTTPErrorCode(rawValue: (task.response as? HTTPURLResponse)?.statusCode ?? -1)
let serverError : FileProviderWebDavError? = code != nil ? FileProviderWebDavError(code: code!, path: path, errorDescription: code?.description, url: url) : nil
@@ -413,20 +469,35 @@ extension WebDAVFileProvider: FileProviderReadWrite {
let opType = FileOperationType.fetch(path: path)
let url = self.url(of: path)
var request = URLRequest(url: url)
request.httpMethod = "GET"
if length > 0 {
request.setValue("bytes=\(offset)-\(offset + Int64(length) - 1)", forHTTPHeaderField: "Range")
} else if offset > 0 && length < 0 {
request.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
}
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, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8), url: url)
let task = session.downloadTask(with: request)
let handle = RemoteOperationHandle(operationType: opType, tasks: [task])
completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { error in
completionHandler(nil, error)
}
downloadCompletionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { tempURL in
guard let httpResponse = task.response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
let code = FileProviderHTTPErrorCode(rawValue: (task.response as? HTTPURLResponse)?.statusCode ?? -1)
let serverError : FileProviderWebDavError? = code != nil ? FileProviderWebDavError(code: code!, path: path, errorDescription: code?.description, url: url) : nil
completionHandler(nil, serverError)
return
}
completionHandler(data, responseError ?? error)
})
do {
let data = try Data(contentsOf: tempURL)
self.dispatch_queue.async {
completionHandler(data, nil)
}
} catch let e {
completionHandler(nil, e)
}
}
task.taskDescription = opType.json
task.resume()
return handle
}
@@ -444,14 +515,14 @@ extension WebDAVFileProvider: FileProviderReadWrite {
request.setValue("F", forHTTPHeaderField: "Overwrite")
}
let task = session.uploadTask(with: request, from: data ?? Data())
completionHandlersForTasks[task.taskIdentifier] = completionHandler
dataCompletionHandlersForTasks[task.taskIdentifier] = { [weak self] data in
completionHandlersForTasks[session.sessionDescription!]?[task.taskIdentifier] = { [weak self] error in
var responseError: FileProviderWebDavError?
if let code = (task.response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, path: path, errorDescription: String(data: data, encoding: .utf8), url: url)
// We can't fetch server result from delegate!
responseError = FileProviderWebDavError(code: rCode, path: path, errorDescription: nil, url: url)
}
completionHandler?(responseError)
self?.delegateNotify(.create(path: path), error: responseError)
completionHandler?(responseError ?? error)
self?.delegateNotify(opType, error: responseError ?? error)
}
task.taskDescription = opType.json
task.resume()
@@ -591,7 +662,7 @@ struct DavResponse {
public final class WebDavFileObject: FileObject {
internal init(_ davResponse: DavResponse) {
let href = davResponse.href
let name = davResponse.prop["displayname"] ?? (davResponse.hrefString.removingPercentEncoding! as NSString).lastPathComponent
let name = davResponse.prop["displayname"] ?? davResponse.href.lastPathComponent
let relativePath = href.relativePath
let path = relativePath.hasPrefix("/") ? relativePath : ("/" + relativePath)
super.init(url: href, name: name, path: path)
@@ -607,22 +678,54 @@ public final class WebDavFileObject: FileObject {
/// MIME type of the file.
open internal(set) var contentType: String {
get {
return allValues[.mimeType] as? String ?? ""
return allValues[.mimeTypeKey] as? String ?? ""
}
set {
allValues[.mimeType] = newValue
allValues[.mimeTypeKey] = newValue
}
}
/// HTTP E-Tag, can be used to mark changed files.
open internal(set) var entryTag: String? {
get {
return allValues[.entryTag] as? String
return allValues[.entryTagKey] as? String
}
set {
allValues[.entryTag] = newValue
allValues[.entryTagKey] = newValue
}
}
internal class func resourceKeyToDAVProp(_ key: URLResourceKey) -> String? {
switch key {
case URLResourceKey.fileSizeKey:
return "getcontentlength"
case URLResourceKey.creationDateKey:
return "creationdate"
case URLResourceKey.contentModificationDateKey:
return "getlastmodified"
case URLResourceKey.fileResourceTypeKey, URLResourceKey.mimeTypeKey:
return "getcontenttype"
case URLResourceKey.isHiddenKey:
return "ishidden"
case URLResourceKey.entryTagKey:
return "getetag"
default:
return nil
}
}
internal class func propString(_ keys: [URLResourceKey]) -> String {
var propKeys = ""
for item in keys {
if let prop = WebDavFileObject.resourceKeyToDAVProp(item) {
propKeys += "<D:prop><D:\(prop)/></D:prop>"
}
}
if propKeys.isEmpty {
propKeys = "<D:allprop/>"
}
return propKeys
}
}
/// Error returned by WebDAV server when trying to access or do operations on a file or folder.