Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e9a82a288 | |||
| ea7e089718 | |||
| 19ea14ebc1 | |||
| 47885c0a4e | |||
| ac44aa190f | |||
| 50f6a393a0 | |||
| 779b38f381 | |||
| a374841883 | |||
| 0123d8b117 | |||
| fdc4bb818d | |||
| a08a9fe7a0 | |||
| 792ac6b015 |
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
|
||||
#
|
||||
|
||||
s.name = "FileProvider"
|
||||
s.version = "0.8.1"
|
||||
s.version = "0.8.3"
|
||||
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and macOS."
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
|
||||
@@ -92,6 +92,9 @@
|
||||
799396E01D48C02300086753 /* WebDAVFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A61D48C02300086753 /* WebDAVFileProvider.swift */; };
|
||||
799396E11D48C02300086753 /* WebDAVFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A61D48C02300086753 /* WebDAVFileProvider.swift */; };
|
||||
799396E21D48C02300086753 /* WebDAVFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A61D48C02300086753 /* WebDAVFileProvider.swift */; };
|
||||
79F5745B1DFDB10B00179ABF /* FileObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79F5745A1DFDB10A00179ABF /* FileObject.swift */; };
|
||||
79F5745C1DFDB10B00179ABF /* FileObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79F5745A1DFDB10A00179ABF /* FileObject.swift */; };
|
||||
79F5745D1DFDB10B00179ABF /* FileObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79F5745A1DFDB10A00179ABF /* FileObject.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -131,6 +134,7 @@
|
||||
799396A31D48C02300086753 /* SMB2Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2Types.swift; sourceTree = "<group>"; };
|
||||
799396A41D48C02300086753 /* SMBErrorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMBErrorType.swift; sourceTree = "<group>"; };
|
||||
799396A61D48C02300086753 /* WebDAVFileProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebDAVFileProvider.swift; sourceTree = "<group>"; };
|
||||
79F5745A1DFDB10A00179ABF /* FileObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileObject.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -218,6 +222,7 @@
|
||||
794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */,
|
||||
799396941D48C02300086753 /* FileProvider.h */,
|
||||
799396951D48C02300086753 /* FileProvider.swift */,
|
||||
79F5745A1DFDB10A00179ABF /* FileObject.swift */,
|
||||
799396961D48C02300086753 /* LocalFileProvider.swift */,
|
||||
792572401DF23BDA006A1526 /* LocalHelper.swift */,
|
||||
7902C0851D61B56D00564440 /* RemoteSession.swift */,
|
||||
@@ -402,6 +407,7 @@
|
||||
799396D41D48C02300086753 /* SMB2Tree.swift in Sources */,
|
||||
7924B1A21D89DAE000589DB7 /* Options.swift in Sources */,
|
||||
792572411DF23BDA006A1526 /* LocalHelper.swift in Sources */,
|
||||
79F5745B1DFDB10B00179ABF /* FileObject.swift in Sources */,
|
||||
7924B1991D89DAE000589DB7 /* Element.swift in Sources */,
|
||||
799396C81D48C02300086753 /* SMB2IOCtl.swift in Sources */,
|
||||
799396D71D48C02300086753 /* SMB2Types.swift in Sources */,
|
||||
@@ -437,6 +443,7 @@
|
||||
799396D51D48C02300086753 /* SMB2Tree.swift in Sources */,
|
||||
7924B1A31D89DAE000589DB7 /* Options.swift in Sources */,
|
||||
792572421DF23BDA006A1526 /* LocalHelper.swift in Sources */,
|
||||
79F5745C1DFDB10B00179ABF /* FileObject.swift in Sources */,
|
||||
7924B1B01D89F7DE00589DB7 /* FPSStreamTask.swift in Sources */,
|
||||
7924B19A1D89DAE000589DB7 /* Element.swift in Sources */,
|
||||
799396C91D48C02300086753 /* SMB2IOCtl.swift in Sources */,
|
||||
@@ -472,6 +479,7 @@
|
||||
799396D61D48C02300086753 /* SMB2Tree.swift in Sources */,
|
||||
7924B1A41D89DAE000589DB7 /* Options.swift in Sources */,
|
||||
792572431DF23BDA006A1526 /* LocalHelper.swift in Sources */,
|
||||
79F5745D1DFDB10B00179ABF /* FileObject.swift in Sources */,
|
||||
7924B1B11D89F7DF00589DB7 /* FPSStreamTask.swift in Sources */,
|
||||
7924B19B1D89DAE000589DB7 /* Element.swift in Sources */,
|
||||
799396CA1D48C02300086753 /* SMB2IOCtl.swift in Sources */,
|
||||
@@ -505,7 +513,7 @@
|
||||
799396601D48B7BF00086753 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_VERSION_STRING = 0.7.0;
|
||||
BUNDLE_VERSION_STRING = 0.8.3;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
@@ -528,14 +536,14 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SWIFT_VERSION = 3.0.1;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
799396611D48B7BF00086753 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_VERSION_STRING = 0.7.0;
|
||||
BUNDLE_VERSION_STRING = 0.8.3;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
@@ -556,7 +564,7 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 3.0.1;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -565,7 +573,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
BUNDLE_VERSION_STRING = 0.8.0;
|
||||
BUNDLE_VERSION_STRING = 0.8.2;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
@@ -618,7 +626,7 @@
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
BUNDLE_VERSION_STRING = 0.8.0;
|
||||
BUNDLE_VERSION_STRING = 0.8.2;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
|
||||
@@ -43,21 +43,26 @@ Legacy version is available in swift-2 branch
|
||||
|
||||
### Cocoapods / Carthage / Swift Package Manager
|
||||
|
||||
FileProvider supports both CocoaPods.
|
||||
|
||||
Add this line to your pods file:
|
||||
|
||||
```ruby
|
||||
pod "FileProvider"
|
||||
```
|
||||
|
||||
Or add this to cartfile:
|
||||
Or add this to Cartfile:
|
||||
|
||||
```
|
||||
github "amosavian/FileProvider"
|
||||
```
|
||||
|
||||
### Git
|
||||
Or to use in Swift Package Manager add this line in `Dependencies`:
|
||||
|
||||
```swift
|
||||
.Package(url: "https://github.com/amosavian/FileProvider.git", majorVersion: 0, minorVersion: 8)
|
||||
```
|
||||
|
||||
### Manually
|
||||
|
||||
To have latest updates with ease, use this command on terminal to get a clone:
|
||||
|
||||
```bash
|
||||
@@ -75,11 +80,11 @@ if you have a git based project, use this command in your projects directory to
|
||||
```bash
|
||||
git submodule add https://github.com/amosavian/FileProvider
|
||||
```
|
||||
Then you can do either:
|
||||
|
||||
### Manually
|
||||
**First way:** Copy Source folder to your project and Voila!
|
||||
* Copy Source folder to your project and Voila!
|
||||
|
||||
**Second way:** Drop FileProvider.xcodeproj to you Xcode workspace and add the framework to your Embeded Binaries in target.
|
||||
* Drop FileProvider.xcodeproj to you Xcode workspace and add the framework to your Embeded Binaries in target.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -112,7 +117,7 @@ let webdavProvider = WebDAVFileProvider(baseURL: URL(string: "http://www.example
|
||||
|
||||
* In case you want to connect non-secure servers for WebDAV (http) in iOS 9+ / macOS 10.11+ you should disable App Transport Security (ATS) according to [this guide.](https://gist.github.com/mlynch/284699d676fe9ed0abfa)
|
||||
|
||||
* For Dropbox, user is clientID and password is Token which both must be retrieved via [OAuth2 API of Dropbox](https://www.dropbox.com/developers/reference/oauth-guide). There are libraries like [p2/OAuth2](https://github.com/p2/OAuth2) or [OAuthSwift](https://github.com/OAuthSwift/OAuthSwift) which can facilate the procedure to retrieve token.
|
||||
* For Dropbox, user is clientID and password is Token which both must be retrieved via [OAuth2 API of Dropbox](https://www.dropbox.com/developers/reference/oauth-guide). There are libraries like [p2/OAuth2](https://github.com/p2/OAuth2) or [OAuthSwift](https://github.com/OAuthSwift/OAuthSwift) which can facilate the procedure to retrieve token. The latter is easier to use and prefered.
|
||||
|
||||
For interaction with UI, set delegate variable of `FileProvider` object
|
||||
|
||||
|
||||
@@ -118,11 +118,11 @@ extension DropboxFileProvider: FileProviderOperations {
|
||||
return self.writeContents(path: filePath, contents: data ?? Data(), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
public func moveItem(path: String, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return doOperation(.move(source: path, destination: toPath), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
public func copyItem(path: String, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return doOperation(.copy(source: path, destination: toPath), completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
@@ -174,17 +174,12 @@ extension DropboxFileProvider: FileProviderOperations {
|
||||
return RemoteOperationHandle(operationType: operation, tasks: [task])
|
||||
}
|
||||
|
||||
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
public func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
guard let data = try? Data(contentsOf: localFile) else {
|
||||
let error = throwError(localFile.absoluteString, code: URLError.fileDoesNotExist as FoundationErrorEnum)
|
||||
completionHandler?(error)
|
||||
return nil
|
||||
}
|
||||
return upload_simple(toPath, data: data, overwrite: true, operation: opType, completionHandler: completionHandler)
|
||||
return upload_simple(toPath, localFile: localFile, overwrite: overwrite, operation: opType, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func copyItem(path: String, toLocalURL destURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
@@ -221,10 +216,6 @@ extension DropboxFileProvider: FileProviderOperations {
|
||||
}
|
||||
|
||||
extension DropboxFileProvider: FileProviderReadWrite {
|
||||
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
return self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
let opType = FileOperationType.fetch(path: path)
|
||||
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
|
||||
@@ -251,13 +242,13 @@ extension DropboxFileProvider: FileProviderReadWrite {
|
||||
return RemoteOperationHandle(operationType: opType, tasks: [task])
|
||||
}
|
||||
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
let opType = FileOperationType.modify(path: path)
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
// FIXME: remove 150MB restriction
|
||||
return upload_simple(path, data: data, overwrite: true, operation: opType, completionHandler: completionHandler)
|
||||
return upload_simple(path, data: data, overwrite: overwrite, operation: opType, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
|
||||
|
||||
@@ -99,19 +99,17 @@ internal extension DropboxFileProvider {
|
||||
|
||||
func upload_simple(_ targetPath: String, data: Data, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
assert(data.count < 150*1024*1024, "Maximum size of allowed size to upload is 150MB")
|
||||
var requestDictionary = [String: AnyObject]()
|
||||
var requestDictionary = [String: Any]()
|
||||
let url: URL
|
||||
url = URL(string: "https://content.dropboxapi.com/2/files/upload")!
|
||||
requestDictionary["path"] = correctPath(targetPath) as NSString?
|
||||
requestDictionary["mode"] = (overwrite ? "overwrite" : "add") as NSString
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssz"
|
||||
requestDictionary["client_modified"] = dateFormatter.string(from: modifiedDate) as NSString
|
||||
requestDictionary["client_modified"] = string(from:modifiedDate)
|
||||
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(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
|
||||
request.setValue(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
|
||||
request.httpBody = data
|
||||
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderDropboxError?
|
||||
@@ -120,7 +118,32 @@ internal extension DropboxFileProvider {
|
||||
}
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
|
||||
})
|
||||
})
|
||||
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? {
|
||||
var requestDictionary = [String: Any]()
|
||||
let url: URL
|
||||
url = URL(string: "https://content.dropboxapi.com/2/files/upload")!
|
||||
requestDictionary["path"] = correctPath(targetPath) as NSString?
|
||||
requestDictionary["mode"] = (overwrite ? "overwrite" : "add") as NSString
|
||||
requestDictionary["client_modified"] = string(from:modifiedDate)
|
||||
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(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
|
||||
let task = session.uploadTask(with: request, fromFile: localFile, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderDropboxError?
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: String(data: data ?? Data(), encoding: .utf8))
|
||||
}
|
||||
completionHandler?(responseError ?? error)
|
||||
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
|
||||
})
|
||||
task.taskDescription = operation.json
|
||||
task.resume()
|
||||
return RemoteOperationHandle(operationType: operation, tasks: [task])
|
||||
@@ -180,7 +203,7 @@ internal extension DropboxFileProvider {
|
||||
fileObject.size = (json["size"] as? NSNumber)?.int64Value ?? -1
|
||||
fileObject.serverTime = resolve(dateString: json["server_modified"] as? String ?? "")
|
||||
fileObject.modifiedDate = resolve(dateString: json["client_modified"] as? String ?? "")
|
||||
fileObject.fileType = (json[".tag"] as? String) == "folder" ? .directory : .regular
|
||||
fileObject.type = (json[".tag"] as? String) == "folder" ? .directory : .regular
|
||||
fileObject.isReadOnly = (json["sharing_info"]?["read_only"] as? NSNumber)?.boolValue ?? false
|
||||
fileObject.id = json["id"] as? String
|
||||
fileObject.rev = json["id"] as? String
|
||||
|
||||
@@ -503,21 +503,7 @@ public protocol FPSStreamDelegate : URLSessionTaskDelegate {
|
||||
@objc optional func urlSession(_ session: URLSession, streamTask: FPSStreamTask, didBecome inputStream: InputStream, outputStream: OutputStream)
|
||||
}
|
||||
|
||||
private let ports = ["http": 80,
|
||||
"https": 443,
|
||||
"smb": 445,
|
||||
"ftp": 21,
|
||||
"sftp": 22,
|
||||
"sftp": 2121,
|
||||
"telnet": 23,
|
||||
"pop": 110,
|
||||
"smtp": 25,
|
||||
"imap": 143]
|
||||
private let securePorts = ["https": 443,
|
||||
"smb": 445,
|
||||
"sftp": 22,
|
||||
"sftp": 2121,
|
||||
"telnet": 992,
|
||||
"pop": 995,
|
||||
"smtp": 465,
|
||||
"imap": 993]
|
||||
private let ports: [String: Int] = ["http": 80, "https": 443, "smb": 445,"ftp": 21,"ftps": 22, "sftp": 2121,
|
||||
"telnet": 23, "pop": 110, "smtp": 25, "imap": 143]
|
||||
private let securePorts: [String: Int] = ["https": 443, "smb": 445, "ftps": 22, "sftp": 2121,
|
||||
"telnet": 992, "pop": 995, "smtp": 465, "imap": 993]
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
//
|
||||
// FileProvider.swift
|
||||
// FileProvider
|
||||
//
|
||||
// Created by Amir Abbas Mousavian.
|
||||
// Copyright © 2016 Mousavian. Distributed under MIT license.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Containts path and attributes of a file or resource.
|
||||
open class FileObject {
|
||||
open internal(set) var allValues: [String: Any]
|
||||
|
||||
internal init(allValues: [String: Any]) {
|
||||
self.allValues = allValues
|
||||
}
|
||||
|
||||
internal init(absoluteURL: URL? = nil, name: String, path: String) {
|
||||
self.allValues = [String: Any]()
|
||||
self.absoluteURL = absoluteURL
|
||||
self.name = name
|
||||
self.path = path
|
||||
}
|
||||
|
||||
/// url to access the resource, not supported by Dropbox provider
|
||||
open internal(set) var absoluteURL: URL? {
|
||||
get {
|
||||
return allValues["NSURLAbsoluteURLKey"] as? URL
|
||||
}
|
||||
set {
|
||||
allValues["NSURLAbsoluteURLKey"] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Name of the file, usually equals with the last path component
|
||||
open internal(set) var name: String {
|
||||
get {
|
||||
return allValues[URLResourceKey.nameKey.rawValue] as! String
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.nameKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Relative path of file object
|
||||
open internal(set) var path: String {
|
||||
get {
|
||||
return allValues[URLResourceKey.pathKey.rawValue] as! String
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.pathKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Size of file on disk, return -1 for directories.
|
||||
open internal(set) var size: Int64 {
|
||||
get {
|
||||
return allValues[URLResourceKey.fileSizeKey.rawValue] as? Int64 ?? -1
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.fileSizeKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// The time contents of file has been created, returns nil if not set
|
||||
open internal(set) var creationDate: Date? {
|
||||
get {
|
||||
return allValues[URLResourceKey.creationDateKey.rawValue] as? Date
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.creationDateKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// The time contents of file has been modified, returns nil if not set
|
||||
open internal(set) var modifiedDate: Date? {
|
||||
get {
|
||||
return allValues[URLResourceKey.contentModificationDateKey.rawValue] as? Date
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.contentModificationDateKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// return resource type of file, usually directory, regular or symLink
|
||||
open internal(set) var type: URLFileResourceType? {
|
||||
get {
|
||||
return allValues[URLResourceKey.fileResourceTypeKey.rawValue] as? URLFileResourceType
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.fileResourceTypeKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Use FileObject.type property instead.")
|
||||
open var fileType: URLFileResourceType? {
|
||||
return self.type
|
||||
}
|
||||
|
||||
/// File is hidden either because begining with dot or filesystem flags
|
||||
/// Setting this value on a file begining with dot has no effect
|
||||
open internal(set) var isHidden: Bool {
|
||||
get {
|
||||
return allValues[URLResourceKey.isHiddenKey.rawValue] as? Bool ?? false
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.isHiddenKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// File can not be written
|
||||
open internal(set) var isReadOnly: Bool {
|
||||
get {
|
||||
return !(allValues[URLResourceKey.isWritableKey.rawValue] as? Bool ?? true)
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.isWritableKey.rawValue] = !newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// File is a Directory
|
||||
open var isDirectory: Bool {
|
||||
return self.type == .directory
|
||||
}
|
||||
|
||||
/// File is a normal file
|
||||
open var isRegularFile: Bool {
|
||||
return self.type == .regular
|
||||
}
|
||||
|
||||
/// File is a Symbolic link
|
||||
open var isSymLink: Bool {
|
||||
return self.type == .symbolicLink
|
||||
}
|
||||
}
|
||||
|
||||
/// Sorting FileObject array by given criteria, not thread-safe
|
||||
public struct FileObjectSorting {
|
||||
|
||||
/// Determines sort kind by which item of File object
|
||||
public enum SortType {
|
||||
/// Sorting by default Finder (case-insensitive) behavior
|
||||
case name
|
||||
/// Sorting by case-sensitive form of file name
|
||||
case nameCaseSensitive
|
||||
/// Sorting by case-in sensitive form of file name
|
||||
case nameCaseInsensitive
|
||||
/// Sorting by file type
|
||||
case `extension`
|
||||
/// Sorting by file modified date
|
||||
case modifiedDate
|
||||
/// Sorting by file creation date
|
||||
case creationDate
|
||||
/// Sorting by file modified date
|
||||
case size
|
||||
|
||||
/// all sort types
|
||||
static var allItems: [SortType] {
|
||||
return [.name, .nameCaseSensitive, .nameCaseInsensitive, .extension,
|
||||
.modifiedDate,.creationDate, .size]
|
||||
}
|
||||
}
|
||||
|
||||
public let sortType: SortType
|
||||
/// puts A before Z, default is true
|
||||
public let ascending: Bool
|
||||
/// puts directories on top, regardless of other attributes, default is false
|
||||
public let isDirectoriesFirst: Bool
|
||||
|
||||
public static let nameAscending = FileObjectSorting(type: .name, ascending: true)
|
||||
public static let nameDesceding = FileObjectSorting(type: .name, ascending: false)
|
||||
public static let sizeAscending = FileObjectSorting(type: .size, ascending: true)
|
||||
public static let sizeDesceding = FileObjectSorting(type: .size, ascending: false)
|
||||
public static let extensionAscending = FileObjectSorting(type: .extension, ascending: true)
|
||||
public static let extensionDesceding = FileObjectSorting(type: .extension, ascending: false)
|
||||
public static let modifiedAscending = FileObjectSorting(type: .modifiedDate, ascending: true)
|
||||
public static let modifiedDesceding = FileObjectSorting(type: .modifiedDate, ascending: false)
|
||||
public static let createdAscending = FileObjectSorting(type: .creationDate, ascending: true)
|
||||
public static let createdDesceding = FileObjectSorting(type: .creationDate, ascending: false)
|
||||
|
||||
public init (type: SortType, ascending: Bool = true, isDirectoriesFirst: Bool = false) {
|
||||
self.sortType = type
|
||||
self.ascending = ascending
|
||||
self.isDirectoriesFirst = isDirectoriesFirst
|
||||
}
|
||||
|
||||
/// Sorts array of FileObjects by criterias set in properties
|
||||
public func sort(_ files: [FileObject]) -> [FileObject] {
|
||||
return files.sorted {
|
||||
if isDirectoriesFirst {
|
||||
if ($0.isDirectory) && !($1.isDirectory) {
|
||||
return true
|
||||
}
|
||||
if !($0.isDirectory) && ($1.isDirectory) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
switch sortType {
|
||||
case .name:
|
||||
return ($0.name).localizedStandardCompare($1.name) == (ascending ? .orderedAscending : .orderedDescending)
|
||||
case .nameCaseSensitive:
|
||||
return ($0.name).localizedCompare($1.name) == (ascending ? .orderedAscending : .orderedDescending)
|
||||
case .nameCaseInsensitive:
|
||||
return ($0.name).localizedCaseInsensitiveCompare($1.name) == (ascending ? .orderedAscending : .orderedDescending)
|
||||
case .extension:
|
||||
let kind1 = $0.isDirectory ? "folder" : ($0.path as NSString).pathExtension
|
||||
let kind2 = $1.isDirectory ? "folder" : ($1.path as NSString).pathExtension
|
||||
return kind1.localizedCaseInsensitiveCompare(kind2) == (ascending ? .orderedAscending : .orderedDescending)
|
||||
case .modifiedDate:
|
||||
let fileMod1 = $0.modifiedDate ?? Date.distantPast
|
||||
let fileMod2 = $1.modifiedDate ?? Date.distantPast
|
||||
return ascending ? fileMod1 < fileMod2 : fileMod1 > fileMod2
|
||||
case .creationDate:
|
||||
let fileCreation1 = $0.creationDate ?? Date.distantPast
|
||||
let fileCreation2 = $1.creationDate ?? Date.distantPast
|
||||
return ascending ? fileCreation1 < fileCreation2 : fileCreation1 > fileCreation2
|
||||
case .size:
|
||||
return ascending ? $0.size < $1.size : $0.size > $1.size
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension URLFileResourceType {
|
||||
public init(fileTypeValue: FileAttributeType) {
|
||||
switch fileTypeValue {
|
||||
case FileAttributeType.typeCharacterSpecial: self = .characterSpecial
|
||||
case FileAttributeType.typeDirectory: self = .directory
|
||||
case FileAttributeType.typeBlockSpecial: self = .blockSpecial
|
||||
case FileAttributeType.typeRegular: self = .regular
|
||||
case FileAttributeType.typeSymbolicLink: self = .symbolicLink
|
||||
case FileAttributeType.typeSocket: self = .socket
|
||||
case FileAttributeType.typeUnknown: self = .unknown
|
||||
default: self = .unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal extension URL {
|
||||
var uw_scheme: String {
|
||||
return self.scheme ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
internal func jsonToDictionary(_ jsonString: String) -> [String: AnyObject]? {
|
||||
guard let data = jsonString.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
if let dic = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? [String: AnyObject] {
|
||||
return dic
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
internal func dictionaryToJSON(_ dictionary: [String: AnyObject]) -> String? {
|
||||
if let data = try? JSONSerialization.data(withJSONObject: dictionary, options: JSONSerialization.WritingOptions()) {
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+61
-147
@@ -98,29 +98,80 @@ public protocol FileProviderOperations: FileProviderBasic {
|
||||
@discardableResult
|
||||
func create(file: String, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func moveItem(path: String, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func moveItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func copyItem(path: String, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func copyItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
|
||||
@discardableResult
|
||||
func copyItem(localFile: URL, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func copyItem(localFile: URL, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
}
|
||||
|
||||
extension FileProviderOperations {
|
||||
@discardableResult
|
||||
public func moveItem(path: String, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return self.moveItem(path: path, to: to, overwrite: false, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func copyItem(localFile: URL, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return self.copyItem(localFile: localFile, to: to, overwrite: false, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func copyItem(path: String, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return self.copyItem(path: path, to: to, overwrite: false, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol FileProviderReadWrite: FileProviderBasic {
|
||||
@discardableResult
|
||||
func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle?
|
||||
@discardableResult
|
||||
func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle?
|
||||
|
||||
@discardableResult
|
||||
func writeContents(path: String, contents: Data, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func writeContents(path: String, contents: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func writeContents(path: String, contents: Data, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
@discardableResult
|
||||
func writeContents(path: String, contents: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
|
||||
|
||||
func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void))
|
||||
}
|
||||
|
||||
extension FileProviderReadWrite {
|
||||
@discardableResult
|
||||
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle?{
|
||||
return self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func writeContents(path: String, contents: Data, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return self.writeContents(path: path, contents: contents, atomically: false, overwrite: false, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func writeContents(path: String, contents: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return self.writeContents(path: path, contents: contents, atomically: atomically, overwrite: false, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func writeContents(path: String, contents: Data, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
return self.writeContents(path: path, contents: contents, atomically: false, overwrite: overwrite, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol FileProviderMonitor: FileProviderBasic {
|
||||
func registerNotifcation(path: String, eventHandler: @escaping (() -> Void))
|
||||
func unregisterNotifcation(path: String)
|
||||
@@ -246,6 +297,15 @@ extension FileProviderBasic {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func string(from date:Date) -> String {
|
||||
let fm = DateFormatter()
|
||||
fm.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
|
||||
fm.timeZone = TimeZone(identifier:"UTC")
|
||||
fm.locale = Locale(identifier:"en_US_POSIX")
|
||||
return fm.string(from:date)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public protocol ExtendedFileProvider: FileProvider {
|
||||
@@ -300,113 +360,6 @@ public enum FileOperationType: CustomStringConvertible {
|
||||
}
|
||||
}
|
||||
|
||||
open class FileObject {
|
||||
open internal(set) var allValues: [String: Any]
|
||||
|
||||
internal init(allValues: [String: Any]) {
|
||||
self.allValues = allValues
|
||||
}
|
||||
|
||||
internal init(absoluteURL: URL? = nil, name: String, path: String) {
|
||||
self.allValues = [String: Any]()
|
||||
self.absoluteURL = absoluteURL
|
||||
self.name = name
|
||||
self.path = path
|
||||
}
|
||||
|
||||
open internal(set) var absoluteURL: URL? {
|
||||
get {
|
||||
return allValues["NSURLAbsoluteURLKey"] as? URL
|
||||
}
|
||||
set {
|
||||
allValues["NSURLAbsoluteURLKey"] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
open internal(set) var name: String {
|
||||
get {
|
||||
return allValues[URLResourceKey.nameKey.rawValue] as! String
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.nameKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
open internal(set) var path: String {
|
||||
get {
|
||||
return allValues[URLResourceKey.pathKey.rawValue] as! String
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.pathKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
open internal(set) var size: Int64 {
|
||||
get {
|
||||
return allValues[URLResourceKey.fileSizeKey.rawValue] as? Int64 ?? -1
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.fileSizeKey.rawValue] = Int(exactly: newValue) ?? Int.max
|
||||
}
|
||||
}
|
||||
|
||||
open internal(set) var creationDate: Date? {
|
||||
get {
|
||||
return allValues[URLResourceKey.creationDateKey.rawValue] as? Date
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.creationDateKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
open internal(set) var modifiedDate: Date? {
|
||||
get {
|
||||
return allValues[URLResourceKey.contentModificationDateKey.rawValue] as? Date
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.contentModificationDateKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
open internal(set) var fileType: URLFileResourceType? {
|
||||
get {
|
||||
return allValues[URLResourceKey.fileResourceTypeKey.rawValue] as? URLFileResourceType
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.fileResourceTypeKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
open internal(set) var isHidden: Bool {
|
||||
get {
|
||||
return allValues[URLResourceKey.isHiddenKey.rawValue] as? Bool ?? false
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.isHiddenKey.rawValue] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
open internal(set) var isReadOnly: Bool {
|
||||
get {
|
||||
return !(allValues[URLResourceKey.isWritableKey.rawValue] as? Bool ?? true)
|
||||
}
|
||||
set {
|
||||
allValues[URLResourceKey.isWritableKey.rawValue] = !newValue
|
||||
}
|
||||
}
|
||||
|
||||
open var isDirectory: Bool {
|
||||
return self.fileType == .directory
|
||||
}
|
||||
|
||||
open var isRegularFile: Bool {
|
||||
return self.fileType == .regular
|
||||
}
|
||||
|
||||
open var isSymLink: Bool {
|
||||
return self.fileType == .symbolicLink
|
||||
}
|
||||
}
|
||||
|
||||
public protocol OperationHandle {
|
||||
var operationType: FileOperationType { get }
|
||||
@@ -440,13 +393,6 @@ public protocol FileOperationDelegate: class {
|
||||
func fileProvider(_ fileProvider: FileProviderOperations, shouldProceedAfterError error: Error, operation: FileOperationType) -> Bool
|
||||
}
|
||||
|
||||
// THESE ARE METHODS TO PROVIDE COMPATIBILITY WITH SWIFT 2.3 SIMOULTANIOUSLY!
|
||||
internal extension URL {
|
||||
var uw_scheme: String {
|
||||
return self.scheme ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
internal class Weak<T: AnyObject> {
|
||||
weak var value : T?
|
||||
init (_ value: T) {
|
||||
@@ -454,21 +400,6 @@ internal class Weak<T: AnyObject> {
|
||||
}
|
||||
}
|
||||
|
||||
extension URLFileResourceType {
|
||||
public init(fileTypeValue: FileAttributeType) {
|
||||
switch fileTypeValue {
|
||||
case FileAttributeType.typeCharacterSpecial: self = .characterSpecial
|
||||
case FileAttributeType.typeDirectory: self = .directory
|
||||
case FileAttributeType.typeBlockSpecial: self = .blockSpecial
|
||||
case FileAttributeType.typeRegular: self = .regular
|
||||
case FileAttributeType.typeSymbolicLink: self = .symbolicLink
|
||||
case FileAttributeType.typeSocket: self = .socket
|
||||
case FileAttributeType.typeUnknown: self = .unknown
|
||||
default: self = .unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol FoundationErrorEnum {
|
||||
init? (rawValue: Int)
|
||||
var rawValue: Int { get }
|
||||
@@ -476,20 +407,3 @@ public protocol FoundationErrorEnum {
|
||||
|
||||
extension URLError.Code: FoundationErrorEnum {}
|
||||
extension CocoaError.Code: FoundationErrorEnum {}
|
||||
|
||||
internal func jsonToDictionary(_ jsonString: String) -> [String: AnyObject]? {
|
||||
guard let data = jsonString.data(using: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
if let dic = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? [String: AnyObject] {
|
||||
return dic
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
internal func dictionaryToJSON(_ dictionary: [String: AnyObject]) -> String? {
|
||||
if let data = try? JSONSerialization.data(withJSONObject: dictionary, options: JSONSerialization.WritingOptions()) {
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -182,7 +182,8 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
// TODO: Make use of overwrite parameter
|
||||
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
|
||||
operation_queue.async {
|
||||
do {
|
||||
@@ -232,6 +233,9 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
|
||||
@discardableResult
|
||||
open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
if length < 0 {
|
||||
return self.contents(path: path, completionHandler: completionHandler)
|
||||
}
|
||||
let opType = FileOperationType.fetch(path: path)
|
||||
dispatch_queue.async {
|
||||
let aPath = self.absoluteURL(path).path
|
||||
@@ -255,8 +259,15 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
let opType = FileOperationType.modify(path: path)
|
||||
var options: Data.WritingOptions = []
|
||||
if atomically {
|
||||
options.insert(.atomic)
|
||||
}
|
||||
if overwrite {
|
||||
options.insert(.withoutOverwriting)
|
||||
}
|
||||
operation_queue.async {
|
||||
try? data.write(to: self.absoluteURL(path), options: atomically ? [.atomic] : [])
|
||||
DispatchQueue.main.async(execute: {
|
||||
|
||||
@@ -201,11 +201,11 @@ public enum FileProviderHTTPErrorCode: Int {
|
||||
case notExtended = 510
|
||||
case networkAuthenticationRequired = 511
|
||||
|
||||
fileprivate static let status1xx = [100: "Continue", 101: "Switching Protocols", 102: "Processing"]
|
||||
fileprivate static let status2xx = [200: "OK", 201: "Created", 202: "Accepted", 203: "Non-Authoritative Information", 204: "No Content", 205: "Reset Content", 206: "Partial Content", 207: "Multi-Status", 208: "Already Reported", 226: "IM Used"]
|
||||
fileprivate static let status3xx = [300: "Multiple Choices", 301: "Moved Permanently", 302: "Found", 303: "See Other", 304: "Not Modified", 305: "Use Proxy", 306: "Switch Proxy", 307: "Temporary Redirect", 308: "Permanent Redirect"]
|
||||
fileprivate static let status4xx = [400: "Bad Request", 401: "Unauthorized/Expired Session", 402: "Payment Required", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable", 407: "Proxy Authentication Required", 408: "Request Timeout", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Payload Too Large", 414: "URI Too Long", 415: "Unsupported Media Type", 416: "Range Not Satisfiable", 417: "Expectation Failed", 421: "Misdirected Request", 422: "Unprocessable Entity", 423: "Locked", 424: "Failed Dependency", 425: "Unordered Collection", 426: "Upgrade Required", 428: "Precondition Required", 429: "Too Many Requests", 431: "Request Header Fields Too Large", 451: "Unavailable For Legal Reasons"]
|
||||
fileprivate static let status5xx = [500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout", 505: "HTTP Version Not Supported", 506: "Variant Also Negotiates", 507: "Insufficient Storage", 508: "Loop Detected", 509: "Bandwidth Limit Exceeded", 510: "Not Extended", 511: "Network Authentication Required"]
|
||||
fileprivate static let status1xx: [Int: String] = [100: "Continue", 101: "Switching Protocols", 102: "Processing"]
|
||||
fileprivate static let status2xx: [Int: String] = [200: "OK", 201: "Created", 202: "Accepted", 203: "Non-Authoritative Information", 204: "No Content", 205: "Reset Content", 206: "Partial Content", 207: "Multi-Status", 208: "Already Reported", 226: "IM Used"]
|
||||
fileprivate static let status3xx: [Int: String] = [300: "Multiple Choices", 301: "Moved Permanently", 302: "Found", 303: "See Other", 304: "Not Modified", 305: "Use Proxy", 306: "Switch Proxy", 307: "Temporary Redirect", 308: "Permanent Redirect"]
|
||||
fileprivate static let status4xx: [Int: String] = [400: "Bad Request", 401: "Unauthorized/Expired Session", 402: "Payment Required", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable", 407: "Proxy Authentication Required", 408: "Request Timeout", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Payload Too Large", 414: "URI Too Long", 415: "Unsupported Media Type", 416: "Range Not Satisfiable", 417: "Expectation Failed", 421: "Misdirected Request", 422: "Unprocessable Entity", 423: "Locked", 424: "Failed Dependency", 425: "Unordered Collection", 426: "Upgrade Required", 428: "Precondition Required", 429: "Too Many Requests", 431: "Request Header Fields Too Large", 451: "Unavailable For Legal Reasons"]
|
||||
fileprivate static let status5xx: [Int: String] = [500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout", 505: "HTTP Version Not Supported", 506: "Variant Also Negotiates", 507: "Insufficient Storage", 508: "Loop Detected", 509: "Bandwidth Limit Exceeded", 510: "Not Extended", 511: "Network Authentication Required"]
|
||||
|
||||
public var description: String {
|
||||
switch self.rawValue {
|
||||
|
||||
@@ -68,7 +68,7 @@ class SMBFileProvider: FileProvider, FileProviderMonitor {
|
||||
return nil
|
||||
}
|
||||
|
||||
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
@@ -88,7 +88,7 @@ class SMBFileProvider: FileProvider, FileProviderMonitor {
|
||||
return nil
|
||||
}
|
||||
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
open func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
NotImplemented()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -66,3 +66,32 @@ struct SMBTime {
|
||||
return Date(timeIntervalSince1970: Double(self.time) / 10000000 - 11644473600)
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
init<T>(value: T) {
|
||||
var value = value
|
||||
self = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
|
||||
}
|
||||
|
||||
func scanValue<T>() -> T? {
|
||||
guard MemoryLayout<T>.size <= self.count else { return nil }
|
||||
return self.withUnsafeBytes { $0.pointee }
|
||||
}
|
||||
|
||||
func scanValue<T>(start: Int) -> T? {
|
||||
let length = MemoryLayout<T>.size
|
||||
guard self.count >= start + length else { return nil }
|
||||
return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
|
||||
}
|
||||
|
||||
func scanString(start: Int = 0, length: Int, encoding: String.Encoding) -> String? {
|
||||
guard self.count >= start + length else { return nil }
|
||||
return String(data: self.subdata(in: start..<start+length), encoding: encoding)
|
||||
}
|
||||
|
||||
static func mapMemory<T, U>(from: T) -> U? {
|
||||
guard MemoryLayout<T>.size >= MemoryLayout<U>.size else { return nil }
|
||||
let data = Data(value: from)
|
||||
return data.scanValue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,35 +8,6 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Data {
|
||||
init<T>(value: T) {
|
||||
var value = value
|
||||
self = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
|
||||
}
|
||||
|
||||
func scanValue<T>() -> T? {
|
||||
guard MemoryLayout<T>.size <= self.count else { return nil }
|
||||
return self.withUnsafeBytes { $0.pointee }
|
||||
}
|
||||
|
||||
func scanValue<T>(start: Int) -> T? {
|
||||
let length = MemoryLayout<T>.size
|
||||
guard self.count >= start + length else { return nil }
|
||||
return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
|
||||
}
|
||||
|
||||
func scanString(start: Int = 0, length: Int, encoding: String.Encoding) -> String? {
|
||||
guard self.count >= start + length else { return nil }
|
||||
return String(data: self.subdata(in: start..<start+length), encoding: encoding)
|
||||
}
|
||||
|
||||
static func mapMemory<T, U>(from: T) -> U? {
|
||||
guard MemoryLayout<T>.size >= MemoryLayout<U>.size else { return nil }
|
||||
let data = Data(value: from)
|
||||
return data.scanValue()
|
||||
}
|
||||
}
|
||||
|
||||
protocol FileProviderSMBHeader {
|
||||
var protocolID: UInt32 { get }
|
||||
static var protocolConst: UInt32 { get }
|
||||
|
||||
@@ -280,7 +280,8 @@ extension WebDAVFileProvider: FileProviderOperations {
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
public func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
// TODO: Make use of overwrite parameter
|
||||
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
|
||||
return nil
|
||||
@@ -332,11 +333,6 @@ extension WebDAVFileProvider: FileProviderOperations {
|
||||
}
|
||||
|
||||
extension WebDAVFileProvider: FileProviderReadWrite {
|
||||
@discardableResult
|
||||
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
return self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
|
||||
let opType = FileOperationType.fetch(path: path)
|
||||
@@ -360,7 +356,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
public func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
|
||||
let opType = FileOperationType.modify(path: path)
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
|
||||
return nil
|
||||
@@ -369,6 +365,9 @@ extension WebDAVFileProvider: FileProviderReadWrite {
|
||||
let url = atomically ? absoluteURL(path).appendingPathExtension("tmp") : absoluteURL(path)
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PUT"
|
||||
if !overwrite {
|
||||
request.setValue("F", forHTTPHeaderField: "Overwrite")
|
||||
}
|
||||
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
|
||||
var responseError: FileProviderWebDavError?
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
@@ -540,7 +539,7 @@ internal extension WebDAVFileProvider {
|
||||
fileObject.creationDate = self.resolve(dateString: davResponse.prop["creationdate"] ?? "")
|
||||
fileObject.modifiedDate = self.resolve(dateString: davResponse.prop["getlastmodified"] ?? "")
|
||||
fileObject.contentType = davResponse.prop["getcontenttype"] ?? "octet/stream"
|
||||
fileObject.fileType = fileObject.contentType == "httpd/unix-directory" ? .directory : .regular
|
||||
fileObject.type = fileObject.contentType == "httpd/unix-directory" ? .directory : .regular
|
||||
fileObject.entryTag = davResponse.prop["getetag"]
|
||||
return fileObject
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user