Compare commits

...

5 Commits

Author SHA1 Message Date
Amir Abbas Mousavian 940c7c1028 Dropbox implementation completed and getting storage size, used 2016-08-04 23:06:24 +04:30
Amir Abbas Mousavian b4ace7e680 Unified HTTP based services (WebDAV/Dropbox) Error Handling
- DropboxFileProvider.contentsOfDirectory() method implemented
2016-08-03 13:40:12 +04:30
Amir Abbas Mousavian eccbeb7174 Fixed WebDAV connection error for Apache servers 2016-08-02 14:59:32 +04:30
Amir Abbas Mousavian a077d000bc Updated Readme 2016-07-31 01:27:13 +04:30
Amir Abbas Mousavian 6a3ea633bf WebDAV file protocol conformance to FileProvider 2016-07-29 10:57:18 +04:30
10 changed files with 505 additions and 168 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.3.2"
s.version = "0.4.0"
s.summary = "NSFileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and MacOS."
# This description is used to generate tags and improve search results.
+8 -5
View File
@@ -170,16 +170,16 @@
isa = PBXGroup;
children = (
7993969A1D48C02300086753 /* CIFSTypes.swift */,
799396A41D48C02300086753 /* SMBErrorType.swift */,
7993969B1D48C02300086753 /* SMB2DataTypes.swift */,
7993969C1D48C02300086753 /* SMB2FileHandle.swift */,
799396A31D48C02300086753 /* SMB2Types.swift */,
799396A21D48C02300086753 /* SMB2Tree.swift */,
799396A01D48C02300086753 /* SMB2Session.swift */,
7993969D1D48C02300086753 /* SMB2FileOperation.swift */,
7993969C1D48C02300086753 /* SMB2FileHandle.swift */,
7993969E1D48C02300086753 /* SMB2IOCtl.swift */,
7993969F1D48C02300086753 /* SMB2Query.swift */,
799396A01D48C02300086753 /* SMB2Session.swift */,
799396A11D48C02300086753 /* SMB2SetInfo.swift */,
799396A21D48C02300086753 /* SMB2Tree.swift */,
799396A31D48C02300086753 /* SMB2Types.swift */,
799396A41D48C02300086753 /* SMBErrorType.swift */,
);
path = SMBTypes;
sourceTree = "<group>";
@@ -778,6 +778,7 @@
7993966E1D48B7F600086753 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7993967A1D48B80D00086753 /* Build configuration list for PBXNativeTarget "FileProvider OSX" */ = {
isa = XCConfigurationList;
@@ -786,6 +787,7 @@
7993967C1D48B80D00086753 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
799396871D48B82700086753 /* Build configuration list for PBXNativeTarget "FileProvider tvOS" */ = {
isa = XCConfigurationList;
@@ -794,6 +796,7 @@
799396891D48B82700086753 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
+31 -12
View File
@@ -23,14 +23,14 @@ Local and WebDAV providers are fully tested and can be used in production enviro
- [x] **LocalFileProvider** a wrapper around `NSFileManager` with some additions like searching and reading a portion of file.
- [x] **WebDAVFileProvider** WebDAV protocol is usual file transmission system on Macs.
- [x] **DropboxFileProvider** *implemented but not tested*
- [ ] **SMBFileProvider** SMB/CIFS and SMB2/3 are file and printer sharing protocol which is originated from IBM & Microsoft and SMB2/3 is now replacing AFP protocol on MacOS. I implemented data types and some basic functions but *main interface is not implemented yet!*
- [ ] **DropboxFileProvider** *partially implemented*
- [ ] **FTPFileProvider**
- [ ] **AmazonS3FileProvider**
## Requirements
- **Swift 2.2**
- **Swift 2.2 or 2.3**
- iOS 8.0 , OSX 10.10
- XCode 7.3
@@ -81,7 +81,9 @@ You can't change the base url later. and all paths are related to this base url
For remote file providers authentication may be necessary:
let credential = NSURLCredential(user: "user", password: "pass", persistence: NSURLCredentialPersistence.Permanent)
let webdavProvider = WebDAVFileProvider(baseURL: "http://www.example.com/dav", credential: credential)
let webdavProvider = WebDAVFileProvider(baseURL: NSURL(string: "http://www.example.com/dav")!, credential: credential)
* 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.
@@ -101,7 +103,7 @@ Your class should conforms `FileProviderDelegate` class:
documentsProvider.delegate = self
}
func fileproviderSucceed(fileProvider: FileProvider, operation: FileOperation) {
func fileproviderSucceed(fileProvider: FileProviderOperations, operation: FileOperation) {
switch operation {
case .Copy(source: let source, destination: let dest):
NSLog("\(source) copied to \(dest).")
@@ -112,7 +114,7 @@ Your class should conforms `FileProviderDelegate` class:
}
}
func fileproviderFailed(fileProvider: FileProvider, operation: FileOperation) {
func fileproviderFailed(fileProvider: FileProviderOperations, operation: FileOperation) {
switch operation {
case .Copy(source: let source, destination: let dest):
NSLog("copy of \(source) failed.")
@@ -123,7 +125,7 @@ Your class should conforms `FileProviderDelegate` class:
}
}
func fileproviderProgress(fileProvider: FileProvider, operation: FileOperation, progress: Float) {
func fileproviderProgress(fileProvider: FileProviderOperations, operation: FileOperation, progress: Float) {
switch operation {
case .Copy(source: let source, destination: let dest):
NSLog("Copy\(source) to \(dest): \(progress * 100) completed.")
@@ -153,26 +155,36 @@ There is a `FileObject` class which holds file attributes like size and creation
For a single file:
documentsProvider.attributesOfItemAtPath(path: "/file.txt", completionHandler: {
(attributes: LocalFileObject?, error: ErrorType?) -> Void} in
(attributes: LocalFileObject?, error: ErrorType?) -> Void in
if let attributes = attributes {
print("File Size: \(attributes.size)")
print("Creation Date: \(attributes.createdDate)")
print("Modification Date: \(modifiedDate)")
print("Is Read Only: \(isReadOnly)")
}
)
})
To get list of files in a directory:
documentsProvider.contentsOfDirectoryAtPath(path: "/", completionHandler: {
(contents: [LocalFileObject], error: ErrorType?) -> Void} in
(contents: [LocalFileObject], error: ErrorType?) -> Void in
for file in contents {
print("Name: \(attributes.name)")
print("Size: \(attributes.size)")
print("Creation Date: \(attributes.createdDate)")
print("Modification Date: \(modifiedDate)")
}
)
})
To get size of strage and used/free space:
func storageProperties(completionHandler: {(total: Int64, used: Int64) -> Void in
print("Total Storage Space: \(total)")
print("Used Space: \(used)")
print("Free Space: \(total - frees)")
})
* if this function is unavailable on provider or an error has been occurred, total space will be reported "-1" and used space "0"
### Change current directory
@@ -211,7 +223,7 @@ Move file old.txt to new.txt in current path:
### Retrieve Content of File
THere is two method for this purpose, one of them loads entire file into NSData and another can load a portion of file.
There is two method for this purpose, one of them loads entire file into NSData and another can load a portion of file.
documentsProvider.contentsAtPath(path: "old.txt", completionHandler: {
(contents: NSData?, error: ErrorType?) -> Void
@@ -252,6 +264,13 @@ You can monitor updates in some file system (Local and SMB2), there is three met
We would love for you to contribute to **FileProvider**, check the `LICENSE` file for more info.
## Projects in use
* [EDM - Browse and Receive Files](https://itunes.apple.com/us/app/edm-browse-and-receive-files/id948397575?ls=1&mt=8)
* [File Manager - PDF Reader & Music Player](https://itunes.apple.com/us/app/file-manager-pdf-reader-music/id1017809685?ls=1&mt=8)
If you used this library in your project, you can open an issue to inform us.
## Meta
Amir-Abbas Mousavian [@amosavian](https://twitter.com/amosavian)
@@ -260,7 +279,7 @@ Distributed under the MIT license. See `LICENSE` for more information.
[https://github.com/yourname/github-link](https://github.com/dbader/)
[swift-image]:https://img.shields.io/badge/swift-2.2-green.svg
[swift-image]:https://img.shields.io/badge/swift-2.2%2C%202.3-green.svg
[swift-url]: https://swift.org/
[license-image]: https://img.shields.io/badge/License-MIT-blue.svg
[license-url]: LICENSE
+201 -74
View File
@@ -8,30 +8,12 @@
import Foundation
public enum FileProviderDropboxErrorCode: Int {
case BadInputParameter = 400
case ExpiredToken = 401
case Forbidden = 403
case Endpoint = 409
case TooManyRequests = 429
case InternalServer = 500
case BadGateway = 502
}
public struct FileProviderDropboxError: ErrorType, CustomStringConvertible {
public let code: FileProviderDropboxErrorCode
public let code: FileProviderHTTPErrorCode
public let path: String
public var description: String {
switch code {
case .BadInputParameter: return "Bad input parameter."
case .ExpiredToken: return "Bad or expired token. To fix this, you should re-authenticate the user."
case .Forbidden: return "Forbidden."
case .Endpoint: return "Endpoint-specific error."
case .TooManyRequests: return "Your app is making too many requests"
case .InternalServer: return "An error occurred on the Dropbox servers."
case .BadGateway: return "An error occurred on the Dropbox servers."
}
return code.description
}
}
@@ -89,11 +71,13 @@ public class DropboxFileProvider: NSObject, FileProviderBasic {
}
public func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObject], error: ErrorType?) -> Void)) {
NotImplemented()
list(path) { (contents, cursor, error) in
completionHandler(contents: contents, error: error)
}
}
public func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void)) {
let url = NSURL(string: "https://api.dropboxapi.com/2/files/list_revisions")!
let url = NSURL(string: "https://api.dropboxapi.com/2/files/get_metadata")!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
@@ -105,14 +89,11 @@ public class DropboxFileProvider: NSObject, FileProviderBasic {
defer {
self.delegateNotify(FileOperation.Create(path: path), error: error)
}
let code = FileProviderDropboxErrorCode(rawValue: response.statusCode)
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path) : nil
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) {
let json = self.jsonToDictionary(jsonStr)
if (json?["is_deleted"] as? NSNumber)?.boolValue ?? false, let entries = json?["entries"] as? [AnyObject] where entries.count > 0 , let entry = entries[0] as? [String: AnyObject], let file = self.mapToFileObject(entry) {
completionHandler(attributes: file, error: dbError)
return
}
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding), let json = self.jsonToDictionary(jsonStr), let file = self.mapToFileObject(json) {
completionHandler(attributes: file, error: dbError)
return
}
completionHandler(attributes: nil, error: dbError)
return
@@ -122,6 +103,23 @@ public class DropboxFileProvider: NSObject, FileProviderBasic {
task.resume()
}
public func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void)) {
let url = NSURL(string: "https://api.dropboxapi.com/2/users/get_space_usage")!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding), let json = self.jsonToDictionary(jsonStr) {
let totalSize = ((json["allocation"] as? NSDictionary)?["allocated"] as? NSNumber)?.longLongValue ?? -1
let usedSize = (json["used"] as? NSNumber)?.longLongValue ?? 0
completionHandler(total: totalSize, used: usedSize)
return
}
completionHandler(total: -1, used: 0)
}
task.resume()
}
public weak var fileOperationDelegate: FileOperationDelegate?
}
@@ -132,7 +130,7 @@ extension DropboxFileProvider: FileProviderOperations {
}
public func createFile(fileAttribs: FileObject, atPath path: String, contents data: NSData?, completionHandler: SimpleCompletionHandler) {
NotImplemented()
self.writeContentsAtPath(path, contents: data ?? NSData(), completionHandler: completionHandler)
}
public func moveItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) {
@@ -181,7 +179,7 @@ extension DropboxFileProvider: FileProviderOperations {
request.HTTPBody = dictionaryToJSON(requestDictionary)?.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let response = response as? NSHTTPURLResponse {
let code = FileProviderDropboxErrorCode(rawValue: response.statusCode)
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path ?? fromPath ?? "") : nil
defer {
self.delegateNotify(operation, error: error ?? dbError)
@@ -198,32 +196,37 @@ extension DropboxFileProvider: FileProviderOperations {
}
public func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) {
NotImplemented()
let request = NSMutableURLRequest(URL: absoluteURL(toPath))
request.HTTPMethod = "PUT"
let task = session.uploadTaskWithRequest(request, fromFile: localFile) { (data, response, error) in
guard let data = NSData(contentsOfURL: localFile) else {
let error = throwError(localFile.uw_absoluteString, code: NSURLError.FileDoesNotExist)
completionHandler?(error: error)
self.delegateNotify(.Move(source: localFile.uw_absoluteString, destination: toPath), error: error)
return
}
task.taskDescription = self.dictionaryToJSON(["type": "Copy", "source": localFile.uw_absoluteString, "dest": toPath])
task.resume()
upload_simple(toPath, data: data, overwrite: true, operation: .Copy(source: localFile.absoluteString, destination: toPath), completionHandler: completionHandler)
}
public func copyPathToLocalFile(path: String, toLocalURL: NSURL, completionHandler: SimpleCompletionHandler) {
NotImplemented()
let request = NSMutableURLRequest(URL: absoluteURL(path))
let task = session.downloadTaskWithRequest(request) { (sourceFileURL, response, error) in
if let sourceFileURL = sourceFileURL {
do {
try NSFileManager.defaultManager().copyItemAtURL(sourceFileURL, toURL: toLocalURL)
} catch let e {
completionHandler?(error: e)
return
}
public func copyPathToLocalFile(path: String, toLocalURL destURL: NSURL, completionHandler: SimpleCompletionHandler) {
let url = NSURL(string: "https://api.dropboxapi.com/2/files/download")!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestDictionary = ["path": path]
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.downloadTaskWithRequest(request, completionHandler: { (cacheURL, response, error) in
guard let cacheURL = cacheURL, let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode < 300 else {
let code = FileProviderHTTPErrorCode(rawValue: (response as? NSHTTPURLResponse)?.statusCode ?? -1)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path) : nil
completionHandler?(error: dbError ?? error)
return
}
completionHandler?(error: error)
}
task.taskDescription = self.dictionaryToJSON(["type": "Copy", "source": path, "dest": toLocalURL.uw_absoluteString])
do {
try NSFileManager.defaultManager().moveItemAtURL(cacheURL, toURL: destURL)
completionHandler?(error: nil)
} catch let e {
completionHandler?(error: e)
}
})
task.taskDescription = self.dictionaryToJSON(["type": "Copy", "source": path, "dest": destURL.uw_absoluteString])
task.resume()
}
}
@@ -234,7 +237,6 @@ extension DropboxFileProvider: FileProviderReadWrite {
}
public func contentsAtPath(path: String, offset: Int64, length: Int, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
let url = NSURL(string: "https://api.dropboxapi.com/2/files/download")!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
@@ -249,7 +251,7 @@ extension DropboxFileProvider: FileProviderReadWrite {
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.downloadTaskWithRequest(request, completionHandler: { (cacheURL, response, error) in
guard let cacheURL = cacheURL, let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode < 300 else {
let code = FileProviderDropboxErrorCode(rawValue: (response as? NSHTTPURLResponse)?.statusCode ?? -1)
let code = FileProviderHTTPErrorCode(rawValue: (response as? NSHTTPURLResponse)?.statusCode ?? -1)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path) : nil
completionHandler(contents: nil, error: dbError ?? error)
return
@@ -266,32 +268,22 @@ extension DropboxFileProvider: FileProviderReadWrite {
}
public func writeContentsAtPath(path: String, contents data: NSData, atomically: Bool = false, completionHandler: SimpleCompletionHandler) {
NotImplemented()
let url = atomically ? absoluteURL(path).uw_URLByAppendingPathExtension("tmp") : absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "PUT"
let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in
defer {
self.delegateNotify(.Modify(path: path), error: error)
}
if atomically {
self.moveItemAtPath((path as NSString).stringByAppendingPathExtension("tmp")!, toPath: path, completionHandler: completionHandler)
}
if let error = error {
// If there is no error, completionHandler has been executed by move command
completionHandler?(error: error)
}
}
task.taskDescription = self.dictionaryToJSON(["type": "Modify", "source": path])
task.resume()
// FIXME: remove 150MB restriction
upload_simple(path, data: data, overwrite: true, operation: .Modify(path: path), completionHandler: completionHandler)
}
public func searchFilesAtPath(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: ((files: [FileObject], error: ErrorType?) -> Void)) {
NotImplemented()
var foundFiles = [DropboxFileObject]()
search(path, query: query, foundItem: { (file) in
foundFiles.append(file)
foundItemHandler?(file)
}, completionHandler: { (error) in
completionHandler(files: foundFiles, error: error)
})
}
private func registerNotifcation(path: String, eventHandler: (() -> Void)) {
/* There is two ways to monitor folders chaging in Dropbox. Either using webooks
/* There is two ways to monitor folders changing in Dropbox. Either using webooks
* which means you have to implement a server to translate it to push notifications
* or using apiv2 list_folder/longpoll method. The second one is implemeted here.
* Tough webhooks are much more efficient, longpoll is much simpler to implement!
@@ -302,6 +294,139 @@ extension DropboxFileProvider: FileProviderReadWrite {
private func unregisterNotifcation(path: String) {
NotImplemented()
}
// TODO: Implement /copy_reference, /get_preview & /get_thumbnail, /get_temporary_link, /save_url, /get_account & /get_current_account
}
private extension DropboxFileProvider {
private func list(path: String, cursor: String? = nil, prevContents: [DropboxFileObject] = [], recursive: Bool = false, completionHandler: ((contents: [FileObject], cursor: String?, error: ErrorType?) -> Void)) {
var requestDictionary = [String: AnyObject]()
let url: NSURL
if let cursor = cursor {
url = NSURL(string: "https://api.dropboxapi.com/2/files/list_folder/continue")!
requestDictionary["cursor"] = cursor
} else {
url = NSURL(string: "https://api.dropboxapi.com/2/files/list_folder")!
requestDictionary["path"] = correctPath(path)
requestDictionary["recursive"] = NSNumber(bool: recursive)
}
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.HTTPBody = dictionaryToJSON(requestDictionary)?.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request) { (data, response, error) in
var responseError: FileProviderDropboxError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderDropboxError(code: rCode, path: path)
}
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) {
let json = self.jsonToDictionary(jsonStr)
if let entries = json?["entries"] as? [AnyObject] where entries.count > 0 {
var files = prevContents
for entry in entries {
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
files.append(file)
}
}
let ncursor = json?["cursor"] as? String
let hasmore = (json?["has_more"] as? NSNumber)?.boolValue ?? false
if hasmore {
self.list(path, cursor: ncursor, prevContents: files, completionHandler: completionHandler)
} else {
completionHandler(contents: files, cursor: ncursor, error: responseError ?? error)
}
return
}
}
completionHandler(contents: [], cursor: nil, error: responseError ?? error)
}
task.resume()
}
private func upload_simple(targetPath: String, data: NSData, modifiedDate: NSDate = NSDate(), overwrite: Bool, operation: FileOperation, completionHandler: SimpleCompletionHandler) {
assert(data.length < 150*1024*1024, "Maximum size of allowed size to upload is 150MB")
var requestDictionary = [String: AnyObject]()
let url: NSURL
url = NSURL(string: "https://content.dropboxapi.com/2/files/upload")!
requestDictionary["path"] = correctPath(targetPath)
requestDictionary["mode"] = overwrite ? "overwrite" : "add"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssz"
requestDictionary["client_modified"] = dateFormatter.stringFromDate(modifiedDate)
let request = NSMutableURLRequest(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.HTTPBody = data
let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in
var responseError: FileProviderDropboxError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderDropboxError(code: rCode, path: targetPath)
}
defer {
self.delegateNotify(.Create(path: targetPath), error: responseError ?? error)
}
completionHandler?(error: responseError ?? error)
}
var dic: [String: AnyObject] = ["type": operation.description]
switch operation {
case .Create(path: let s):
dic["source"] = s
case .Copy(source: let s, destination: let d):
dic["source"] = s
dic["dest"] = d
case .Modify(path: let s):
dic["source"] = s
case .Move(source: let s, destination: let d):
dic["source"] = s
dic["dest"] = d
default:
break
}
task.taskDescription = self.dictionaryToJSON(dic)
task.resume()
}
func search(startPath: String = "", query: String, start: Int = 0, maxResultPerPage: Int = 25, foundItem:((file: DropboxFileObject) -> Void), completionHandler: ((error: ErrorType?) -> Void)) {
let url = NSURL(string: "https://api.dropboxapi.com/2/files/search")!
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
var requestDictionary: [String: AnyObject] = ["path": startPath]
requestDictionary["query"] = query
requestDictionary["start"] = start
requestDictionary["max_results"] = maxResultPerPage
request.HTTPBody = dictionaryToJSON(requestDictionary)?.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request) { (data, response, error) in
var responseError: FileProviderDropboxError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderDropboxError(code: rCode, path: startPath)
}
if let data = data, let jsonStr = String(data: data, encoding: NSUTF8StringEncoding) {
let json = self.jsonToDictionary(jsonStr)
if let entries = json?["matches"] as? [AnyObject] where entries.count > 0 {
for entry in entries {
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
foundItem(file: file)
}
}
let rstart = json?["start"] as? Int
let hasmore = (json?["more"] as? NSNumber)?.boolValue ?? false
if hasmore, let rstart = rstart {
self.search(startPath, query: query, start: rstart + entries.count, maxResultPerPage: maxResultPerPage, foundItem: foundItem, completionHandler: completionHandler)
} else {
completionHandler(error: responseError ?? error)
}
return
}
}
completionHandler(error: responseError ?? error)
}
task.resume()
}
}
internal extension DropboxFileProvider {
@@ -335,6 +460,8 @@ internal extension DropboxFileProvider {
}
}
extension DropboxFileProvider: FileProvider {}
// MARK: URLSession delegate
extension DropboxFileProvider: NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
public func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
+3 -1
View File
@@ -122,6 +122,8 @@ public protocol FileProviderBasic: class {
*/
func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObject], error: ErrorType?) -> Void))
func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void))
func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void))
}
public protocol FileProviderOperations: FileProviderBasic {
@@ -336,7 +338,7 @@ public protocol FileOperationDelegate: class {
/// fileProvider(_:shouldOperate:) gives the delegate an opportunity to filter the file operation. Returning true from this method will allow the copy to happen. Returning false from this method causes the item in question to be skipped. If the item skipped was a directory, no children of that directory will be subject of the operation, nor will the delegate be notified of those children.
func fileProvider(fileProvider: FileProviderOperations, shouldDoOperation operation: FileOperation) -> Bool
/// fileProvider:shouldProceedAfterError:copyingItemAtPath:toPath: gives the delegate an opportunity to recover from or continue copying after an error. If an error occurs, the error object will contain an ErrorType indicating the problem. The source path and destination paths are also provided. If this method returns true, the FileProvider instance will continue as if the error had not occurred. If this method returns false, the NSFileManager instance will stop copying, return false from copyItemAtPath:toPath:error: and the error will be provied there.
/// fileProvider(_:shouldProceedAfterError:copyingItemAtPath:toPath:) gives the delegate an opportunity to recover from or continue copying after an error. If an error occurs, the error object will contain an ErrorType indicating the problem. The source path and destination paths are also provided. If this method returns true, the FileProvider instance will continue as if the error had not occurred. If this method returns false, the NSFileManager instance will stop copying, return false from copyItemAtPath:toPath:error: and the error will be provied there.
func fileProvider(fileProvider: FileProviderOperations, shouldProceedAfterError error: ErrorType, operation: FileOperation) -> Bool
}
+8
View File
@@ -85,6 +85,13 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
return fileAttr
}
public func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void)) {
let dict = (try? NSFileManager.defaultManager().attributesOfFileSystemForPath(baseURL?.path ?? "/")) as NSDictionary?;
let totalSize = dict?.objectForKey(NSFileSystemSize)?.longLongValue ?? -1;
let freeSize = dict?.objectForKey(NSFileSystemFreeSize)?.longLongValue ?? 0;
completionHandler(total: totalSize, used: totalSize - freeSize)
}
public func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void)) {
dispatch_async(dispatch_queue) {
completionHandler(attributes: self.attributesOfItemAtURL(self.absoluteURL(path)), error: nil)
@@ -324,6 +331,7 @@ public class LocalFileProvider: FileProvider, FileProviderMonitor {
for (i, monitor) in monitors.enumerate() {
if self.relativePathOf(url: monitor.url) == path {
removedMonitor = monitors.removeAtIndex(i)
break
}
}
removedMonitor?.stop()
+4
View File
@@ -40,6 +40,10 @@ public class SMBFileProvider: FileProvider, FileProviderMonitor {
NotImplemented()
}
public func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void)) {
NotImplemented()
}
public weak var fileOperationDelegate: FileOperationDelegate?
public func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) {
+2 -1
View File
@@ -243,11 +243,12 @@ extension SMB2 {
// SRV_ENUMERATE_SNAPSHOTS
struct SrvSnapshots: IOCtlResponseProtocol {
let count: UInt32
let returnedCount: UInt32
let snapshots: [SMBTime]
init?(data: NSData) {
self.count = decode(data)
let returnedCount: UInt32 = decode(data.subdataWithRange(NSRange(location: 4, length: 4)))
self.returnedCount = decode(data.subdataWithRange(NSRange(location: 4, length: 4)))
//let size: UInt32 = decode(data.subdataWithRange(NSRange(location: 8, length: 4)))
var snapshots = [SMBTime]()
let dateFormatter = NSDateFormatter()
+2
View File
@@ -11,6 +11,8 @@ import Foundation
extension SMB2 {
// MARK: SMB2 Query Directory
// MARK: SMB2 Change Notify
// MARK: SMB2 Query Info
+245 -74
View File
@@ -8,45 +8,6 @@
import Foundation
public enum FileProviderWebDavErrorCode: Int {
case OK = 200
case Created = 201
case NoContent = 204
case MultiStatus = 207
case Forbidden = 403
case MethodNotAllowed = 405
case Conflict = 409
case PreconditionFailed = 412
case UnsupportedMediaType = 415
case Locked = 423
case FailedDependency = 424
case BadGateway = 502
case InsufficientStorage = 507
}
public struct FileProviderWebDavError: ErrorType, CustomStringConvertible {
public let code: FileProviderWebDavErrorCode
public let url: NSURL
public var description: String {
switch code {
case .OK: return "OK"
case .Created: return "Created"
case .NoContent: return "No Content"
case .MultiStatus: return ""
case .Forbidden: return "Forbidden"
case .MethodNotAllowed: return "Method Not Allowed"
case .Conflict: return "Conflict"
case .PreconditionFailed: return "Precondition Failed"
case .UnsupportedMediaType: return "Unsupported Media Type"
case .Locked: return "Locked"
case .FailedDependency: return "Failed Dependency"
case .BadGateway: return "Bad Gateway"
case .InsufficientStorage: return "Insufficient Storage"
}
}
}
public final class WebDavFileObject: FileObject {
public let contentType: String
public let entryTag: String?
@@ -102,12 +63,15 @@ public class WebDAVFileProvider: NSObject, FileProviderBasic {
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "PROPFIND"
request.setValue(baseURL?.uw_absoluteString, forHTTPHeaderField: "Host")
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>".dataUsingEncoding(NSUTF8StringEncoding)
request.setValue(String(request.HTTPBody!.length), forHTTPHeaderField: "Content-Length")
let task = session.dataTaskWithRequest(request) { (data, response, error) in
var responseError: FileProviderWebDavError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
if let data = data {
let xresponse = self.parseXMLResponse(data)
var fileObjects = [WebDavFileObject]()
@@ -117,31 +81,63 @@ public class WebDAVFileProvider: NSObject, FileProviderBasic {
}
fileObjects.append(self.mapToFileObject(attr))
}
completionHandler(contents: fileObjects, error: error)
completionHandler(contents: fileObjects, error: responseError ?? error)
return
}
completionHandler(contents: [], error: error)
completionHandler(contents: [], error: responseError ?? error)
}
task.resume()
}
public func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObject?, error: ErrorType?) -> Void)) {
let request = NSMutableURLRequest(URL: absoluteURL(path))
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "PROPFIND"
request.setValue(baseURL?.uw_absoluteString, forHTTPHeaderField: "Host")
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>".dataUsingEncoding(NSUTF8StringEncoding)
request.setValue(String(request.HTTPBody!.length), forHTTPHeaderField: "Content-Length")
let task = session.dataTaskWithRequest(request) { (data, response, error) in
var responseError: FileProviderWebDavError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
if let data = data {
let xresponse = self.parseXMLResponse(data)
if let attr = xresponse.first {
completionHandler(attributes: self.mapToFileObject(attr), error: error)
completionHandler(attributes: self.mapToFileObject(attr), error: responseError ?? error)
return
}
}
completionHandler(attributes: nil, error: error)
completionHandler(attributes: nil, error: responseError ?? error)
}
task.resume()
}
public func storageProperties(completionHandler: ((total: Int64, used: Int64) -> Void)) {
// Not all WebDAV clients implements RFC2518 which allows geting storage quota.
// In this case you won't get error. totalSize is NSURLSessionTransferSizeUnknown
// and used space is zero.
guard let baseURL = baseURL else {
return
}
let request = NSMutableURLRequest(URL: baseURL)
request.HTTPMethod = "PROPFIND"
request.setValue("0", 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:prop><D:quota-available-bytes/><D:quota-used-bytes/></D:prop>\n</D:propfind>".dataUsingEncoding(NSUTF8StringEncoding)
request.setValue(String(request.HTTPBody!.length), forHTTPHeaderField: "Content-Length")
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let data = data {
let xresponse = self.parseXMLResponse(data)
if let attr = xresponse.first {
let totalSize = Int64(attr.prop["quota-available-bytes"] ?? "")
let usedSize = Int64(attr.prop["quota-used-bytes"] ?? "")
completionHandler(total: totalSize ?? -1, used: usedSize ?? 0)
return
}
}
completionHandler(total: -1, used: 0)
}
task.resume()
}
@@ -154,24 +150,32 @@ extension WebDAVFileProvider: FileProviderOperations {
let url = absoluteURL((atPath as NSString).stringByAppendingPathComponent(folderName) + "/")
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "MKCOL"
request.setValue(baseURL?.uw_absoluteString, forHTTPHeaderField: "Host")
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) where code != .OK {
var responseError: FileProviderWebDavError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
if let response = response as? NSHTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) where code != .OK {
completionHandler?(error: FileProviderWebDavError(code: code, url: url))
return
}
completionHandler?(error: error)
self.delegateNotify(.Create(path: (atPath as NSString).stringByAppendingPathComponent(folderName) + "/"), error: error)
completionHandler?(error: responseError ?? error)
self.delegateNotify(.Create(path: (atPath as NSString).stringByAppendingPathComponent(folderName) + "/"), error: responseError ?? error)
}
task.resume()
}
public func createFile(fileAttribs: FileObject, atPath path: String, contents data: NSData?, completionHandler: SimpleCompletionHandler) {
let request = NSMutableURLRequest(URL: absoluteURL(path))
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "PUT"
let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in
completionHandler?(error: error)
self.delegateNotify(.Create(path: (path as NSString).stringByAppendingPathComponent(fileAttribs.name)), error: error)
var responseError: FileProviderWebDavError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
completionHandler?(error: responseError ?? error)
self.delegateNotify(.Create(path: (path as NSString).stringByAppendingPathComponent(fileAttribs.name)), error: responseError ?? error)
}
task.taskDescription = self.dictionaryToJSON(["type": "Create", "source": (path as NSString).stringByAppendingPathComponent(fileAttribs.name)])
task.resume()
@@ -193,13 +197,12 @@ extension WebDAVFileProvider: FileProviderOperations {
} else {
request.HTTPMethod = "COPY"
}
request.setValue(baseURL?.uw_absoluteString, forHTTPHeaderField: "Host")
request.setValue(absoluteURL(path).uw_absoluteString, forHTTPHeaderField: "Destination")
if !overwrite {
request.setValue("F", forHTTPHeaderField: "Overwrite")
}
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) {
if let response = response as? NSHTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
defer {
let op = move ? FileOperation.Move(source: path, destination: toPath) : .Copy(source: path, destination: toPath)
self.delegateNotify(op, error: error)
@@ -223,9 +226,8 @@ extension WebDAVFileProvider: FileProviderOperations {
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "DELETE"
request.setValue(baseURL?.uw_absoluteString, forHTTPHeaderField: "Host")
let task = session.dataTaskWithRequest(request) { (data, response, error) in
if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) {
if let response = response as? NSHTTPURLResponse, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
defer {
self.delegateNotify(.Remove(path: path), error: error)
}
@@ -245,19 +247,29 @@ extension WebDAVFileProvider: FileProviderOperations {
}
public func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) {
let request = NSMutableURLRequest(URL: absoluteURL(toPath))
let url = absoluteURL(toPath)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "PUT"
let task = session.uploadTaskWithRequest(request, fromFile: localFile) { (data, response, error) in
completionHandler?(error: error)
self.delegateNotify(.Move(source: localFile.uw_absoluteString, destination: toPath), error: error)
var responseError: FileProviderWebDavError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
completionHandler?(error: responseError ?? error)
self.delegateNotify(.Move(source: localFile.uw_absoluteString, destination: toPath), error: responseError ?? error)
}
task.taskDescription = self.dictionaryToJSON(["type": "Copy", "source": localFile.uw_absoluteString, "dest": toPath])
task.resume()
}
public func copyPathToLocalFile(path: String, toLocalURL: NSURL, completionHandler: SimpleCompletionHandler) {
let request = NSMutableURLRequest(URL: absoluteURL(path))
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
let task = session.downloadTaskWithRequest(request) { (sourceFileURL, response, error) in
var responseError: FileProviderWebDavError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
if let sourceFileURL = sourceFileURL {
do {
try NSFileManager.defaultManager().copyItemAtURL(sourceFileURL, toURL: toLocalURL)
@@ -266,7 +278,7 @@ extension WebDAVFileProvider: FileProviderOperations {
return
}
}
completionHandler?(error: error)
completionHandler?(error: responseError ?? error)
}
task.taskDescription = self.dictionaryToJSON(["type": "Copy", "source": path, "dest": toLocalURL.uw_absoluteString])
task.resume()
@@ -279,7 +291,8 @@ extension WebDAVFileProvider: FileProviderReadWrite {
}
public func contentsAtPath(path: String, offset: Int64, length: Int, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) {
let request = NSMutableURLRequest(URL: absoluteURL(path))
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
if length > 0 {
request.setValue("bytes=\(offset)-\(offset + length)", forHTTPHeaderField: "Range")
@@ -287,7 +300,11 @@ extension WebDAVFileProvider: FileProviderReadWrite {
request.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
}
let task = session.dataTaskWithRequest(request) { (data, response, error) in
completionHandler(contents: data, error: error)
var responseError: FileProviderWebDavError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
completionHandler(contents: data, error: responseError ?? error)
}
task.resume()
}
@@ -298,16 +315,20 @@ extension WebDAVFileProvider: FileProviderReadWrite {
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "PUT"
let task = session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in
var responseError: FileProviderWebDavError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: self.absoluteURL(path))
}
defer {
self.delegateNotify(.Modify(path: path), error: error)
self.delegateNotify(.Modify(path: path), error: responseError ?? error)
}
if let error = error {
completionHandler?(error: error)
return
}
if atomically {
self.moveItemAtPath((path as NSString).stringByAppendingPathExtension("tmp")!, toPath: path, completionHandler: completionHandler)
}
if let error = error {
// If there is no error, completionHandler has been executed by move command
completionHandler?(error: error)
}
}
task.taskDescription = self.dictionaryToJSON(["type": "Modify", "source": path])
task.resume()
@@ -317,13 +338,16 @@ extension WebDAVFileProvider: FileProviderReadWrite {
let url = absoluteURL(path)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "PROPFIND"
request.setValue(baseURL?.uw_absoluteString, forHTTPHeaderField: "Host")
//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>".dataUsingEncoding(NSUTF8StringEncoding)
request.setValue(String(request.HTTPBody!.length), forHTTPHeaderField: "Content-Length")
let task = session.dataTaskWithRequest(request) { (data, response, error) in
// FIXME: paginating results
var responseError: FileProviderWebDavError?
if let code = (response as? NSHTTPURLResponse)?.statusCode where code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
if let data = data {
let xresponse = self.parseXMLResponse(data)
var fileObjects = [WebDavFileObject]()
@@ -335,10 +359,10 @@ extension WebDAVFileProvider: FileProviderReadWrite {
fileObjects.append(fileObject)
foundItemHandler?(fileObject)
}
completionHandler(files: fileObjects, error: error)
completionHandler(files: fileObjects, error: responseError ?? error)
return
}
completionHandler(files: [], error: error)
completionHandler(files: [], error: responseError ?? error)
}
task.resume()
}
@@ -357,6 +381,8 @@ extension WebDAVFileProvider: FileProviderReadWrite {
// TODO: implements methods for lock mechanism
}
extension WebDAVFileProvider: FileProvider {}
// MARK: WEBDAV XML response implementation
internal extension WebDAVFileProvider {
@@ -506,10 +532,155 @@ extension WebDAVFileProvider: NSURLSessionDataDelegate, NSURLSessionDownloadDele
}
public func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential)
let deposition: NSURLSessionAuthChallengeDisposition = credential != nil ? .UseCredential : .PerformDefaultHandling
completionHandler(deposition, credential)
}
public func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential)
let deposition: NSURLSessionAuthChallengeDisposition = credential != nil ? .UseCredential : .PerformDefaultHandling
completionHandler(deposition, credential)
}
}
public struct FileProviderWebDavError: ErrorType, CustomStringConvertible {
public let code: FileProviderHTTPErrorCode
public let url: NSURL
public var description: String {
return code.description
}
}
public enum FileProviderHTTPErrorCode: Int {
case OK = 200
case Created = 201
case Accepted = 202
case NonAuthoritativeInformation = 203
case NoContent = 204
case ResetContent = 205
case PartialContent = 206
case MultiStatus = 207
case AlreadyReported = 208
case IMUsed = 226
case MultipleChoices = 300
case MovedPermanently = 301
case Found = 302
case SeeOther = 303
case NotModified = 304
case UseProxy = 305
case TemporaryRedirect = 307
case PermanentRedirect = 308
case BadRequest = 400
case Unauthorized = 401
case PaymentRequired = 402
case Forbidden = 403
case NotFound = 404
case MethodNotAllowed = 405
case NotAcceptable = 406
case ProxyAuthenticationRequired = 407
case RequestTimeout = 408
case Conflict = 409
case Gone = 410
case LengthRequired = 411
case PreconditionFailed = 412
case PayloadTooLarge = 413
case URITooLong = 414
case UnsupportedMediaType = 415
case RangeNotSatisfiable = 416
case ExpectationFailed = 417
case MisdirectedRequest = 421
case UnprocessableEntity = 422
case Locked = 423
case FailedDependency = 424
case UpgradeRequired = 426
case PreconditionRequired = 428
case TooManyRequests = 429
case RequestHeaderFieldsTooLarge = 431
case UnavailableForLegalReasons = 451
case InternalServerError = 500
case BadGateway = 502
case ServiceUnavailable = 503
case GatewayTimeout = 504
case HTTPVersionNotSupported = 505
case VariantlsoNegotiates = 506
case InsufficientStorage = 507
case LoopDetected = 508
case NotExtended = 510
case NetworkAuthenticationRequired = 511
public var description: String {
switch self.rawValue {
case 100: return "Continue"
case 101: return "Switching Protocols"
case 102: return "Processing"
case 200: return "OK"
case 201: return "Created"
case 202: return "Accepted"
case 203: return "Non-Authoritative Information"
case 204: return "No Content"
case 205: return "Reset Content"
case 206: return "Partial Content"
case 207: return "Multi-Status"
case 208: return "Already Reported"
case 226: return "IM Used"
case 300: return "Multiple Choices"
case 301: return "Moved Permanently"
case 302: return "Found"
case 303: return "See Other"
case 304: return "Not Modified"
case 305: return "Use Proxy"
case 307: return "Temporary Redirect"
case 308: return "Permanent Redirect"
case 400: return "Bad Request"
case 401: return "Unauthorized/Expired Session"
case 402: return "Payment Required"
case 403: return "Forbidden"
case 404: return "Not Found"
case 405: return "Method Not Allowed"
case 406: return "Not Acceptable"
case 407: return "Proxy Authentication Required"
case 408: return "Request Timeout"
case 409: return "Conflict"
case 410: return "Gone"
case 411: return "Length Required"
case 412: return "Precondition Failed"
case 413: return "Payload Too Large"
case 414: return "URI Too Long"
case 415: return "Unsupported Media Type"
case 416: return "Range Not Satisfiable"
case 417: return "Expectation Failed"
case 421: return "Misdirected Request"
case 422: return "Unprocessable Entity"
case 423: return "Locked"
case 424: return "Failed Dependency"
case 426: return "Upgrade Required"
case 428: return "Precondition Required"
case 429: return "Too Many Requests"
case 431: return "Request Header Fields Too Large"
case 451: return "Unavailable For Legal Reasons"
case 500: return "Internal Server Error"
case 501: return "Not Implemented"
case 502: return "Bad Gateway"
case 503: return "Service Unavailable"
case 504: return "Gateway Timeout"
case 505: return "HTTP Version Not Supported"
case 506: return "Variant Also Negotiates"
case 507: return "Insufficient Storage"
case 508: return "Loop Detected"
case 510: return "Not Extended"
case 511: return "Network Authentication Required"
default: return typeDescription
}
}
public var typeDescription: String {
switch self.rawValue {
case 100...199: return "Informational"
case 200...299: return "Success"
case 300...399: return "Redirection"
case 400...499: return "Client Error"
case 500...599: return "Server Error"
default: return "Server Error"
}
}
}