Making protocol method implementations overridable

This commit is contained in:
Amir Abbas
2017-08-19 19:47:54 +04:30
parent 0de558c160
commit 0da957d25f
7 changed files with 314 additions and 330 deletions
-3
View File
@@ -752,9 +752,6 @@ open class CloudFileProvider: LocalFileProvider, FileProviderSharing {
}
}
}
}
extension CloudFileProvider {
/// Removes local copy of file, but spares cloud copy.
/// - Parameter path: Path of file or directory to be removed from local
+2 -12
View File
@@ -16,7 +16,7 @@ import CoreGraphics
- Note: Uploading files and data are limited to 150MB, for now.
*/
open class DropboxFileProvider: HTTPFileProvider {
open class DropboxFileProvider: HTTPFileProvider, FileProviderSharing {
override open class var type: String { return "Dropbox" }
/// Dropbox RPC API URL, which is equal with [https://api.dropboxapi.com/2/](https://api.dropboxapi.com/2/)
@@ -133,12 +133,6 @@ open class DropboxFileProvider: HTTPFileProvider {
return progress
}
open override func isReachable(completionHandler: @escaping (Bool) -> Void) {
self.storageProperties { total, _ in
completionHandler(total > 0)
}
}
override func request(for operation: FileOperationType, overwrite: Bool, attributes: [URLResourceKey : Any]) -> URLRequest {
// content operations
var request: URLRequest
@@ -227,9 +221,7 @@ open class DropboxFileProvider: HTTPFileProvider {
return super.upload_simple(targetPath, request: request, data: data, localFile: localFile, operation: operation, completionHandler: completionHandler)
}
}
extension DropboxFileProvider {
/*
fileprivate func registerNotifcation(path: String, eventHandler: (() -> Void)) {
/* There is two ways to monitor folders changing in Dropbox. Either using webooks
@@ -245,9 +237,7 @@ extension DropboxFileProvider {
}
*/
// TODO: Implement /get_account & /get_current_account
}
extension DropboxFileProvider: FileProviderSharing {
open func publicLink(to path: String, completionHandler: @escaping ((_ link: URL?, _ attribute: FileObject?, _ expiration: Date?, _ error: Error?) -> Void)) {
let url = URL(string: "files/get_temporary_link", relativeTo: apiURL)!
var request = URLRequest(url: url)
+213 -217
View File
@@ -12,7 +12,7 @@ import Foundation
Allows accessing to FTP files and directories. This provider doesn't cache or save files internally.
It's a complete reimplementation and doesn't use CFNetwork deprecated API.
*/
open class FTPFileProvider: FileProviderBasicRemote {
open class FTPFileProvider: FileProviderBasicRemote, FileProviderOperations, FileProviderReadWrite {
open class var type: String { return "FTP" }
open let baseURL: URL?
open var currentPath: String
@@ -317,7 +317,7 @@ open class FTPFileProvider: FileProviderBasicRemote {
return progress
}
public func url(of path: String?) -> URL {
open func url(of path: String?) -> URL {
let path = (path ?? self.currentPath).trimmingCharacters(in: CharacterSet(charactersIn: "/ ")).addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? (path ?? self.currentPath)
var baseUrlComponent = URLComponents(url: self.baseURL!, resolvingAgainstBaseURL: true)
@@ -326,7 +326,7 @@ open class FTPFileProvider: FileProviderBasicRemote {
return URL(string: path, relativeTo: baseUrlComponent?.url ?? baseURL) ?? baseUrlComponent?.url ?? baseURL!
}
public func relativePathOf(url: URL) -> String {
open func relativePathOf(url: URL) -> String {
// check if url derieved from current base url
let relativePath = url.relativePath
if !relativePath.isEmpty, url.baseURL == self.baseURL {
@@ -347,9 +347,7 @@ open class FTPFileProvider: FileProviderBasicRemote {
}
open weak var fileOperationDelegate: FileOperationDelegate?
}
extension FTPFileProvider: FileProviderOperations {
open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> Progress? {
let path = (atPath as NSString).appendingPathComponent(folderName) + "/"
return doOperation(.create(path: path), completionHandler: completionHandler)
@@ -367,210 +365,6 @@ extension FTPFileProvider: FileProviderOperations {
return doOperation(.remove(path: path), completionHandler: completionHandler)
}
fileprivate func doOperation(_ opType: FileOperationType, completionHandler: SimpleCompletionHandler) -> Progress? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let sourcePath = opType.source
let destPath = opType.destination
let command: String
switch opType {
case .create:
command = "MKD \(ftpPath(sourcePath))"
case .copy:
command = "SITE CPFR \(ftpPath(sourcePath))\r\nSITE CPTO \(ftpPath(destPath!))"
case .move:
command = "RNFR \(ftpPath(sourcePath))\r\nRNTO \(ftpPath(destPath!))"
case .remove:
command = "DELE \(ftpPath(sourcePath))"
case .link:
command = "SITE SYMLINK \(ftpPath(sourcePath)) \(ftpPath(destPath!))"
default: // modify, fetch
return nil
}
let progress = Progress(totalUnitCount: 1)
progress.setUserInfoObject(opType, forKey: .fileProvderOperationTypeKey)
progress.kind = .file
progress.setUserInfoObject(Progress.FileOperationKind.downloading, forKey: .fileOperationKindKey)
let task = session.fpstreamTask(withHostName: baseURL!.host!, port: baseURL!.port!)
self.ftpLogin(task) { (error) in
if let error = error {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
return
}
self.execute(command: command, on: task, completionHandler: { (response, error) in
if let error = error {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
return
}
guard let response = response else {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: self.throwError(sourcePath, code: URLError.badServerResponse))
}
return
}
let codes: [Int] = response.components(separatedBy: .newlines).flatMap({ $0.isEmpty ? nil : $0})
.flatMap {
let code = $0.components(separatedBy: .whitespaces).flatMap({ $0.isEmpty ? nil : $0}).first
return code != nil ? Int(code!) : nil
}
if codes.filter({ (450..<560).contains($0) }).count > 0 {
let errorCode: URLError.Code
switch opType {
case .create:
errorCode = URLError.cannotCreateFile
case .modify:
errorCode = URLError.cannotWriteToFile
case .copy:
self.fallbackCopy(opType, progress: progress, completionHandler: completionHandler)
return
case .move:
errorCode = URLError.cannotMoveFile
case .remove:
self.fallbackRemove(opType, progress: progress, on: task, completionHandler: completionHandler)
return
case .link:
errorCode = URLError.cannotWriteToFile
default:
errorCode = URLError.cannotOpenFile
}
let error = self.throwError(sourcePath, code: errorCode)
progress.cancel()
self.dispatch_queue.async {
completionHandler?(error)
}
self.delegateNotify(opType, error: error)
return
}
progress.completedUnitCount = progress.totalUnitCount
self.dispatch_queue.async {
completionHandler?(nil)
}
self.delegateNotify(opType, error: nil)
})
}
progress.cancellationHandler = { [weak task] in
task?.cancel()
}
progress.setUserInfoObject(Date(), forKey: .startingTimeKey)
return progress
}
private func fallbackCopy(_ opType: FileOperationType, progress: Progress, completionHandler: SimpleCompletionHandler) {
let sourcePath = opType.source
guard let destPath = opType.destination else { return }
let localURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString).appendingPathExtension("tmp")
progress.becomeCurrent(withPendingUnitCount: 1)
_ = self.copyItem(path: sourcePath, toLocalURL: localURL) { (error) in
if let error = error {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
return
}
progress.becomeCurrent(withPendingUnitCount: 1)
_ = self.copyItem(localFile: localURL, to: destPath) { error in
completionHandler?(nil)
self.delegateNotify(opType, error: nil)
}
progress.resignCurrent()
}
progress.resignCurrent()
return
}
private func fallbackRemove(_ opType: FileOperationType, progress: Progress, on task: FileProviderStreamTask, completionHandler: SimpleCompletionHandler) {
let sourcePath = opType.source
self.execute(command: "SITE RMDIR \(ftpPath(sourcePath))", on: task) { (response, error) in
if let error = error {
progress.cancel()
self.dispatch_queue.async {
completionHandler?(error)
}
self.delegateNotify(opType, error: error)
return
}
guard let response = response else {
progress.cancel()
let error = self.throwError(sourcePath, code: URLError.badServerResponse)
self.dispatch_queue.async {
completionHandler?(error)
}
self.delegateNotify(opType, error: error)
return
}
if response.hasPrefix("50") {
self.fallbackRecursiveRemove(opType, progress: progress, on: task, completionHandler: completionHandler)
return
}
var error: Error?
if !response.hasPrefix("2") {
error = self.throwError(sourcePath, code: URLError.cannotRemoveFile)
}
self.dispatch_queue.async {
completionHandler?(error)
}
self.delegateNotify(opType, error: error)
}
}
private func fallbackRecursiveRemove(_ opType: FileOperationType, progress: Progress, on task: FileProviderStreamTask, completionHandler: SimpleCompletionHandler) {
let sourcePath = opType.source
_ = self.recursiveList(path: sourcePath, useMLST: true, completionHandler: { (contents, error) in
if let error = error {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
return
}
let recursiveProgress = Progress(parent: progress, userInfo: nil)
recursiveProgress.totalUnitCount = Int64(contents.count)
let sortedContents = contents.sorted(by: {
$0.path.localizedStandardCompare($1.path) == .orderedDescending
})
var command = ""
for file in sortedContents {
command += (file.isDirectory ? "RMD \(self.ftpPath(file.path))" : "DELE \(self.ftpPath(file.path))") + "\r\n"
}
command += "RMD \(self.ftpPath(sourcePath))"
self.execute(command: command, on: task, completionHandler: { (response, error) in
recursiveProgress.completedUnitCount += 1
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
// TODO: Digest response
})
})
}
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> Progress? {
// check file is not a folder
guard (try? localFile.resourceValues(forKeys: [.fileResourceTypeKey]))?.fileResourceType ?? .unknown == .regular else {
@@ -720,9 +514,7 @@ extension FTPFileProvider: FileProviderOperations {
}
return progress
}
}
extension FTPFileProvider: FileProviderReadWrite {
open func contents(path: String, completionHandler: @escaping ((Data?, Error?) -> Void)) -> Progress? {
let opType = FileOperationType.fetch(path: path)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
@@ -879,9 +671,7 @@ extension FTPFileProvider: FileProviderReadWrite {
return progress
}
}
public extension FTPFileProvider {
/**
Creates a symbolic link at the specified path that points to an item at the given path.
This method does not traverse symbolic links contained in destination path, making it possible
@@ -895,10 +685,216 @@ public extension FTPFileProvider {
- withDestinationPath: The path that contains the item to be pointed to by the link. In other words, this is the destination of the link.
- completionHandler: If an error parameter was provided, a presentable `Error` will be returned.
*/
public func create(symbolicLink path: String, withDestinationPath destPath: String, completionHandler: SimpleCompletionHandler) {
open func create(symbolicLink path: String, withDestinationPath destPath: String, completionHandler: SimpleCompletionHandler) {
let opType = FileOperationType.link(link: path, target: destPath)
_=self.doOperation(opType, completionHandler: completionHandler)
}
}
extension FTPFileProvider {
fileprivate func doOperation(_ opType: FileOperationType, completionHandler: SimpleCompletionHandler) -> Progress? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
let sourcePath = opType.source
let destPath = opType.destination
let command: String
switch opType {
case .create:
command = "MKD \(ftpPath(sourcePath))"
case .copy:
command = "SITE CPFR \(ftpPath(sourcePath))\r\nSITE CPTO \(ftpPath(destPath!))"
case .move:
command = "RNFR \(ftpPath(sourcePath))\r\nRNTO \(ftpPath(destPath!))"
case .remove:
command = "DELE \(ftpPath(sourcePath))"
case .link:
command = "SITE SYMLINK \(ftpPath(sourcePath)) \(ftpPath(destPath!))"
default: // modify, fetch
return nil
}
let progress = Progress(totalUnitCount: 1)
progress.setUserInfoObject(opType, forKey: .fileProvderOperationTypeKey)
progress.kind = .file
progress.setUserInfoObject(Progress.FileOperationKind.downloading, forKey: .fileOperationKindKey)
let task = session.fpstreamTask(withHostName: baseURL!.host!, port: baseURL!.port!)
self.ftpLogin(task) { (error) in
if let error = error {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
return
}
self.execute(command: command, on: task, completionHandler: { (response, error) in
if let error = error {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
return
}
guard let response = response else {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: self.throwError(sourcePath, code: URLError.badServerResponse))
}
return
}
let codes: [Int] = response.components(separatedBy: .newlines).flatMap({ $0.isEmpty ? nil : $0})
.flatMap {
let code = $0.components(separatedBy: .whitespaces).flatMap({ $0.isEmpty ? nil : $0}).first
return code != nil ? Int(code!) : nil
}
if codes.filter({ (450..<560).contains($0) }).count > 0 {
let errorCode: URLError.Code
switch opType {
case .create:
errorCode = URLError.cannotCreateFile
case .modify:
errorCode = URLError.cannotWriteToFile
case .copy:
self.fallbackCopy(opType, progress: progress, completionHandler: completionHandler)
return
case .move:
errorCode = URLError.cannotMoveFile
case .remove:
self.fallbackRemove(opType, progress: progress, on: task, completionHandler: completionHandler)
return
case .link:
errorCode = URLError.cannotWriteToFile
default:
errorCode = URLError.cannotOpenFile
}
let error = self.throwError(sourcePath, code: errorCode)
progress.cancel()
self.dispatch_queue.async {
completionHandler?(error)
}
self.delegateNotify(opType, error: error)
return
}
progress.completedUnitCount = progress.totalUnitCount
self.dispatch_queue.async {
completionHandler?(nil)
}
self.delegateNotify(opType, error: nil)
})
}
progress.cancellationHandler = { [weak task] in
task?.cancel()
}
progress.setUserInfoObject(Date(), forKey: .startingTimeKey)
return progress
}
private func fallbackCopy(_ opType: FileOperationType, progress: Progress, completionHandler: SimpleCompletionHandler) {
let sourcePath = opType.source
guard let destPath = opType.destination else { return }
let localURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString).appendingPathExtension("tmp")
progress.becomeCurrent(withPendingUnitCount: 1)
_ = self.copyItem(path: sourcePath, toLocalURL: localURL) { (error) in
if let error = error {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
return
}
progress.becomeCurrent(withPendingUnitCount: 1)
_ = self.copyItem(localFile: localURL, to: destPath) { error in
completionHandler?(nil)
self.delegateNotify(opType, error: nil)
}
progress.resignCurrent()
}
progress.resignCurrent()
return
}
private func fallbackRemove(_ opType: FileOperationType, progress: Progress, on task: FileProviderStreamTask, completionHandler: SimpleCompletionHandler) {
let sourcePath = opType.source
self.execute(command: "SITE RMDIR \(ftpPath(sourcePath))", on: task) { (response, error) in
if let error = error {
progress.cancel()
self.dispatch_queue.async {
completionHandler?(error)
}
self.delegateNotify(opType, error: error)
return
}
guard let response = response else {
progress.cancel()
let error = self.throwError(sourcePath, code: URLError.badServerResponse)
self.dispatch_queue.async {
completionHandler?(error)
}
self.delegateNotify(opType, error: error)
return
}
if response.hasPrefix("50") {
self.fallbackRecursiveRemove(opType, progress: progress, on: task, completionHandler: completionHandler)
return
}
var error: Error?
if !response.hasPrefix("2") {
error = self.throwError(sourcePath, code: URLError.cannotRemoveFile)
}
self.dispatch_queue.async {
completionHandler?(error)
}
self.delegateNotify(opType, error: error)
}
}
private func fallbackRecursiveRemove(_ opType: FileOperationType, progress: Progress, on task: FileProviderStreamTask, completionHandler: SimpleCompletionHandler) {
let sourcePath = opType.source
_ = self.recursiveList(path: sourcePath, useMLST: true, completionHandler: { (contents, error) in
if let error = error {
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
return
}
let recursiveProgress = Progress(parent: progress, userInfo: nil)
recursiveProgress.totalUnitCount = Int64(contents.count)
let sortedContents = contents.sorted(by: {
$0.path.localizedStandardCompare($1.path) == .orderedDescending
})
var command = ""
for file in sortedContents {
command += (file.isDirectory ? "RMD \(self.ftpPath(file.path))" : "DELE \(self.ftpPath(file.path))") + "\r\n"
}
command += "RMD \(self.ftpPath(sourcePath))"
self.execute(command: command, on: task, completionHandler: { (response, error) in
recursiveProgress.completedUnitCount += 1
self.dispatch_queue.async {
completionHandler?(error)
self.delegateNotify(opType, error: error)
}
// TODO: Digest response
})
})
}
}
extension FTPFileProvider: FileProvider { }
+61 -52
View File
@@ -64,6 +64,7 @@ open class HTTPFileProvider: FileProviderBasicRemote, FileProviderOperations, Fi
}
fileprivate var _longpollSession: URLSession?
/// This session has extended timeout up to 10 minutes, suitable for monitoring.
internal var longpollSession: URLSession {
if _longpollSession == nil {
let config = URLSessionConfiguration.default
@@ -73,6 +74,14 @@ open class HTTPFileProvider: FileProviderBasicRemote, FileProviderOperations, Fi
return _longpollSession!
}
/**
This is parent initializer for subclasses. Using this method on `HTTPFileProvider` will fail as `type` is not implemented.
- Parameters:
- baseURL: Location of WebDAV server.
- credential: An `URLCredential` object with `user` and `password`.
- cache: A URLCache to cache downloaded files and contents.
*/
public init(baseURL: URL?, credential: URLCredential?, cache: URLCache?) {
self.baseURL = baseURL
self.currentPath = ""
@@ -164,57 +173,6 @@ open class HTTPFileProvider: FileProviderBasicRemote, FileProviderOperations, Fi
return doOperation(.remove(path: path), completionHandler: completionHandler)
}
internal func request(for operation: FileOperationType, overwrite: Bool = false, attributes: [URLResourceKey: Any] = [:]) -> URLRequest {
fatalError("HTTPFileProvider is an abstract class. Please implement \(#function) in subclass.")
}
internal func serverError(with code: FileProviderHTTPErrorCode, path: String?, data: Data?) -> FileProviderHTTPError {
fatalError("HTTPFileProvider is an abstract class. Please implement \(#function) in subclass.")
}
internal func multiStatusHandler(source: String, data: Data, completionHandler: SimpleCompletionHandler) -> Void {
// WebDAV will override this function
}
fileprivate func doOperation(_ operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> Progress? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: operation) ?? true == true else {
return nil
}
let progress = Progress(totalUnitCount: 1)
progress.setUserInfoObject(operation, forKey: .fileProvderOperationTypeKey)
progress.kind = .file
progress.setUserInfoObject(Progress.FileOperationKind.downloading, forKey: .fileOperationKindKey)
let request = self.request(for: operation)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var serverError: FileProviderHTTPError?
if let response = response as? HTTPURLResponse, response.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
serverError = self.serverError(with: code, path: operation.source, data: data)
}
if let response = response as? HTTPURLResponse, FileProviderHTTPErrorCode(rawValue: response.statusCode) == .multiStatus, let data = data {
self.multiStatusHandler(source: operation.source, data: data, completionHandler: completionHandler)
}
if serverError == nil && error == nil {
progress.completedUnitCount = 1
} else {
progress.cancel()
}
completionHandler?(serverError ?? error)
self.delegateNotify(operation, error: serverError ?? error)
})
task.taskDescription = operation.json
progress.cancellationHandler = { [weak task] in
task?.cancel()
}
progress.setUserInfoObject(Date(), forKey: .startingTimeKey)
task.resume()
return progress
}
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> Progress? {
// check file is not a folder
guard (try? localFile.resourceValues(forKeys: [.fileResourceTypeKey]))?.fileResourceType ?? .unknown == .regular else {
@@ -298,7 +256,58 @@ open class HTTPFileProvider: FileProviderBasicRemote, FileProviderOperations, Fi
let request = self.request(for: operation, overwrite: overwrite, attributes: [.contentModificationDateKey: Date()])
return upload_simple(path, request: request, data: data ?? Data(), operation: operation, completionHandler: completionHandler)
}
internal func request(for operation: FileOperationType, overwrite: Bool = false, attributes: [URLResourceKey: Any] = [:]) -> URLRequest {
fatalError("HTTPFileProvider is an abstract class. Please implement \(#function) in subclass.")
}
internal func serverError(with code: FileProviderHTTPErrorCode, path: String?, data: Data?) -> FileProviderHTTPError {
fatalError("HTTPFileProvider is an abstract class. Please implement \(#function) in subclass.")
}
internal func multiStatusHandler(source: String, data: Data, completionHandler: SimpleCompletionHandler) -> Void {
// WebDAV will override this function
}
fileprivate func doOperation(_ operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> Progress? {
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: operation) ?? true == true else {
return nil
}
let progress = Progress(totalUnitCount: 1)
progress.setUserInfoObject(operation, forKey: .fileProvderOperationTypeKey)
progress.kind = .file
progress.setUserInfoObject(Progress.FileOperationKind.downloading, forKey: .fileOperationKindKey)
let request = self.request(for: operation)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var serverError: FileProviderHTTPError?
if let response = response as? HTTPURLResponse, response.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
serverError = self.serverError(with: code, path: operation.source, data: data)
}
if let response = response as? HTTPURLResponse, FileProviderHTTPErrorCode(rawValue: response.statusCode) == .multiStatus, let data = data {
self.multiStatusHandler(source: operation.source, data: data, completionHandler: completionHandler)
}
if serverError == nil && error == nil {
progress.completedUnitCount = 1
} else {
progress.cancel()
}
completionHandler?(serverError ?? error)
self.delegateNotify(operation, error: serverError ?? error)
})
task.taskDescription = operation.json
progress.cancellationHandler = { [weak task] in
task?.cancel()
}
progress.setUserInfoObject(Date(), forKey: .startingTimeKey)
task.resume()
return progress
}
internal func upload_simple(_ targetPath: String, request: URLRequest, data: Data? = nil, localFile: URL? = nil, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> Progress? {
let size = data?.count ?? Int((try? localFile?.resourceValues(forKeys: [.fileSizeKey]))??.fileSize ?? -1)
+3 -5
View File
@@ -565,9 +565,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo
open func isRegisteredForNotification(path: String) -> Bool {
return monitors.map( { self.relativePathOf(url: $0.url) } ).contains(path.trimmingCharacters(in: CharacterSet(charactersIn: "/")))
}
}
public extension LocalFileProvider {
/**
Creates a symbolic link at the specified path that points to an item at the given path.
This method does not traverse symbolic links contained in destination path, making it possible
@@ -579,7 +577,7 @@ public extension LocalFileProvider {
- withDestinationPath: The path that contains the item to be pointed to by the link. In other words, this is the destination of the link.
- completionHandler: If an error parameter was provided, a presentable `Error` will be returned.
*/
public func create(symbolicLink path: String, withDestinationPath destPath: String, completionHandler: SimpleCompletionHandler) {
open func create(symbolicLink path: String, withDestinationPath destPath: String, completionHandler: SimpleCompletionHandler) {
operation_queue.addOperation {
do {
try self.opFileManager.createSymbolicLink(at: self.url(of: path), withDestinationURL: self.url(of: destPath))
@@ -601,7 +599,7 @@ public extension LocalFileProvider {
/// - Parameters:
/// - path: The path of a file or directory.
/// - completionHandler: Returns destination url of given symbolic link, or an `Error` object if it fails.
public func destination(ofSymbolicLink path: String, completionHandler: @escaping (_ url: URL?, _ error: Error?) -> Void) {
open func destination(ofSymbolicLink path: String, completionHandler: @escaping (_ url: URL?, _ error: Error?) -> Void) {
dispatch_queue.async {
do {
let destPath = try self.opFileManager.destinationOfSymbolicLink(atPath: self.url(of: path).path)
+2 -4
View File
@@ -17,7 +17,7 @@ import CoreGraphics
- Note: Uploading files and data are limited to 100MB, for now.
*/
open class OneDriveFileProvider: HTTPFileProvider {
open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
override open class var type: String { return "OneDrive" }
/// Drive name for user, default is `root`. Changing its value will effect on new operations.
open var drive: String
@@ -227,9 +227,7 @@ open class OneDriveFileProvider: HTTPFileProvider {
return super.upload_simple(targetPath, request: request, data: data, localFile: localFile, operation: operation, completionHandler: completionHandler)
}
}
extension OneDriveFileProvider {
fileprivate func registerNotifcation(path: String, eventHandler: (() -> Void)) {
/* There is two ways to monitor folders changing in OneDrive. Either using webooks
* which means you have to implement a server to translate it to push notifications
@@ -244,7 +242,7 @@ extension OneDriveFileProvider {
}
}
extension OneDriveFileProvider: FileProviderSharing {
extension OneDriveFileProvider {
open func publicLink(to path: String, completionHandler: @escaping ((_ link: URL?, _ attribute: FileObject?, _ expiration: Date?, _ error: Error?) -> Void)) {
var request = URLRequest(url: self.url(of: path, modifier: "action.createLink"))
request.httpMethod = "POST"
+33 -37
View File
@@ -20,7 +20,7 @@ import CoreGraphics
in case of using this class with unencrypted HTTP connection.
[Read this to know how](http://iosdevtips.co/post/121756573323/ios-9-xcode-7-http-connect-server-error).
*/
open class WebDAVFileProvider: HTTPFileProvider {
open class WebDAVFileProvider: HTTPFileProvider, FileProviderSharing {
override open class var type: String { return "WebDAV" }
public var credentialType: HTTPAuthenticationType = .digest
@@ -230,6 +230,38 @@ open class WebDAVFileProvider: HTTPFileProvider {
})
}
open func publicLink(to path: String, completionHandler: @escaping ((URL?, FileObject?, Date?, Error?) -> Void)) {
guard self.baseURL?.host?.contains("dav.yandex.") ?? false else {
dispatch_queue.async {
completionHandler(nil, nil, nil, self.throwError(path, code: URLError.resourceUnavailable))
}
return
}
let url = self.url(of: path)
var request = URLRequest(url: url)
request.httpMethod = "PROPPATCH"
request.set(httpAuthentication: credential, with: credentialType)
request.set(contentType: .xml)
let body = "<propertyupdate xmlns=\"DAV:\">\n<set><prop>\n<public_url xmlns=\"urn:yandex:disk:meta\">true</public_url>\n</prop></set>\n</propertyupdate>"
request.httpBody = body.data(using: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
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, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8), url: url)
}
if let data = data {
let xresponse = DavResponse.parse(xmlResponse: data, baseURL: self.baseURL)
if let urlStr = xresponse.first?.prop["public_url"], let url = URL(string: urlStr) {
completionHandler(url, nil, nil, nil)
return
}
}
completionHandler(nil, nil, nil, responseError ?? error)
})
}
override func request(for operation: FileOperationType, overwrite: Bool = true, attributes: [URLResourceKey: Any] = [:]) -> URLRequest {
let method: String
let url: URL
@@ -294,8 +326,6 @@ open class WebDAVFileProvider: HTTPFileProvider {
completionHandler?(error)
}
}
}
extension WebDAVFileProvider {
/*
fileprivate func registerNotifcation(path: String, eventHandler: (() -> Void)) {
@@ -359,40 +389,6 @@ extension WebDAVFileProvider: ExtendedFileProvider {
}
}
extension WebDAVFileProvider: FileProviderSharing {
open func publicLink(to path: String, completionHandler: @escaping ((URL?, FileObject?, Date?, Error?) -> Void)) {
guard self.baseURL?.host?.contains("dav.yandex.") ?? false else {
dispatch_queue.async {
completionHandler(nil, nil, nil, self.throwError(path, code: URLError.resourceUnavailable))
}
return
}
let url = self.url(of: path)
var request = URLRequest(url: url)
request.httpMethod = "PROPPATCH"
request.set(httpAuthentication: credential, with: credentialType)
request.set(contentType: .xml)
let body = "<propertyupdate xmlns=\"DAV:\">\n<set><prop>\n<public_url xmlns=\"urn:yandex:disk:meta\">true</public_url>\n</prop></set>\n</propertyupdate>"
request.httpBody = body.data(using: .utf8)
request.setValue(String(request.httpBody!.count), forHTTPHeaderField: "Content-Length")
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, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8), url: url)
}
if let data = data {
let xresponse = DavResponse.parse(xmlResponse: data, baseURL: self.baseURL)
if let urlStr = xresponse.first?.prop["public_url"], let url = URL(string: urlStr) {
completionHandler(url, nil, nil, nil)
return
}
}
completionHandler(nil, nil, nil, responseError ?? error)
})
}
}
// MARK: WEBDAV XML response implementation
struct DavResponse {