Fixed OneDrive provider
- Added authentication document - Fixed date parsing
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
# Authentication
|
||||
|
||||
Dropbox and OneDrive are using OAuth2 authentication method. Here is sample codes to get Bearer token using [OAuthSwift](https://github.com/OAuthSwift/OAuthSwift)
|
||||
|
||||
## Handling url scheme in App delegate
|
||||
|
||||
Add these lines to your application delegate:
|
||||
|
||||
```swift
|
||||
extension AppDelegate {
|
||||
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
|
||||
if url.host == "oauth-callback" {
|
||||
OAuthSwift.handle(url: url)
|
||||
}
|
||||
|
||||
// HANDLING OTHER PATERNS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dropbox
|
||||
|
||||
Your client id and secret must be given by Dropbox developer portal. Bearer tokens created by this method are permanent.
|
||||
|
||||
```swift
|
||||
let appScheme = "YOUR_APP_SCHEME"
|
||||
oauth = OAuth2Swift(consumerKey: "CLIENT_ID",
|
||||
consumerSecret: "CLIENT_SECRET",
|
||||
authorizeUrl: "https://www.dropbox.com/oauth2/authorize",
|
||||
responseType: "token")!
|
||||
oauth.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauth)
|
||||
_ = oauth.authorize(withCallbackURL: URL(string: "\(appScheme)://oauth-callback/dropbox")!,
|
||||
scope: "", state:"DROPBOX",
|
||||
success: { credential, response, parameters in
|
||||
let urlcredential = URLCredential(user: user ?? "anonymous", password: credential.oauthToken, persistence: .permanent)
|
||||
// TODO: Save credential in keychain
|
||||
// TODO: Create Dropbox provider using urlcredential
|
||||
}, failure: { error in
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## OneDrive
|
||||
|
||||
Your client id must be given by Microsoft developer portal. OneDrive doesn't need client secret for native apps, but will need to refresh token every one hour.
|
||||
|
||||
We must save refresh token in adition to bearer token and use it when we get `.unauthorized` 401 HTTP error in completion handlers to get a new bearer token.
|
||||
|
||||
```swift
|
||||
let appScheme = "YOUR_APP_SCHEME"
|
||||
oauth = OAuth2Swift.init(consumerKey: "CLIENT_ID",
|
||||
consumerSecret: "",
|
||||
authorizeUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
||||
accessTokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
|
||||
responseType: "code")!
|
||||
oauth.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauth)
|
||||
if let refreshToken = {SAVED_REFRESH_TOKEN} {
|
||||
oauth.renewAccessToken(withRefreshToken: token,
|
||||
success: { credential, response, parameters in
|
||||
let urlcredential = URLCredential(user: user ?? "anonymous", password: credential.oauthToken, persistence: .permanent)
|
||||
let refreshToken = credential.oauthRefreshToken
|
||||
// TODO: Save refreshToken in keychain
|
||||
// TODO: Save credential in keychain
|
||||
// TODO: Create OneDrive provider using urlcredential
|
||||
}, failure: { error in
|
||||
print(error.localizedDescription)
|
||||
// TODO: Clear saved refresh token and call this method again to get authorization token
|
||||
})
|
||||
} else {
|
||||
_ = oauth.authorize(
|
||||
withCallbackURL: URL(string: "\(appScheme)://oauth-callback/onedrive")!,
|
||||
scope: "offline_access User.Read Files.ReadWrite Files.ReadWrite.All Files.ReadWrite.AppFolder", state: "ONEDRIVE",
|
||||
success: { credential, response, parameters in
|
||||
let credential = URLCredential(user: user ?? "anonymous", password: credential.oauthToken, persistence: .permanent)
|
||||
// TODO: Save refreshToken in keychain
|
||||
// TODO: Save credential in keychain
|
||||
// TODO: Create OneDrive provider using credential
|
||||
}, failure: { error in
|
||||
print(error.localizedDescription)
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -156,9 +156,9 @@ let webdavProvider = WebDAVFileProvider(baseURL: URL(string: "http://www.example
|
||||
|
||||
* In case you want to connect non-secure servers for WebDAV (http) or FTP 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 & OneDrive, 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 Dropbox & OneDrive, 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. Please see [Authentication.md](Authentication.md) for detailed instruction.
|
||||
|
||||
For interaction with UI, set delegate variable of `FileProvider` object
|
||||
For interaction with UI, set delegate property of `FileProvider` object.
|
||||
|
||||
You can use `url(of:)` method if provider to get direct access url (local or remote files) for some file systems which allows to do so (Dropbox doesn't support and returns path simply wrapped in URL).
|
||||
|
||||
|
||||
@@ -478,32 +478,50 @@ internal extension TimeInterval {
|
||||
public extension Date {
|
||||
/// Date formats used commonly in internet messaging defined by various RFCs.
|
||||
public enum RFCStandards: String {
|
||||
/// Date format defined by usenet, commonly used in old implementations.
|
||||
/// Obsolete (2-digit year) date format defined by RFC 822 for http.
|
||||
case rfc822 = "EEE',' dd' 'MMM' 'yy HH':'mm':'ss z"
|
||||
/// Obsolete (2-digit year) date format defined by RFC 850 for usenet.
|
||||
case rfc850 = "EEEE',' dd'-'MMM'-'yy HH':'mm':'ss z"
|
||||
/// Date format defined by RFC 1132 for http.
|
||||
/// Date format defined by RFC 1123 for http.
|
||||
case rfc1123 = "EEE',' dd' 'MMM' 'yyyy HH':'mm':'ss z"
|
||||
/// Date format defined by ISO 8601, also defined in RFC 3339. Used by Dropbox.
|
||||
case iso8601 = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
|
||||
/// Date format defined by RFC 3339, as a profile of ISO 8601.
|
||||
case rfc3339 = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZZZ"
|
||||
/// Date format defined RFC 3339 as rare case with milliseconds.
|
||||
case rfc3339Extended = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSSZZZZZ"
|
||||
/// Date string returned by asctime() function.
|
||||
case asctime = "EEE MMM d HH':'mm':'ss yyyy"
|
||||
|
||||
// Defining a http alias allows changing default time format if a new RFC becomes standard.
|
||||
/// Equivalent to and defined by RFC 1123.
|
||||
public static let http = RFCStandards.rfc1123
|
||||
/// Equivalent to and defined by ISO 8610.
|
||||
public static let rfc3339 = RFCStandards.iso8601
|
||||
/// Equivalent to and defined by RFC 850.
|
||||
public static let usenet = RFCStandards.rfc850
|
||||
|
||||
// Sorted by commonness
|
||||
fileprivate static let allValues: [RFCStandards] = [.rfc1123, .rfc850, .iso8601, .asctime]
|
||||
/* re. [RFC7231 section-7.1.1.1](https://tools.ietf.org/html/rfc7231#section-7.1.1.1)
|
||||
"HTTP servers and client MUST accept all three HTTP-date formats" which are IMF-fixdate,
|
||||
obsolete RFC 850 format and ANSI C's asctime() format.
|
||||
|
||||
ISO 8601 format is common in JSON and XML fields, defined by RFC 3339 as a timestamp format.
|
||||
Though not mandated, we check string against them to allow using Date(rfcString:) in
|
||||
wider and more general sitations.
|
||||
|
||||
We use RFC 822 instead of RFC 1123 to convert from string because NSDateFormatter can parse
|
||||
both 2-digit and 4-digit year correctly when `dateFormat` year is 2-digit.
|
||||
|
||||
These values are sorted by frequency.
|
||||
*/
|
||||
fileprivate static let parsingCases: [RFCStandards] = [.rfc822, .rfc850, .asctime, .rfc3339, .rfc3339Extended]
|
||||
}
|
||||
|
||||
private static let posixLocale = Locale(identifier: "en_US_POSIX")
|
||||
private static let utcTimezone = TimeZone(identifier: "UTC")
|
||||
|
||||
/// Checks date string against various RFC standards and returns `Date`.
|
||||
public init?(rfcString: String) {
|
||||
let dateFor: DateFormatter = DateFormatter()
|
||||
dateFor.locale = Locale(identifier: "en_US")
|
||||
dateFor.locale = Date.posixLocale
|
||||
|
||||
for standard in RFCStandards.allValues {
|
||||
for standard in RFCStandards.parsingCases {
|
||||
dateFor.dateFormat = standard.rawValue
|
||||
if let date = dateFor.date(from: rfcString) {
|
||||
self = date
|
||||
@@ -515,11 +533,12 @@ public extension Date {
|
||||
}
|
||||
|
||||
/// Formats date according to RFCs standard.
|
||||
public func format(with standard: RFCStandards, locale: Locale? = nil, timeZone: TimeZone? = nil) -> String {
|
||||
/// - Note: local and timezone paramters should be nil for `.http` standard
|
||||
internal func format(with standard: RFCStandards, locale: Locale? = nil, timeZone: TimeZone? = nil) -> String {
|
||||
let fm = DateFormatter()
|
||||
fm.dateFormat = standard.rawValue
|
||||
fm.timeZone = timeZone ?? TimeZone(identifier: "UTC")
|
||||
fm.locale = locale ?? Locale(identifier: "en_US_POSIX")
|
||||
fm.timeZone = timeZone ?? Date.utcTimezone
|
||||
fm.locale = locale ?? Date.posixLocale
|
||||
return fm.string(from: self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,16 @@ open class VolumeObject {
|
||||
allValues[.volumeNameKey] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The root directory of the resource’s volume, returned as an `URL` object.
|
||||
open internal(set) var uuid: String? {
|
||||
get {
|
||||
return allValues[.volumeUUIDStringKey] as? String
|
||||
}
|
||||
set {
|
||||
allValues[.volumeUUIDStringKey] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// the volume’s capacity in bytes, return -1 if is undetermined.
|
||||
open internal(set) var totalCapacity: Int64 {
|
||||
|
||||
@@ -307,12 +307,12 @@ open class HTTPFileProvider: FileProviderBasicRemote, FileProviderOperations, Fi
|
||||
// WebDAV will override this function
|
||||
}
|
||||
|
||||
fileprivate func doOperation(_ operation: FileOperationType, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> Progress? {
|
||||
internal func doOperation(_ operation: FileOperationType, overwrite: Bool = false, progress: Progress? = nil, completionHandler: SimpleCompletionHandler) -> Progress? {
|
||||
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: operation) ?? true == true else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let progress = Progress(totalUnitCount: 1)
|
||||
let progress = progress ?? Progress(totalUnitCount: 1)
|
||||
progress.setUserInfoObject(operation, forKey: .fileProvderOperationTypeKey)
|
||||
progress.kind = .file
|
||||
progress.setUserInfoObject(Progress.FileOperationKind.downloading, forKey: .fileOperationKindKey)
|
||||
@@ -321,12 +321,14 @@ open class HTTPFileProvider: FileProviderBasicRemote, FileProviderOperations, Fi
|
||||
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var serverError: FileProviderHTTPError?
|
||||
if let response = response as? HTTPURLResponse, response.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
|
||||
serverError = self.serverError(with: code, path: operation.source, data: data)
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse, FileProviderHTTPErrorCode(rawValue: response.statusCode) == .multiStatus, let data = data {
|
||||
self.multiStatusHandler(source: operation.source, data: data, completionHandler: completionHandler)
|
||||
if let response = response as? HTTPURLResponse {
|
||||
if response.statusCode >= 300, let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) {
|
||||
serverError = self.serverError(with: code, path: operation.source, data: data)
|
||||
}
|
||||
|
||||
if FileProviderHTTPErrorCode(rawValue: response.statusCode) == .multiStatus, let data = data {
|
||||
self.multiStatusHandler(source: operation.source, data: data, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
if serverError == nil && error == nil {
|
||||
@@ -334,6 +336,7 @@ open class HTTPFileProvider: FileProviderBasicRemote, FileProviderOperations, Fi
|
||||
} else {
|
||||
progress.cancel()
|
||||
}
|
||||
|
||||
completionHandler?(serverError ?? error)
|
||||
self.delegateNotify(operation, error: serverError ?? error)
|
||||
})
|
||||
|
||||
@@ -92,6 +92,13 @@ open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Microsoft Graph URL
|
||||
public static var graphURL = URL(string: "https://graph.microsoft.com/")!
|
||||
|
||||
/// Microsoft Graph URL
|
||||
public static var graphVersion = "v1.0"
|
||||
|
||||
/// Route for container, default is `.me`.
|
||||
open let route: Route
|
||||
|
||||
@@ -129,7 +136,8 @@ open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
|
||||
- cache: A URLCache to cache downloaded files and contents.
|
||||
*/
|
||||
public init(credential: URLCredential?, serverURL: URL? = nil, route: Route = .me, cache: URLCache? = nil) {
|
||||
let baseURL = serverURL?.absoluteURL ?? URL(string: "https://api.onedrive.com/")!
|
||||
let baseURL = (serverURL?.absoluteURL ?? OneDriveFileProvider.graphURL)
|
||||
.appendingPathComponent(OneDriveFileProvider.graphVersion, isDirectory: true)
|
||||
let refinedBaseURL = baseURL.absoluteString.hasSuffix("/") ? baseURL : baseURL.appendingPathComponent("")
|
||||
self.route = route
|
||||
super.init(baseURL: refinedBaseURL, credential: credential, cache: cache)
|
||||
@@ -233,7 +241,8 @@ open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
|
||||
/// Returns volume/provider information asynchronously.
|
||||
/// - Parameter volumeInfo: Information of filesystem/Provider returned by system/server.
|
||||
open override func storageProperties(completionHandler: @escaping (_ volumeInfo: VolumeObject?) -> Void) {
|
||||
var request = URLRequest(url: url(of: ""))
|
||||
let url = URL(string: route.drivePath, relativeTo: baseURL)!
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
request.setValue(authentication: self.credential, with: .oAuth2)
|
||||
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
@@ -244,6 +253,7 @@ open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
|
||||
|
||||
let volume = VolumeObject(allValues: [:])
|
||||
volume.url = request.url
|
||||
volume.uuid = json["id"] as? String
|
||||
volume.name = json["name"] as? String
|
||||
volume.creationDate = (json["createdDateTime"] as? String).flatMap { Date(rfcString: $0) }
|
||||
volume.totalCapacity = (json["quota"]?["total"] as? NSNumber)?.int64Value ?? -1
|
||||
@@ -328,15 +338,18 @@ open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
|
||||
*/
|
||||
open func url(of path: String, modifier: String? = nil) -> URL {
|
||||
var url: URL = baseURL!
|
||||
var rpath: String = path
|
||||
let isId = path.hasPrefix("id:")
|
||||
|
||||
var rpath: String = path.replacingOccurrences(of: "id:", with: "", options: .anchored)
|
||||
|
||||
//url.appendPathComponent("v1.0")
|
||||
url.appendPathComponent(route.drivePath)
|
||||
|
||||
if isId {
|
||||
url.appendPathComponent("root:")
|
||||
} else {
|
||||
if rpath.isEmpty {
|
||||
url.appendPathComponent("root")
|
||||
} else if isId {
|
||||
url.appendPathComponent("items")
|
||||
} else {
|
||||
url.appendPathComponent("root:")
|
||||
}
|
||||
|
||||
rpath = rpath.trimmingCharacters(in: pathTrimSet)
|
||||
@@ -442,23 +455,24 @@ open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
|
||||
case .fetch(path: let path):
|
||||
method = "GET"
|
||||
url = self.url(of: path, modifier: "content")
|
||||
case .create(path: let path) where path.hasSuffix("/"):
|
||||
method = "POST"
|
||||
let parent = (path as NSString).deletingLastPathComponent
|
||||
url = self.url(of: parent, modifier: "children")
|
||||
case .modify(path: let path):
|
||||
method = "PUT"
|
||||
let queryStr = overwrite ? "" : "?@name.conflictBehavior=fail"
|
||||
url = self.url(of: path, modifier: "content\(queryStr)")
|
||||
case .create(path: let path):
|
||||
method = "CREATE"
|
||||
url = self.url(of: path)
|
||||
case .copy(let source, let dest) where !source.hasPrefix("file://") && !dest.hasPrefix("file://"):
|
||||
method = "POST"
|
||||
url = self.url(of: source)
|
||||
case .copy(let source, let dest) where source.hasPrefix("file://"):
|
||||
url = URL(string: self.url(of: path, modifier: "content").absoluteString + queryStr)!
|
||||
case .copy(source: let source, destination: let dest) where source.hasPrefix("file://"):
|
||||
method = "PUT"
|
||||
let queryStr = overwrite ? "" : "?@name.conflictBehavior=fail"
|
||||
url = self.url(of: dest, modifier: "content\(queryStr)")
|
||||
case .copy(let source, let dest) where dest.hasPrefix("file://"):
|
||||
url = URL(string: self.url(of: dest, modifier: "content").absoluteString + queryStr)!
|
||||
case .copy(source: let source, destination: let dest) where dest.hasPrefix("file://"):
|
||||
method = "GET"
|
||||
url = self.url(of: source, modifier: "content")
|
||||
case .copy(source: let source, destination: _):
|
||||
method = "POST"
|
||||
url = self.url(of: source, modifier: "copy")
|
||||
case .move(source: let source, destination: _):
|
||||
method = "PATCH"
|
||||
url = self.url(of: source)
|
||||
@@ -471,29 +485,40 @@ open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = method
|
||||
request.setValue(authentication: self.credential, with: .oAuth2)
|
||||
// Remove gzip to fix availability of progress per (Oleg Marchik)[https://github.com/evilutioner] PR (#61)
|
||||
request.setValue(acceptEncoding: .deflate)
|
||||
request.addValue(acceptEncoding: .identity)
|
||||
// Remove gzip to fix availability of progress re. (Oleg Marchik)[https://github.com/evilutioner] PR (#61)
|
||||
if method == "GET" {
|
||||
request.setValue(acceptEncoding: .deflate)
|
||||
request.addValue(acceptEncoding: .identity)
|
||||
}
|
||||
|
||||
switch operation {
|
||||
case .create(path: let path) where path.hasSuffix("/"):
|
||||
request.setValue(contentType: .json)
|
||||
var requestDictionary = [String: AnyObject]()
|
||||
let name = (path as NSString).lastPathComponent
|
||||
requestDictionary["name"] = name as NSString
|
||||
requestDictionary["folder"] = NSDictionary()
|
||||
requestDictionary["@microsoft.graph.conflictBehavior"] = "fail" as NSString
|
||||
request.httpBody = Data(jsonDictionary: requestDictionary)
|
||||
case .copy(let source, let dest) where !source.hasPrefix("file://") && !dest.hasPrefix("file://"),
|
||||
.move(source: let source, destination: let dest):
|
||||
request.setValue(contentType: .json)
|
||||
request.setValue(contentType: .json, charset: .utf8)
|
||||
let cdest = correctPath(dest) as NSString
|
||||
var parentRefrence: [String: AnyObject] = [:]
|
||||
var parentReference: [String: AnyObject] = [:]
|
||||
if cdest.hasPrefix("id:") {
|
||||
parentRefrence["id"] = cdest.components(separatedBy: "/").first as NSString?
|
||||
switch self.route {
|
||||
case .drive(uuid: let uuid):
|
||||
parentRefrence["driveId"] = uuid.uuidString as NSString
|
||||
default:
|
||||
break
|
||||
}
|
||||
parentReference["id"] = cdest.components(separatedBy: "/").first?.replacingOccurrences(of: "id:", with: "", options: .anchored) as NSString?
|
||||
} else {
|
||||
parentRefrence["path"] = cdest.deletingLastPathComponent as NSString
|
||||
parentReference["path"] = ("/drive/root:" as NSString).appendingPathComponent(cdest.deletingLastPathComponent) as NSString
|
||||
}
|
||||
switch self.route {
|
||||
case .drive(uuid: let uuid):
|
||||
parentReference["driveId"] = uuid.uuidString as NSString
|
||||
default:
|
||||
//parentReference["driveId"] = cachedDriveID as NSString? ?? ""
|
||||
break
|
||||
}
|
||||
var requestDictionary = [String: AnyObject]()
|
||||
requestDictionary["parentReference"] = parentRefrence as NSDictionary
|
||||
requestDictionary["parentReference"] = parentReference as NSDictionary
|
||||
requestDictionary["name"] = (cdest as NSString).lastPathComponent as NSString
|
||||
request.httpBody = Data(jsonDictionary: requestDictionary)
|
||||
default:
|
||||
@@ -531,7 +556,7 @@ open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
|
||||
}
|
||||
|
||||
open func publicLink(to path: String, completionHandler: @escaping ((_ link: URL?, _ attribute: FileObject?, _ expiration: Date?, _ error: Error?) -> Void)) {
|
||||
var request = URLRequest(url: self.url(of: path, modifier: "action.createLink"))
|
||||
var request = URLRequest(url: self.url(of: path, modifier: "createLink"))
|
||||
request.httpMethod = "POST"
|
||||
let requestDictionary: [String: AnyObject] = ["type": "view" as NSString]
|
||||
request.httpBody = Data(jsonDictionary: requestDictionary)
|
||||
@@ -557,10 +582,6 @@ open class OneDriveFileProvider: HTTPFileProvider, FileProviderSharing {
|
||||
|
||||
extension OneDriveFileProvider: ExtendedFileProvider {
|
||||
open func thumbnailOfFileSupported(path: String) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
open func propertiesOfFileSupported(path: String) -> Bool {
|
||||
let fileExt = (path as NSString).pathExtension.lowercased()
|
||||
switch fileExt {
|
||||
case "jpg", "jpeg", "bmp", "gif", "png", "tif", "tiff":
|
||||
@@ -569,24 +590,32 @@ extension OneDriveFileProvider: ExtendedFileProvider {
|
||||
return true
|
||||
case "mp4", "mpg", "3gp", "mov", "avi", "wmv":
|
||||
return true
|
||||
case "doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
open func propertiesOfFileSupported(path: String) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
open func thumbnailOfFile(path: String, dimension: CGSize?, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void)) -> Progress? {
|
||||
let url: URL
|
||||
if let dimension = dimension {
|
||||
url = self.url(of: path, modifier: "thumbnails/0/=c\(dimension.width)x\(dimension.height)/content")
|
||||
} else {
|
||||
url = self.url(of: path, modifier: "thumbnails/0/small/content")
|
||||
let thumbQuery: String
|
||||
switch dimension.map( {max($0.width, $0.height) } ) ?? 0 {
|
||||
case 0...96: thumbQuery = "small"
|
||||
case 97...176: thumbQuery = "medium"
|
||||
default: thumbQuery = "large"
|
||||
}
|
||||
|
||||
let url = self.url(of: path, modifier: "thumbnails")
|
||||
.appendingPathComponent("0").appendingPathComponent(thumbQuery)
|
||||
.appendingPathComponent("content")
|
||||
var request = URLRequest(url: url)
|
||||
request.setValue(authentication: credential, with: .oAuth2)
|
||||
let task = self.session.dataTask(with: request, completionHandler: { (data, response, error) in
|
||||
var image: ImageClass? = nil
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 400, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
|
||||
let responseError = self.serverError(with: rCode, path: path, data: data)
|
||||
completionHandler(nil, responseError)
|
||||
return
|
||||
|
||||
@@ -18,10 +18,9 @@ public struct FileProviderOneDriveError: FileProviderHTTPError {
|
||||
/// Containts path, url and attributes of a OneDrive file or resource.
|
||||
public final class OneDriveFileObject: FileObject {
|
||||
internal init(baseURL: URL?, name: String, path: String) {
|
||||
let rpath = (URL(string:path)?.appendingPathComponent(name).absoluteString)!.replacingOccurrences(of: "/", with: "", options: .anchored)
|
||||
let url = URL(string: rpath, relativeTo: baseURL) ?? URL(string: rpath)!
|
||||
let url = URL(string: path, relativeTo: baseURL) ?? baseURL
|
||||
|
||||
super.init(url: url, name: name, path: rpath.removingPercentEncoding ?? path)
|
||||
super.init(url: url, name: name, path: path)
|
||||
}
|
||||
|
||||
internal convenience init? (baseURL: URL?, route: OneDriveFileProvider.Route, jsonStr: String) {
|
||||
@@ -31,18 +30,31 @@ public final class OneDriveFileObject: FileObject {
|
||||
|
||||
internal convenience init? (baseURL: URL?, route: OneDriveFileProvider.Route, json: [String: AnyObject]) {
|
||||
guard let name = json["name"] as? String else { return nil }
|
||||
guard let path = json["parentReference"]?["path"] as? String else { return nil }
|
||||
var lPath = path.replacingOccurrences(of: route.drivePath, with: "", options: .anchored, range: nil)
|
||||
lPath = lPath.replacingOccurrences(of: "/:", with: "", options: .anchored)
|
||||
lPath = lPath.replacingOccurrences(of: "//", with: "", options: .anchored)
|
||||
self.init(baseURL: baseURL, name: name, path: lPath)
|
||||
guard let id = json["id"] as? String else { return nil }
|
||||
let path: String
|
||||
if let refpath = json["parentReference"]?["path"] as? String {
|
||||
let parentPath: String
|
||||
if let colonIndex = refpath.index(of: ":") {
|
||||
#if swift(>=4.0)
|
||||
parentPath = String(refpath[refpath.index(after: colonIndex)...])
|
||||
#else
|
||||
parentPath = refpath.substring(from: refpath.index(after: colonIndex))
|
||||
#endif
|
||||
} else {
|
||||
parentPath = refpath
|
||||
}
|
||||
path = (parentPath as NSString).appendingPathComponent(name)
|
||||
} else {
|
||||
path = "id:\(id)"
|
||||
}
|
||||
self.init(baseURL: baseURL, name: name, path: path)
|
||||
self.id = id
|
||||
self.size = (json["size"] as? NSNumber)?.int64Value ?? -1
|
||||
self.childrensCount = json["folder"]?["childCount"] as? Int
|
||||
self.modifiedDate = (json["lastModifiedDateTime"] as? String).flatMap { Date(rfcString: $0) }
|
||||
self.creationDate = (json["createdDateTime"] as? String).flatMap { Date(rfcString: $0) }
|
||||
self.type = json["folder"] != nil ? .directory : .regular
|
||||
self.contentType = (json["file"]?["mimeType"] as? String).flatMap(ContentMIMEType.init(rawValue:)) ?? .stream
|
||||
self.id = json["id"] as? String
|
||||
self.entryTag = json["eTag"] as? String
|
||||
let hashes = json["file"]?["hashes"] as? NSDictionary
|
||||
// checks for both sha1 or quickXor. First is available in personal drives, second in business one.
|
||||
@@ -313,8 +325,17 @@ internal extension OneDriveFileProvider {
|
||||
|
||||
if let audio = json["audio"] as? [String: Any] {
|
||||
for (key, value) in audio {
|
||||
var value = value
|
||||
if key == "bitrate" || key == "isVariableBitrate" { continue }
|
||||
let casedKey = spaceCamelCase(key)
|
||||
switch casedKey {
|
||||
case "Duration":
|
||||
value = (value as? Int64).map { (TimeInterval($0) / 1000).formatshort } as Any
|
||||
case "Bitrate":
|
||||
value = (value as? Int64).map { "\($0)kbps" } as Any
|
||||
default:
|
||||
break
|
||||
}
|
||||
add(key: casedKey, value: value)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user