Compare commits

...

4 Commits

Author SHA1 Message Date
Amir Abbas Mousavian c38ee1ccd3 standardized FileObject.path property
- FileObject.path now has heading slash in all scenarios
- fixes #27, WebDAV fileObject.path was not relative
2017-02-01 21:23:11 +03:30
Amir Abbas Mousavian 1a44df3fd7 Refactoring, better webdav response handling
- Added Dropbox copy from reference method
- refactored `mapToFileObject` methods into `FileObject` initializers
- fixed `requestDictionary` type to `[String: AnyObject]`
2017-02-01 14:08:28 +03:30
Amir Abbas Mousavian eff3725680 Added release version badge 2017-02-01 00:21:48 +03:30
Amir Abbas Mousavian 9368f636d3 Added carthage badge 2017-01-31 23:53:06 +03:30
11 changed files with 261 additions and 231 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.11.2"
s.version = "0.11.4"
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/OneDrive/SMB2) files on iOS and macOS."
# This description is used to generate tags and improve search results.
+2 -2
View File
@@ -603,7 +603,7 @@
799396601D48B7BF00086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.11.2;
BUNDLE_VERSION_STRING = 0.11.4;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -633,7 +633,7 @@
799396611D48B7BF00086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.11.2;
BUNDLE_VERSION_STRING = 0.11.4;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
+9 -6
View File
@@ -3,17 +3,18 @@
>This Swift library provide a swifty way to deal with local and remote files and directories in a unified way.
[![Swift Version][swift-image]][swift-url]
[![Platform](https://img.shields.io/badge/Platform-iOS%2C%20macOS%2C%20tvOS-lightgray.svg)](#)
[![License][license-image]][license-url]
[![Platform](https://img.shields.io/badge/Platform-iOS%2C%20macOS%2C%20tvOS-lightgray.svg)]()
[![release badge][release-image]][release-url]
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/FileProvider.svg)](https://cocoapods.org/pods/FileProvider)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?)](https://github.com/Carthage/Carthage)
[![Build Status][travis-image]][travis-url]
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/FileProvider.svg)](https://cocoapods.org/pods/FileProvider)
[![codebeat badge][codebeat-image]][codebeat-url]
<!---
[![codecov](https://codecov.io/gh/amosavian/FileProvider/branch/master/graph/badge.svg)](https://codecov.io/gh/amosavian/FileProvider)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![codecov](https://codecov.io/gh/amosavian/FileProvider/branch/master/graph/badge.svg)](https://codecov.io/gh/amosavian/FileProvider)
--->
This library provides implementaion of WebDav, Dropbox, OneDrive and SMB2 (incomplete) and local files.
@@ -411,4 +412,6 @@ Distributed under the MIT license. See `LICENSE` for more information.
[codebeat-image]: https://codebeat.co/badges/7b359f48-78eb-4647-ab22-56262a827517
[codebeat-url]: https://codebeat.co/projects/github-com-amosavian-fileprovider
[travis-image]: https://img.shields.io/travis/amosavian/FileProvider/master.svg
[travis-url]: https://travis-ci.org/amosavian/FileProvider
[travis-url]: https://travis-ci.org/amosavian/FileProvider
[release-url]: https://github.com/amosavian/FileProvider/releases
[release-image]: https://img.shields.io/github/release/amosavian/FileProvider.svg
+31 -12
View File
@@ -82,7 +82,7 @@ open class DropboxFileProvider: FileProviderBasicRemote {
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestDictionary = ["path": correctPath(path)! as NSString]
let requestDictionary: [String: AnyObject] = ["path": correctPath(path)! as NSString]
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var dbError: FileProviderDropboxError?
@@ -90,7 +90,7 @@ open class DropboxFileProvider: FileProviderBasicRemote {
if let response = response as? HTTPURLResponse {
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
dbError = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr), let file = self.mapToFileObject(json) {
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr), let file = DropboxFileObject(json: json) {
fileObject = file
}
}
@@ -205,8 +205,8 @@ extension DropboxFileProvider: FileProviderOperations {
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
let requestDictionary = ["path": path]
let requestJson = dictionaryToJSON(requestDictionary as [String : AnyObject]) ?? ""
let requestDictionary: [String: AnyObject] = ["path": path as NSString]
let requestJson = dictionaryToJSON(requestDictionary) ?? ""
request.setValue(requestJson, forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.downloadTask(with: request, completionHandler: { (cacheURL, response, error) in
guard let cacheURL = cacheURL, let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
@@ -241,8 +241,8 @@ extension DropboxFileProvider: FileProviderReadWrite {
} else if offset > 0 && length < 0 {
request.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
}
let requestDictionary = ["path": correctPath(path)! as NSString]
request.setValue(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
let requestDictionary: [String: AnyObject] = ["path": correctPath(path)! as NSString]
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var dbError: FileProviderDropboxError?
if let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: httpResponse.statusCode) {
@@ -305,7 +305,7 @@ extension DropboxFileProvider {
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestDictionary = ["path": correctPath(path)! as NSString]
let requestDictionary: [String: AnyObject] = ["path": correctPath(path)! as NSString]
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var dbError: FileProviderDropboxError?
@@ -319,7 +319,7 @@ extension DropboxFileProvider {
link = URL(string: linkStr)
}
if let attribDic = json["metadata"] as? [String: AnyObject] {
fileObject = self.mapToFileObject(attribDic)
fileObject = DropboxFileObject(json: attribDic)
}
}
}
@@ -336,7 +336,7 @@ extension DropboxFileProvider {
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestDictionary = ["path": correctPath(toPath)! as NSString, "url" : remoteURL.absoluteString as NSString]
let requestDictionary: [String: AnyObject] = ["path": correctPath(toPath)! as NSString, "url" : remoteURL.absoluteString as NSString]
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var dbError: FileProviderDropboxError?
@@ -348,7 +348,7 @@ extension DropboxFileProvider {
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr) {
jobId = json["async_job_id"] as? String
if let attribDic = json["metadata"] as? [String: AnyObject] {
fileObject = self.mapToFileObject(attribDic)
fileObject = DropboxFileObject(json: attribDic)
}
}
}
@@ -356,6 +356,25 @@ extension DropboxFileProvider {
})
task.resume()
}
open func copyItem(reference: String, to toPath: String, completionHandler:SimpleCompletionHandler) {
let url = URL(string: "files/save_url", relativeTo: apiURL)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestDictionary: [String: AnyObject] = ["path": correctPath(toPath)! as NSString, "copy_reference" : reference as NSString]
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var dbError: FileProviderDropboxError?
if let response = response as? HTTPURLResponse {
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
dbError = code != nil ? FileProviderDropboxError(code: code!, path: toPath, errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil
}
completionHandler?(dbError ?? error)
})
task.resume()
}
}
extension DropboxFileProvider: ExtendedFileProvider {
@@ -405,7 +424,7 @@ extension DropboxFileProvider: ExtendedFileProvider {
}
var request = URLRequest(url: url)
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
var requestDictionary = ["path": path as NSString]
var requestDictionary: [String: AnyObject] = ["path": path as NSString]
requestDictionary["format"] = "jpeg" as NSString
if let dimension = dimension {
requestDictionary["size"] = "w\(Int(dimension.width))h\(Int(dimension.height))" as NSString
@@ -438,7 +457,7 @@ extension DropboxFileProvider: ExtendedFileProvider {
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestDictionary = ["path": correctPath(path)! as NSString, "include_media_info": NSNumber(value: true)]
let requestDictionary: [String: AnyObject] = ["path": correctPath(path)! as NSString, "include_media_info": NSNumber(value: true)]
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var dbError: FileProviderDropboxError?
+27 -28
View File
@@ -23,6 +23,24 @@ public final class DropboxFileObject: FileObject {
super.init(url: URL(string: path) ?? URL(string: "/")!, name: name, path: path)
}
internal convenience init? (jsonStr: String) {
guard let json = jsonToDictionary(jsonStr) else { return nil }
self.init(json: json)
}
internal convenience init? (json: [String: AnyObject]) {
guard let name = json["name"] as? String else { return nil }
guard let path = json["path_display"] as? String else { return nil }
self.init(name: name, path: path)
self.size = (json["size"] as? NSNumber)?.int64Value ?? -1
self.serverTime = resolve(dateString: json["server_modified"] as? String ?? "")
self.modifiedDate = resolve(dateString: json["client_modified"] as? String ?? "")
self.type = (json[".tag"] as? String) == "folder" ? .directory : .regular
self.isReadOnly = (json["sharing_info"]?["read_only"] as? NSNumber)?.boolValue ?? false
self.id = json["id"] as? String
self.rev = json["rev"] as? String
}
open internal(set) var serverTime: Date? {
get {
return allValues["NSURLServerDateKey"] as? Date
@@ -79,7 +97,7 @@ internal extension DropboxFileProvider {
let json = jsonToDictionary(jsonStr)
if let entries = json?["entries"] as? [AnyObject] , entries.count > 0 {
for entry in entries {
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
if let entry = entry as? [String: AnyObject], let file = DropboxFileObject(json: entry) {
files.append(file)
}
}
@@ -104,17 +122,17 @@ internal extension DropboxFileProvider {
self.delegateNotify(.create(path: targetPath), error: error)
return nil
}
var requestDictionary = [String: Any]()
var requestDictionary = [String: AnyObject]()
let url: URL
url = URL(string: "files/upload", relativeTo: contentURL)!
requestDictionary["path"] = correctPath(targetPath) as NSString?
requestDictionary["mode"] = (overwrite ? "overwrite" : "add") as NSString
requestDictionary["client_modified"] = string(from:modifiedDate)
requestDictionary["client_modified"] = rfc3339utc(of: modifiedDate) as NSString
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
request.setValue(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
request.httpBody = data
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
var responseError: FileProviderDropboxError?
@@ -137,17 +155,17 @@ internal extension DropboxFileProvider {
self.delegateNotify(.create(path: targetPath), error: error)
return nil
}
var requestDictionary = [String: Any]()
var requestDictionary = [String: AnyObject]()
let url: URL
url = URL(string: "files/upload", relativeTo: contentURL)!
requestDictionary["path"] = correctPath(targetPath) as NSString?
requestDictionary["mode"] = (overwrite ? "overwrite" : "add") as NSString
requestDictionary["client_modified"] = string(from:modifiedDate)
requestDictionary["client_modified"] = rfc3339utc(of: modifiedDate) as NSString
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
request.setValue(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
request.setValue(dictionaryToJSON(requestDictionary), 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) {
@@ -181,7 +199,7 @@ internal extension DropboxFileProvider {
let json = jsonToDictionary(jsonStr)
if let entries = json?["matches"] as? [AnyObject] , entries.count > 0 {
for entry in entries {
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
if let entry = entry as? [String: AnyObject], let file = DropboxFileObject(json: entry) {
foundItem(file)
}
}
@@ -203,25 +221,6 @@ internal extension DropboxFileProvider {
// codebeat:enable[ARITY]
internal extension DropboxFileProvider {
func mapToFileObject(_ jsonStr: String) -> DropboxFileObject? {
guard let json = jsonToDictionary(jsonStr) else { return nil }
return self.mapToFileObject(json)
}
func mapToFileObject(_ json: [String: AnyObject]) -> DropboxFileObject? {
guard let name = json["name"] as? String else { return nil }
guard let path = json["path_display"] as? String else { return nil }
let fileObject = DropboxFileObject(name: name, path: path)
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.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["rev"] as? String
return fileObject
}
static let dateFormatter = DateFormatter()
static let decimalFormatter = NumberFormatter()
@@ -241,7 +240,7 @@ internal extension DropboxFileProvider {
let longStr = DropboxFileProvider.decimalFormatter.string(from: NSNumber(value: longitude))
dic["Location"] = "\(latStr), \(longStr)"
}
if let timeTakenStr = json["time_taken"] as? String, let timeTaken = self.resolve(dateString: timeTakenStr) {
if let timeTakenStr = json["time_taken"] as? String, let timeTaken = resolve(dateString: timeTakenStr) {
keys.append("Date taken")
DropboxFileProvider.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dic["Date taken"] = DropboxFileProvider.dateFormatter.string(from: timeTaken)
+31
View File
@@ -140,6 +140,37 @@ open class FileObject {
}
}
internal func resolve(dateString: String) -> Date? {
let dateFor: DateFormatter = DateFormatter()
dateFor.locale = Locale(identifier: "en_US")
dateFor.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
if let rfc3339 = dateFor.date(from: dateString) {
return rfc3339
}
dateFor.dateFormat = "EEE',' dd' 'MMM' 'yyyy HH':'mm':'ss z"
if let rfc1123 = dateFor.date(from: dateString) {
return rfc1123
}
dateFor.dateFormat = "EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z"
if let rfc850 = dateFor.date(from: dateString) {
return rfc850
}
dateFor.dateFormat = "EEE MMM d HH':'mm':'ss yyyy"
if let asctime = dateFor.date(from: dateString) {
return asctime
}
return nil
}
internal func rfc3339utc(of 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)
}
/// Sorting FileObject array by given criteria, **not thread-safe**
public struct FileObjectSorting {
-31
View File
@@ -299,37 +299,6 @@ extension FileProviderBasic {
internal func NotImplemented(_ fn: String = #function, file: StaticString = #file) {
assert(false, "\(fn) method is not yet implemented. \(file)")
}
internal func resolve(dateString: String) -> Date? {
let dateFor: DateFormatter = DateFormatter()
dateFor.locale = Locale(identifier: "en_US")
dateFor.dateFormat = "EEE',' dd' 'MMM' 'yyyy HH':'mm':'ss zzz"
if let rfc1123 = dateFor.date(from: dateString) {
return rfc1123
}
dateFor.dateFormat = "EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z"
if let rfc850 = dateFor.date(from: dateString) {
return rfc850
}
dateFor.dateFormat = "EEE MMM d HH':'mm':'ss yyyy"
if let asctime = dateFor.date(from: dateString) {
return asctime
}
dateFor.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssz"
if let isotime = dateFor.date(from: dateString) {
return isotime
}
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: FileProviderBasic {
+12 -6
View File
@@ -14,27 +14,33 @@ public final class LocalFileObject: FileObject {
}
public convenience init? (fileWithPath path: String, relativeTo relativeURL: URL?) {
let fileURL: URL
var rpath = path.replacingOccurrences(of: relativeURL?.absoluteString ?? "", with: "")
var fileURL: URL?
var rpath = path.replacingOccurrences(of: relativeURL?.absoluteString ?? "", with: "", options: .anchored)
if path.hasPrefix("/") {
rpath.remove(at: rpath.startIndex)
}
if rpath.isEmpty {
fileURL = relativeURL ?? URL(fileURLWithPath: path)
fileURL = relativeURL
} else {
if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) {
fileURL = URL(fileURLWithPath: rpath, relativeTo: relativeURL)
} else {
fileURL = relativeURL?.appendingPathComponent(path) ?? URL(fileURLWithPath: path)
fileURL = URL(string: rpath, relativeTo: relativeURL)
}
}
self.init(fileWithURL: fileURL)
if let fileURL = fileURL {
self.init(fileWithURL: fileURL)
} else {
return nil
}
}
public convenience init?(fileWithURL fileURL: URL) {
do {
let values = try fileURL.resourceValues(forKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .fileResourceTypeKey, .isHiddenKey, .isWritableKey, .typeIdentifierKey, .generationIdentifierKey])
self.init(url: fileURL, name: values.name ?? fileURL.lastPathComponent, path: fileURL.relativePath)
let path = fileURL.relativePath.hasPrefix("/") ? fileURL.relativePath : "/" + fileURL.relativePath
self.init(url: fileURL, name: values.name ?? fileURL.lastPathComponent, path: path)
for (key, value) in values.allValues {
self.allValues[key.rawValue] = value
}
+1 -1
View File
@@ -87,7 +87,7 @@ open class OneDriveFileProvider: FileProviderBasicRemote {
if let response = response as? HTTPURLResponse {
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
dbError = code != nil ? FileProviderOneDriveError(code: code!, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr), let file = self.mapToFileObject(json) {
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr), let file = OneDriveFileObject(baseURL: self.baseURL, drive: self.drive, json: json) {
fileObject = file
}
}
+20 -22
View File
@@ -28,6 +28,23 @@ public final class OneDriveFileObject: FileObject {
super.init(url: url, name: name, path: path)
}
internal convenience init? (baseURL: URL?, drive: String, jsonStr: String) {
guard let json = jsonToDictionary(jsonStr) else { return nil }
self.init(baseURL: baseURL, drive: drive, json: json)
}
internal convenience init? (baseURL: URL?, drive: String, json: [String: AnyObject]) {
guard let name = json["name"] as? String else { return nil }
guard let path = (json["parentReference"] as? NSDictionary)?["path"] as? String else { return nil }
let lPath = path.replacingOccurrences(of: "/drive/\(drive):", with: "/", options: .anchored, range: nil)
self.init(baseURL: baseURL, name: name, path: lPath)
self.size = (json["size"] as? NSNumber)?.int64Value ?? -1
self.modifiedDate = resolve(dateString: json["lastModifiedDateTime"] as? String ?? "")
self.creationDate = resolve(dateString: json["createdDateTime"] as? String ?? "")
self.type = (json["folder"] as? String) != nil ? .directory : .regular
self.id = json["id"] as? String
self.entryTag = json["eTag"] as? String
}
open internal(set) var id: String? {
get {
@@ -79,7 +96,7 @@ internal extension OneDriveFileProvider {
let json = jsonToDictionary(jsonStr)
if let entries = json?["value"] as? [AnyObject] , entries.count > 0 {
for entry in entries {
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
if let entry = entry as? [String: AnyObject], let file = OneDriveFileObject(baseURL: self.baseURL, drive: self.drive, json: entry) {
files.append(file)
}
}
@@ -173,7 +190,7 @@ internal extension OneDriveFileProvider {
let json = jsonToDictionary(jsonStr)
if let entries = json?["value"] as? [AnyObject] , entries.count > 0 {
for entry in entries {
if let entry = entry as? [String: AnyObject], let file = self.mapToFileObject(entry) {
if let entry = entry as? [String: AnyObject], let file = OneDriveFileObject(baseURL: self.baseURL, drive: self.drive, json: entry) {
foundItem(file)
}
}
@@ -195,25 +212,6 @@ internal extension OneDriveFileProvider {
// codebeat:enable[ARITY]
internal extension OneDriveFileProvider {
func mapToFileObject(_ jsonStr: String) -> OneDriveFileObject? {
guard let json = jsonToDictionary(jsonStr) else { return nil }
return self.mapToFileObject(json)
}
func mapToFileObject(_ json: [String: AnyObject]) -> OneDriveFileObject? {
guard let name = json["name"] as? String else { return nil }
guard let path = (json["parentReference"] as? NSDictionary)?["path"] as? String else { return nil }
let lPath = path.replacingOccurrences(of: "/drive/\(drive):", with: "/", options: .anchored, range: nil)
let fileObject = OneDriveFileObject(baseURL: self.baseURL, name: name, path: lPath)
fileObject.size = (json["size"] as? NSNumber)?.int64Value ?? -1
fileObject.modifiedDate = resolve(dateString: json["lastModifiedDateTime"] as? String ?? "")
fileObject.creationDate = resolve(dateString: json["createdDateTime"] as? String ?? "")
fileObject.type = (json["folder"] as? String) != nil ? .directory : .regular
fileObject.id = json["id"] as? String
fileObject.entryTag = json["eTag"] as? String
return fileObject
}
static let dateFormatter = DateFormatter()
static let decimalFormatter = NumberFormatter()
@@ -254,7 +252,7 @@ internal extension OneDriveFileProvider {
keys.append("Duration")
dic["Duration"] = OneDriveFileProvider.formatshort(interval: TimeInterval(duration) / 1000)
}
if let timeTakenStr = json["takenDateTime"] as? String, let timeTaken = self.resolve(dateString: timeTakenStr) {
if let timeTakenStr = json["takenDateTime"] as? String, let timeTaken = resolve(dateString: timeTakenStr) {
keys.append("Date taken")
OneDriveFileProvider.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dic["Date taken"] = OneDriveFileProvider.dateFormatter.string(from: timeTaken)
+127 -122
View File
@@ -8,30 +8,6 @@
import Foundation
public final class WebDavFileObject: FileObject {
internal override init(url: URL, name: String, path: String) {
super.init(url: url, name: name, path: path)
}
open internal(set) var contentType: String {
get {
return allValues["NSURLContentTypeKey"] as? String ?? ""
}
set {
allValues["NSURLContentTypeKey"] = newValue
}
}
open internal(set) var entryTag: String? {
get {
return allValues["NSURLEntryTagKey"] as? String
}
set {
allValues["NSURLEntryTagKey"] = newValue
}
}
}
/// Because this class uses NSURLSession, it's necessary to disable App Transport Security
/// in case of using this class with unencrypted HTTP connection.
@@ -105,12 +81,12 @@ open class WebDAVFileProvider: FileProviderBasicRemote {
}
var fileObjects = [WebDavFileObject]()
if let data = data {
let xresponse = self.parseXMLResponse(data)
for attr in xresponse {
let xresponse = DavResponse.parse(xmlResponse: data, baseURL: self.baseURL)
for attr in xresponse where attr.href != url {
if attr.href.path == url.path {
continue
}
fileObjects.append(self.mapToFileObject(attr))
fileObjects.append(WebDavFileObject(attr))
}
}
completionHandler(fileObjects, responseError ?? error)
@@ -131,9 +107,9 @@ open class WebDAVFileProvider: FileProviderBasicRemote {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
if let data = data {
let xresponse = self.parseXMLResponse(data)
let xresponse = DavResponse.parse(xmlResponse: data, baseURL: self.baseURL)
if let attr = xresponse.first {
completionHandler(self.mapToFileObject(attr), responseError ?? error)
completionHandler(WebDavFileObject(attr), responseError ?? error)
return
}
}
@@ -158,7 +134,7 @@ open class WebDAVFileProvider: FileProviderBasicRemote {
var totalSize: Int64 = -1
var usedSize: Int64 = 0
if let data = data {
let xresponse = self.parseXMLResponse(data)
let xresponse = DavResponse.parse(xmlResponse: data, baseURL: self.baseURL)
if let attr = xresponse.first {
totalSize = Int64(attr.prop["quota-available-bytes"] ?? "") ?? -1
usedSize = Int64(attr.prop["quota-used-bytes"] ?? "") ?? 0
@@ -270,7 +246,7 @@ extension WebDAVFileProvider: FileProviderOperations {
responseError = FileProviderWebDavError(code: code, url: sourceURL)
}
if code == .multiStatus, let data = data {
let xresponses = self.parseXMLResponse(data)
let xresponses = DavResponse.parse(xmlResponse: data, baseURL: self.baseURL)
for xresponse in xresponses where (xresponse.status ?? 0) >= 300 {
completionHandler?(FileProviderWebDavError(code: code, url: sourceURL))
}
@@ -411,14 +387,14 @@ extension WebDAVFileProvider: FileProviderReadWrite {
responseError = FileProviderWebDavError(code: rCode, url: url)
}
if let data = data {
let xresponse = self.parseXMLResponse(data)
let xresponse = DavResponse.parse(xmlResponse: data, baseURL: self.baseURL)
var fileObjects = [WebDavFileObject]()
for attr in xresponse {
let path = attr.href.path
if !((path as NSString).lastPathComponent.contains(query)) {
continue
}
let fileObject = self.mapToFileObject(attr)
let fileObject = WebDavFileObject(attr)
fileObjects.append(fileObject)
foundItemHandler?(fileObject)
}
@@ -458,37 +434,34 @@ extension WebDAVFileProvider: FileProvider {
// MARK: WEBDAV XML response implementation
internal extension WebDAVFileProvider {
struct DavResponse {
let href: URL
let hrefString: String
let status: Int?
let prop: [String: String]
fileprivate func delegateNotify(_ operation: FileOperationType, error: Error?) {
DispatchQueue.main.async(execute: {
if error == nil {
self.delegate?.fileproviderSucceed(self, operation: operation)
} else {
self.delegate?.fileproviderFailed(self, operation: operation)
}
})
}
}
struct DavResponse {
let href: URL
let hrefString: String
let status: Int?
let prop: [String: String]
fileprivate func parseXMLResponse(_ response: Data) -> [DavResponse] {
var result = [DavResponse]()
do {
let xml = try AEXMLDocument(xml: response)
var rootnode = xml.root
var responsetag = "response"
for node in rootnode.all ?? [] where node.name.lowercased().hasSuffix("multistatus") {
rootnode = node
init? (_ node: AEXMLElement, baseURL: URL?) {
func removeSlash(_ str: String) -> String {
if str.hasPrefix("/") {
return str.substring(from: str.index(after: str.startIndex))
} else {
return str
}
for node in rootnode.children where node.name.lowercased().hasSuffix("response") {
responsetag = node.name
break
}
for responseNode in rootnode[responsetag].all ?? [] {
if let davResponse = mapNodeToDavResponse(responseNode) {
result.append(davResponse)
}
}
} catch _ {
}
return result
}
fileprivate func mapNodeToDavResponse(_ node: AEXMLElement) -> DavResponse? {
// find node names with namespace
var hreftag = "href"
var statustag = "status"
var propstattag = "propstat"
@@ -503,75 +476,107 @@ internal extension WebDAVFileProvider {
propstattag = node.name
}
}
let href = node[hreftag].value
if let href = href {
let phrefURL: URL?
var relativePath = href.replacingOccurrences(of: self.baseURL?.absoluteString ?? "", with: "/")
if relativePath.hasPrefix("http://") || relativePath.hasPrefix("http://") {
phrefURL = URL(string: href)
} else {
if relativePath.hasPrefix("/") {
relativePath.remove(at: relativePath.startIndex)
}
phrefURL = URL(string: relativePath, relativeTo: self.baseURL) ?? self.baseURL
}
//let hrefURL = URL(string: href)
guard let hrefURL = phrefURL else { return nil }
var status: Int?
let statusDesc = (node[statustag].string).components(separatedBy: " ")
if statusDesc.count > 2 {
status = Int(statusDesc[1])
}
var propDic = [String: String]()
let propStatNode = node[propstattag]
for node in propStatNode.children where node.name.lowercased().hasSuffix("status"){
statustag = node.name
break
}
let statusDesc2 = (propStatNode[statustag].string).components(separatedBy: " ")
if statusDesc2.count > 2 {
status = Int(statusDesc2[1])
}
var proptag = "prop"
for tnode in propStatNode.children where tnode.name.lowercased().hasSuffix("prop") {
proptag = tnode.name
break
}
for propItemNode in propStatNode[proptag].children {
propDic[propItemNode.name.components(separatedBy: ":").last!.lowercased()] = propItemNode.value
if propItemNode.name.hasSuffix("resourcetype") && propItemNode.xml.contains("collection") {
propDic["getcontenttype"] = "httpd/unix-directory"
}
}
return DavResponse(href: hrefURL, hrefString: href, status: status, prop: propDic)
guard let hrefString = node[hreftag].value else { return nil }
// trying to figure out relative path out of href
let hrefAbsolute = URL(string: hrefString, relativeTo: baseURL)?.absoluteString ?? hrefString
let relativePath = hrefAbsolute.replacingOccurrences(of: baseURL?.absoluteString ?? "", with: "", options: .anchored, range: nil)
let hrefURL = URL(string: removeSlash(relativePath), relativeTo: baseURL) ?? baseURL
guard let href = hrefURL?.standardized else { return nil }
// reading status and properties
var status: Int?
let statusDesc = (node[statustag].string).components(separatedBy: " ")
if statusDesc.count > 2 {
status = Int(statusDesc[1])
}
return nil
var propDic = [String: String]()
let propStatNode = node[propstattag]
for node in propStatNode.children where node.name.lowercased().hasSuffix("status"){
statustag = node.name
break
}
let statusDesc2 = (propStatNode[statustag].string).components(separatedBy: " ")
if statusDesc2.count > 2 {
status = Int(statusDesc2[1])
}
var proptag = "prop"
for tnode in propStatNode.children where tnode.name.lowercased().hasSuffix("prop") {
proptag = tnode.name
break
}
for propItemNode in propStatNode[proptag].children {
propDic[propItemNode.name.components(separatedBy: ":").last!.lowercased()] = propItemNode.value
if propItemNode.name.hasSuffix("resourcetype") && propItemNode.xml.contains("collection") {
propDic["getcontenttype"] = "httpd/unix-directory"
}
}
self.href = href
self.hrefString = hrefString
self.status = status
self.prop = propDic
}
fileprivate func mapToFileObject(_ davResponse: DavResponse) -> WebDavFileObject {
var href = davResponse.href
if href.baseURL == nil {
href = url(of: href.path)
static func parse(xmlResponse: Data, baseURL: URL?) -> [DavResponse] {
var result = [DavResponse]()
do {
let xml = try AEXMLDocument(xml: xmlResponse)
var rootnode = xml.root
var responsetag = "response"
for node in rootnode.all ?? [] where node.name.lowercased().hasSuffix("multistatus") {
rootnode = node
}
for node in rootnode.children where node.name.lowercased().hasSuffix("response") {
responsetag = node.name
break
}
for responseNode in rootnode[responsetag].all ?? [] {
if let davResponse = DavResponse(responseNode, baseURL: baseURL) {
result.append(davResponse)
}
}
} catch _ {
}
return result
}
}
public final class WebDavFileObject: FileObject {
internal init(_ davResponse: DavResponse) {
let href = davResponse.href
let name = davResponse.prop["displayname"] ?? (davResponse.hrefString.removingPercentEncoding! as NSString).lastPathComponent
let fileObject = WebDavFileObject(url: href, name: name, path: href.path)
fileObject.size = Int64(davResponse.prop["getcontentlength"] ?? "-1") ?? NSURLSessionTransferSizeUnknown
fileObject.creationDate = self.resolve(dateString: davResponse.prop["creationdate"] ?? "")
fileObject.modifiedDate = self.resolve(dateString: davResponse.prop["getlastmodified"] ?? "")
fileObject.contentType = davResponse.prop["getcontenttype"] ?? "octet/stream"
fileObject.type = fileObject.contentType == "httpd/unix-directory" ? .directory : .regular
fileObject.entryTag = davResponse.prop["getetag"]
return fileObject
let relativePath = href.relativePath
let path = relativePath.hasPrefix("/") ? relativePath : ("/" + relativePath)
super.init(url: href, name: name, path: path)
self.size = Int64(davResponse.prop["getcontentlength"] ?? "-1") ?? NSURLSessionTransferSizeUnknown
self.creationDate = resolve(dateString: davResponse.prop["creationdate"] ?? "")
self.modifiedDate = resolve(dateString: davResponse.prop["getlastmodified"] ?? "")
self.contentType = davResponse.prop["getcontenttype"] ?? "octet/stream"
self.isHidden = (Int(davResponse.prop["ishidden"] ?? "0") ?? 0) > 0
self.type = self.contentType == "httpd/unix-directory" ? .directory : .regular
self.entryTag = davResponse.prop["getetag"]
}
fileprivate func delegateNotify(_ operation: FileOperationType, error: Error?) {
DispatchQueue.main.async(execute: {
if error == nil {
self.delegate?.fileproviderSucceed(self, operation: operation)
} else {
self.delegate?.fileproviderFailed(self, operation: operation)
}
})
/// MIME type of the file
open internal(set) var contentType: String {
get {
return allValues["NSURLContentTypeKey"] as? String ?? ""
}
set {
allValues["NSURLContentTypeKey"] = newValue
}
}
/// HTTP E-Tag, can be used to mark changed files
open internal(set) var entryTag: String? {
get {
return allValues["NSURLEntryTagKey"] as? String
}
set {
allValues["NSURLEntryTagKey"] = newValue
}
}
}