FMCPlanner2/FMC Planner 2/DropboxV2ObjC.m

672 lines
31 KiB
Objective-C

//
// DropboxV2ObjC.m
// FMC Planner 2
//
// Created by Kilian Hofmann on 29.03.16.
// Copyright © 2016 Kilian Hofmann. All rights reserved.
//
#import "DropboxV2ObjC.h"
@implementation DropboxV2ObjC
- (DropboxV2ObjC *)init
{
self = [super init];
self.rootDirectory = @"";
self.kJSONNullObject = [@"null" dataUsingEncoding:NSASCIIStringEncoding];
self.kDropboxProtectionSpace =
[[NSURLProtectionSpace alloc] initWithHost:@"dropbox"
port:443
protocol:@"HTTP"
realm:nil
authenticationMethod:@"OAuth2"];
return self;
}
#pragma mark - OAuth stuff
- (BOOL)authorizeUserWithToke:(NSURL *)token
completion:(void (^)(void))handler
presenter:(UIViewController *)presenter
{
NSScanner *scan = [NSScanner scannerWithString:token.absoluteString];
NSString *error = [[NSString alloc] init];
[scan scanUpToString:@"&error=" intoString:&error];
if (![error isEqualToString:token.absoluteString]) {
handler();
return false;
}
// No error, refine token
NSString *tokenUnrefined = [[NSString alloc] init];
scan = [NSScanner scannerWithString:token.absoluteString];
[scan scanUpToString:@"&" intoString:&tokenUnrefined];
NSString *tokenRefined = [tokenUnrefined substringFromIndex:21];
_token = tokenRefined;
NSURLCredential *credential = [NSURLCredential
credentialWithUser:@"Dropbox"
password:_token
persistence:NSURLCredentialPersistencePermanent];
[[NSURLCredentialStorage sharedCredentialStorage]
setCredential:credential
forProtectionSpace:_kDropboxProtectionSpace];
handler();
return true;
}
- (void)deauthorizeUserWithPresenter:(UIViewController *)presenter
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
request.URL = [NSURL
URLWithString:@"https://api.dropboxapi.com/1/disable_access_token"];
[request addValue:[NSString stringWithFormat:@"Bearer %@", _token]
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Authorization")
.precomposedStringWithCanonicalMapping];
[request addValue:(@"application/json")
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Content-Type")
.precomposedStringWithCanonicalMapping];
request.HTTPMethod = @"POST";
request.HTTPBody = _kJSONNullObject;
[NSURLConnection
sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data,
NSError *error) {
if (data != nil &&
[[NSJSONSerialization JSONObjectWithData:data
options:0
error:nil]
isEqualToDictionary:@{}]) {
NSDictionary *credentials =
[[NSURLCredentialStorage sharedCredentialStorage]
credentialsForProtectionSpace:
_kDropboxProtectionSpace];
NSURLCredential *credential =
[credentials.objectEnumerator nextObject];
[[NSURLCredentialStorage sharedCredentialStorage]
removeCredential:credential
forProtectionSpace:_kDropboxProtectionSpace];
}
else if (error != nil) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[SharedDeclerations
presentErrorAlert:error
presenter:presenter.navigationController
.topViewController];
}];
}
else if (![[NSJSONSerialization JSONObjectWithData:data
options:0
error:nil]
isEqualToDictionary:@{}]) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if ([[NSJSONSerialization JSONObjectWithData:data
options:0
error:nil]
isKindOfClass:[NSDictionary class]]) {
[SharedDeclerations
presentErrorAlert:
[NSError
errorWithDomain:DropboxErrorDomain
code:100
userInfo:@{
[[NSJSONSerialization
JSONObjectWithData:data
options:0
error:nil]
valueForKey:
DropboxErrorUserInfo] :
NSLocalizedDescriptionKey
}]
presenter:presenter
.navigationController
.topViewController];
}
else {
[SharedDeclerations
presentErrorAlert:
[NSError
errorWithDomain:DropboxErrorDomain
code:100
userInfo:@{
[[NSString alloc]
initWithData:data
encoding:
NSASCIIStringEncoding] :
NSLocalizedDescriptionKey
}]
presenter:presenter
.navigationController
.topViewController];
}
}];
}
}];
}
#pragma mark - File and directory operations
/**
* Pass nil as path for root dir
* @param completion executed after completion
*/
- (void)contentsOfPath:(NSString *)path
completion:(void (^)(NSArray *data))handler
presenter:(UIViewController *)presenter
{
if (path == nil) {
path = _rootDirectory;
}
else {
path = [NSString stringWithFormat:@"/%@", path];
}
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
request.URL =
[NSURL URLWithString:@"https://api.dropboxapi.com/2/files/list_folder"];
[request addValue:[NSString stringWithFormat:@"Bearer %@", _token]
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Authorization")
.precomposedStringWithCanonicalMapping];
[request addValue:(@"application/json")
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Content-Type")
.precomposedStringWithCanonicalMapping];
request.HTTPMethod = @"POST";
NSData *data = [NSJSONSerialization dataWithJSONObject:@{
@"path" : path.precomposedStringWithCanonicalMapping
}
options:0
error:nil];
request.HTTPBody = [self createJSONExcapedHTTPBody:data];
[NSURLConnection
sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data,
NSError *error) {
if (data != nil) {
data = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
if ([data isKindOfClass:[NSDictionary class]]) {
NSArray *data2 = [data valueForKey:@"entries"];
if (data2 == nil) {
[[NSOperationQueue
mainQueue] addOperationWithBlock:^{
if ([[NSJSONSerialization
JSONObjectWithData:data
options:0
error:nil]
isKindOfClass:[NSDictionary class]]) {
[SharedDeclerations
presentErrorAlert:
[NSError
errorWithDomain:
DropboxErrorDomain
code:100
userInfo:@{
[[NSJSONSerialization
JSONObjectWithData:
data
options:
0
error:
nil]
valueForKey:
DropboxErrorUserInfo] :
NSLocalizedDescriptionKey
}]
presenter:
presenter
.navigationController
.topViewController];
}
else {
[SharedDeclerations
presentErrorAlert:
[NSError
errorWithDomain:
DropboxErrorDomain
code:100
userInfo:@{
[[NSString alloc]
initWithData:
data
encoding:
NSASCIIStringEncoding] :
NSLocalizedDescriptionKey
}]
presenter:
presenter
.navigationController
.topViewController];
}
}];
}
else {
handler(data2);
}
}
}
else if (error != nil) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[SharedDeclerations
presentErrorAlert:error
presenter:presenter.navigationController
.topViewController];
}];
}
}];
}
/**
* Specify if EXPORT or SAVE in file name
* IS synchronus URLRequest, best executed in background thread.
*/
- (void)downloadFromDropbox:(NSArray *)files
presenter:(UIViewController *)presenter
completion:(void (^)(void))handler
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
request.URL = [NSURL
URLWithString:@"https://content.dropboxapi.com/2/files/download"];
[request addValue:[NSString stringWithFormat:@"Bearer %@", _token]
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Authorization")
.precomposedStringWithCanonicalMapping];
[request addValue:(@"").precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Content-Type")];
request.HTTPMethod = @"POST";
request.HTTPBody = nil;
// File Path
for (NSString *string in files) {
NSData *data2 = [NSJSONSerialization dataWithJSONObject:@{
@"path" : [NSString stringWithFormat:@"/%@", string]
.precomposedStringWithCanonicalMapping
}
options:0
error:nil];
[request setValue:[[NSString alloc]
initWithData:[self
createJSONExcapedHTTPBody:data2]
encoding:NSASCIIStringEncoding]
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Dropbox-API-Arg")
.precomposedStringWithCanonicalMapping];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error == nil) {
if ([[NSJSONSerialization JSONObjectWithData:data
options:0
error:nil]
valueForKey:@"error_summary"] == nil) {
[data writeToFile:[SharedDeclerations savePathForFile:string]
atomically:YES];
}
else {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if ([[NSJSONSerialization JSONObjectWithData:data
options:0
error:nil]
isKindOfClass:[NSDictionary class]]) {
[SharedDeclerations
presentErrorAlert:
[NSError
errorWithDomain:DropboxErrorDomain
code:100
userInfo:@{
[[NSJSONSerialization
JSONObjectWithData:data
options:0
error:nil]
valueForKey:
DropboxErrorUserInfo] :
NSLocalizedDescriptionKey
}]
presenter:presenter.navigationController
.topViewController];
}
else {
[SharedDeclerations
presentErrorAlert:
[NSError
errorWithDomain:DropboxErrorDomain
code:100
userInfo:@{
[[NSString alloc]
initWithData:data
encoding:
NSASCIIStringEncoding] :
NSLocalizedDescriptionKey
}]
presenter:presenter.navigationController
.topViewController];
}
}];
}
}
else if (error != nil) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[SharedDeclerations
presentErrorAlert:error
presenter:presenter.navigationController
.topViewController];
}];
}
}
handler();
}
/**
* Specify if SAVE or OLDSAVE in file name
* IS synchronus URLRequest, best executed in background thread.
*/
- (void)uploadToDropbox:(NSArray *)files
presenter:(UIViewController *)presenter
completion:(void (^)(void))handler
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
request.URL =
[NSURL URLWithString:@"https://content.dropboxapi.com/2/files/upload"];
[request addValue:[NSString stringWithFormat:@"Bearer %@", _token]
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Authorization")
.precomposedStringWithCanonicalMapping];
[request addValue:(@"application/octet-stream")
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Content-Type")];
request.HTTPMethod = @"POST";
// File Path
for (NSString *string in files) {
NSData *data2 = [NSJSONSerialization dataWithJSONObject:@{
@"path" : [NSString stringWithFormat:@"/%@", string]
.precomposedStringWithCanonicalMapping,
@"mode" : @"overwrite".precomposedStringWithCanonicalMapping,
}
options:0
error:nil];
[request setValue:[[NSString alloc]
initWithData:[self
createJSONExcapedHTTPBody:data2]
encoding:NSASCIIStringEncoding]
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Dropbox-API-Arg")
.precomposedStringWithCanonicalMapping];
request.HTTPBody = [NSData
dataWithContentsOfFile:[SharedDeclerations savePathForFile:string]];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error != nil) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[SharedDeclerations
presentErrorAlert:error
presenter:presenter.navigationController
.topViewController];
}];
}
if ([[NSJSONSerialization JSONObjectWithData:data options:0 error:nil]
valueForKey:@"error_summary"] != nil) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if ([[NSJSONSerialization JSONObjectWithData:data
options:0
error:nil]
isKindOfClass:[NSDictionary class]]) {
[SharedDeclerations
presentErrorAlert:
[NSError errorWithDomain:DropboxErrorDomain
code:100
userInfo:@{
[[NSJSONSerialization
JSONObjectWithData:data
options:0
error:nil]
valueForKey:
DropboxErrorUserInfo] :
NSLocalizedDescriptionKey
}]
presenter:presenter.navigationController
.topViewController];
}
else {
[SharedDeclerations
presentErrorAlert:
[NSError
errorWithDomain:DropboxErrorDomain
code:100
userInfo:@{
[[NSString alloc]
initWithData:data
encoding:
NSASCIIStringEncoding] :
NSLocalizedDescriptionKey
}]
presenter:presenter.navigationController
.topViewController];
}
}];
}
}
handler();
}
- (void)createFolderAtPath:(NSString *)path
presenter:(UIViewController *)presenter
{
if (path == nil) {
path = _rootDirectory;
}
else {
path = [NSString stringWithFormat:@"/%@", path];
}
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
request.URL = [NSURL
URLWithString:@"https://api.dropboxapi.com/2/files/create_folder"];
[request addValue:[NSString stringWithFormat:@"Bearer %@", _token]
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Authorization")
.precomposedStringWithCanonicalMapping];
[request addValue:(@"application/json")
.precomposedStringWithCanonicalMapping
forHTTPHeaderField:(@"Content-Type")
.precomposedStringWithCanonicalMapping];
request.HTTPMethod = @"POST";
NSData *data = [NSJSONSerialization dataWithJSONObject:@{
@"path" : path.precomposedStringWithCanonicalMapping
}
options:0
error:nil];
request.HTTPBody = [self createJSONExcapedHTTPBody:data];
[NSURLConnection
sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data,
NSError *error) {
if (error == nil) {
if ([[NSJSONSerialization JSONObjectWithData:data
options:0
error:nil]
valueForKey:@"error_summary"] == nil) {
}
else {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[SharedDeclerations
presentErrorAlert:
[NSError
errorWithDomain:DropboxErrorDomain
code:100
userInfo:@{
[[NSJSONSerialization
JSONObjectWithData:data
options:0
error:nil]
valueForKey:
DropboxErrorUserInfo] :
NSLocalizedDescriptionKey
}]
presenter:presenter
.navigationController
.topViewController];
}];
}
}
else if (error != nil) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[SharedDeclerations
presentErrorAlert:error
presenter:presenter.navigationController
.topViewController];
}];
}
}];
}
#pragma mark - Setup methods
- (void)setupDropboxWithHandler:(void (^)(void))handler
presenter:(UIViewController *)presenter
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
// Exports (PMDG format)
[self contentsOfPath:nil
completion:^(NSArray *data) {
BOOL exports = false;
for (NSDictionary *dict in data) {
if ([[dict valueForKey:@"name"]
isEqualToString:@"EXPORTS"]) {
exports = true;
break;
}
}
if (!exports) {
[self
createFolderAtPath:@"EXPORTS"
presenter:presenter.navigationController
.topViewController];
}
}
presenter:presenter];
// NavData (3 files from QW Navigraph data
[self contentsOfPath:nil
completion:^(NSArray *data) {
BOOL exports = false;
for (NSDictionary *dict in data) {
if ([[dict valueForKey:@"name"]
isEqualToString:@"NAVDATA"]) {
exports = true;
break;
}
}
if (!exports) {
[self
createFolderAtPath:@"NAVDATA"
presenter:presenter.navigationController
.topViewController];
}
}
presenter:presenter.navigationController.topViewController];
// Backups of Saves
[self contentsOfPath:nil
completion:^(NSArray *data) {
BOOL exports = false;
for (NSDictionary *dict in data) {
if ([[dict valueForKey:@"name"]
isEqualToString:@"SAVES"]) {
exports = true;
break;
}
}
if (!exports) {
[self
createFolderAtPath:@"SAVES"
presenter:presenter.navigationController
.topViewController];
}
}
presenter:presenter.navigationController.topViewController];
// Old saves (FMC Planner + (or X?))
[self contentsOfPath:nil
completion:^(NSArray *data) {
BOOL exports = false;
for (NSDictionary *dict in data) {
if ([[dict valueForKey:@"name"]
isEqualToString:@"OLDSAVES"]) {
exports = true;
break;
}
}
if (!exports) {
[self
createFolderAtPath:@"OLDSAVES"
presenter:presenter.navigationController
.topViewController];
}
}
presenter:presenter.navigationController.topViewController];
}];
handler();
}
#pragma mark - Helper methods
- (id)parseJSON:(NSData *)data
{
NSError *error = nil;
id object =
[NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (error == nil) {
return object;
}
else {
return
[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}
}
- (NSData *)createJSONExcapedHTTPBody:(NSData *)data
{
NSString *temp =
[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
temp = [temp stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"];
return [temp dataUsingEncoding:NSASCIIStringEncoding];
}
@end