mirror of
https://github.com/drewmccormack/ensembles.git
synced 2026-03-03 18:23:18 +01:00
312 lines
11 KiB
Objective-C
312 lines
11 KiB
Objective-C
//
|
|
// CDENodeCloudFileSystem.m
|
|
//
|
|
// Created by Drew McCormack on 2/17/14.
|
|
// Copyright (c) 2014 The Mental Faculty B.V. All rights reserved.
|
|
//
|
|
|
|
#import "CDENodeCloudFileSystem.h"
|
|
#import "CDEDefines.h"
|
|
#import "CDEFoundationAdditions.h"
|
|
#import "CDECloudFile.h"
|
|
#import "CDECloudDirectory.h"
|
|
#import "CDEFileDownloadOperation.h"
|
|
#import "CDEFileUploadOperation.h"
|
|
|
|
@interface CDENodeCloudFileSystem ()
|
|
|
|
@property (nonatomic, readwrite, assign, getter = isLoggedIn) BOOL loggedIn;
|
|
|
|
@end
|
|
|
|
|
|
@implementation CDENodeCloudFileSystem {
|
|
NSOperationQueue *operationQueue;
|
|
}
|
|
|
|
@synthesize username = username;
|
|
@synthesize password = password;
|
|
@synthesize baseURL = baseURL;
|
|
@synthesize loggedIn = loggedIn;
|
|
|
|
- (instancetype)initWithBaseURL:(NSURL *)newBaseURL
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
baseURL = newBaseURL;
|
|
loggedIn = NO;
|
|
operationQueue = [[NSOperationQueue alloc] init];
|
|
operationQueue.maxConcurrentOperationCount = 1;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)init
|
|
{
|
|
return [self initWithBaseURL:nil];
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[operationQueue cancelAllOperations];
|
|
}
|
|
|
|
#pragma mark KVO
|
|
|
|
+ (NSSet *)keyPathsForValuesAffectingIdentityToken
|
|
{
|
|
return [NSSet setWithObject:@"username"];
|
|
}
|
|
|
|
#pragma mark Connecting
|
|
|
|
- (BOOL)isConnected
|
|
{
|
|
return self.isLoggedIn;
|
|
}
|
|
|
|
- (void)connect:(CDECompletionBlock)completion
|
|
{
|
|
if (self.isConnected) {
|
|
if (completion) completion(nil);
|
|
}
|
|
else {
|
|
[self loginWithCompletion:^(NSError *error) {
|
|
if (error.code == CDEErrorCodeAuthenticationFailure && self.delegate) {
|
|
[self.delegate nodeCloudFileSystem:self updateLoginCredentialsWithCompletion:^(NSError *error) {
|
|
if (error) {
|
|
if (completion) completion(error);
|
|
}
|
|
else {
|
|
// Try the whole process again with new credentials
|
|
[self connect:completion];
|
|
}
|
|
}];
|
|
}
|
|
else {
|
|
if (completion) completion(error);
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (void)loginWithCompletion:(CDECompletionBlock)completion
|
|
{
|
|
NSURL *url = [self.baseURL URLByAppendingPathComponent:@"login" isDirectory:NO];
|
|
[self postJSONObject:nil toURL:url completion:^(NSError *error, NSDictionary *responseDict) {
|
|
self.loggedIn = (nil == error);
|
|
if (completion) completion(error);
|
|
}];
|
|
}
|
|
|
|
#pragma mark - User Identity
|
|
|
|
- (id <NSObject, NSCoding, NSCopying>)identityToken
|
|
{
|
|
return self.username;
|
|
}
|
|
|
|
#pragma mark - Checking File Existence
|
|
|
|
- (void)fileExistsAtPath:(NSString *)path completion:(CDEFileExistenceCallback)completion
|
|
{
|
|
NSURL *url = [self.baseURL URLByAppendingPathComponent:@"fileexists" isDirectory:NO];
|
|
[self postJSONObject:@{@"path":path} toURL:url completion:^(NSError *error, NSDictionary *responseDict) {
|
|
if (error) {
|
|
if (completion) completion(NO, NO, error);
|
|
return;
|
|
}
|
|
|
|
BOOL exists = [responseDict[@"exists"] boolValue];
|
|
BOOL isDir = [responseDict[@"isdir"] boolValue];
|
|
if (completion) completion(exists, isDir, nil);
|
|
}];
|
|
}
|
|
|
|
#pragma mark - Getting Directory Contents
|
|
|
|
- (void)contentsOfDirectoryAtPath:(NSString *)path completion:(CDEDirectoryContentsCallback)completion
|
|
{
|
|
NSURL *url = [self.baseURL URLByAppendingPathComponent:@"listdir" isDirectory:NO];
|
|
[self postJSONObject:@{@"path":path} toURL:url completion:^(NSError *error, NSDictionary *responseDict) {
|
|
NSArray *filenames = responseDict[@"files"];
|
|
NSArray *files = [filenames cde_arrayByTransformingObjectsWithBlock:^CDECloudFile* (NSString *filename) {
|
|
CDECloudFile *file = [[CDECloudFile alloc] init];
|
|
file.name = filename;
|
|
file.path = [path stringByAppendingPathComponent:filename];
|
|
return file;
|
|
}];
|
|
if (completion) completion(files, error);
|
|
}];
|
|
}
|
|
|
|
#pragma mark - Creating Directories
|
|
|
|
- (void)createDirectoryAtPath:(NSString *)path completion:(CDECompletionBlock)completion
|
|
{
|
|
// S3 doesn't have directories, so just indicate success
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (completion) completion(nil);
|
|
});
|
|
}
|
|
|
|
#pragma mark - Deleting
|
|
|
|
- (void)removeItemAtPath:(NSString *)path completion:(CDECompletionBlock)completion
|
|
{
|
|
NSURL *url = [self.baseURL URLByAppendingPathComponent:@"deleteurls" isDirectory:NO];
|
|
[self postJSONObject:@{@"paths" : @[path]} toURL:url completion:^(NSError *error, NSDictionary *responseDict) {
|
|
if (error) {
|
|
if (completion) completion(error);
|
|
return;
|
|
}
|
|
|
|
NSArray *urls = responseDict[@"urls"];
|
|
NSURL *url = [NSURL URLWithString:urls.lastObject];
|
|
[self sendRequestForURL:url HTTPMethod:@"DELETE" authenticate:NO contentType:nil body:nil completion:^(NSError *error, NSDictionary *responseDict) {
|
|
if (completion) completion(error);
|
|
}];
|
|
}];
|
|
}
|
|
|
|
#pragma mark - Uploading and Downloading
|
|
|
|
- (void)uploadLocalFile:(NSString *)fromPath toPath:(NSString *)toPath completion:(CDECompletionBlock)completion
|
|
{
|
|
NSURL *url = [self.baseURL URLByAppendingPathComponent:@"uploadurls" isDirectory:NO];
|
|
[self postJSONObject:@{@"paths" : @[toPath]} toURL:url completion:^(NSError *error, NSDictionary *responseDict) {
|
|
if (error) {
|
|
if (completion) completion(error);
|
|
return;
|
|
}
|
|
|
|
NSArray *urls = responseDict[@"urls"];
|
|
NSURL *url = [NSURL URLWithString:urls.lastObject];
|
|
CDEFileUploadOperation *operation = [[CDEFileUploadOperation alloc] initWithURL:url localPath:fromPath];
|
|
operation.completion = completion;
|
|
[operationQueue addOperation:operation];
|
|
}];
|
|
}
|
|
|
|
- (void)downloadFromPath:(NSString *)fromPath toLocalFile:(NSString *)toPath completion:(CDECompletionBlock)completion
|
|
{
|
|
NSURL *url = [self.baseURL URLByAppendingPathComponent:@"downloadurls" isDirectory:NO];
|
|
[self postJSONObject:@{@"paths" : @[fromPath]} toURL:url completion:^(NSError *error, NSDictionary *responseDict) {
|
|
if (error) {
|
|
if (completion) completion(error);
|
|
return;
|
|
}
|
|
|
|
NSArray *urls = responseDict[@"urls"];
|
|
NSURL *url = [NSURL URLWithString:urls.lastObject];
|
|
CDEFileDownloadOperation *operation = [[CDEFileDownloadOperation alloc] initWithURL:url localPath:toPath];
|
|
operation.completion = completion;
|
|
[operationQueue addOperation:operation];
|
|
}];
|
|
}
|
|
|
|
#pragma mark - Signing Up New User
|
|
|
|
- (void)signUpWithCompletion:(CDECompletionBlock)completion
|
|
{
|
|
NSURL *url = [self.baseURL URLByAppendingPathComponent:@"createuser" isDirectory:NO];
|
|
[self postJSONObject:@{@"email" : (self.username ? : @""), @"password" : (self.password ? : @"")} toURL:url completion:^(NSError *error, NSDictionary *responseDict) {
|
|
if (completion) completion(error);
|
|
}];
|
|
}
|
|
|
|
#pragma mark - Passwords
|
|
|
|
- (void)resetPasswordWithCompletion:(CDECompletionBlock)completion
|
|
{
|
|
NSURL *url = [self.baseURL URLByAppendingPathComponent:@"resetpassword" isDirectory:NO];
|
|
[self postJSONObject:@{@"email" : (self.username ? : @"")} toURL:url completion:^(NSError *error, NSDictionary *responseDict) {
|
|
if (completion) completion(error);
|
|
}];
|
|
}
|
|
|
|
- (void)changePasswordTo:(NSString *)newPassword withCompletion:(CDECompletionBlock)completion
|
|
{
|
|
NSURL *url = [self.baseURL URLByAppendingPathComponent:@"changepassword" isDirectory:NO];
|
|
[self postJSONObject:@{@"newpassword" : (newPassword ? : @"")} toURL:url completion:^(NSError *error, NSDictionary *responseDict) {
|
|
if (!error) self.password = newPassword;
|
|
if (completion) completion(error);
|
|
}];
|
|
}
|
|
|
|
#pragma mark - Requests
|
|
|
|
- (void)postJSONObject:(id)JSONObject toURL:(NSURL *)url completion:(void(^)(NSError *error, NSDictionary *responseDict))completion
|
|
{
|
|
NSError *error;
|
|
NSData *data = nil;
|
|
if (JSONObject) data = [NSJSONSerialization dataWithJSONObject:JSONObject options:0 error:&error];
|
|
if (JSONObject && !data) {
|
|
if (completion) completion(error, nil);
|
|
return;
|
|
}
|
|
[self sendRequestForURL:url HTTPMethod:@"POST" authenticate:YES contentType:@"application/json" body:data completion:completion];
|
|
}
|
|
|
|
- (void)sendRequestForURL:(NSURL *)url HTTPMethod:(NSString *)method authenticate:(BOOL)authenticate contentType:(NSString *)contentType body:(NSData *)bodyObject completion:(void(^)(NSError *error, NSDictionary *responseDict))completion
|
|
{
|
|
// Create request
|
|
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20.0];
|
|
request.HTTPMethod = method;
|
|
if (bodyObject) request.HTTPBody = bodyObject;
|
|
if (contentType) [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
|
|
|
|
// Basic Auth
|
|
if (authenticate && self.username.length > 0 && self.password.length > 0) {
|
|
NSString *authString = [NSString stringWithFormat:@"%@:%@", self.username, self.password];
|
|
NSData *authData = [authString dataUsingEncoding:NSUTF8StringEncoding];
|
|
NSString *base64AuthString = [authData cde_base64String];
|
|
NSString *authValue = [NSString stringWithFormat:@"Basic %@", base64AuthString];
|
|
[request setValue:authValue forHTTPHeaderField:@"Authorization"];
|
|
}
|
|
|
|
// Send request
|
|
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
|
|
// Check error
|
|
BOOL isAuthError = NO;
|
|
if (error) isAuthError = ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorUserCancelledAuthentication);
|
|
if (error && !isAuthError) {
|
|
if (completion) completion(error, nil);
|
|
return;
|
|
}
|
|
|
|
// Check HTTP status
|
|
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
|
NSInteger statusCode = httpResponse.statusCode;
|
|
BOOL statusOK = (statusCode >= 200 && statusCode < 300);
|
|
BOOL authFailed = (statusCode == 401 || isAuthError);
|
|
if (authFailed) self.password = nil;
|
|
if (!statusOK) {
|
|
NSInteger code = authFailed ? CDEErrorCodeAuthenticationFailure : CDEErrorCodeServerError;
|
|
NSDictionary *userInfo = @{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"HTTP status code was %d", statusCode]};
|
|
error = [NSError errorWithDomain:CDEErrorDomain code:code userInfo:userInfo];
|
|
if (completion) completion(error, nil);
|
|
return;
|
|
}
|
|
|
|
// Parse Body
|
|
NSDictionary *responseDict = nil;
|
|
if (data) responseDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
|
|
|
// Check for JSON error
|
|
if (data.length == 0 || (responseDict && [responseDict[@"success"] boolValue])) {
|
|
if (completion) completion(nil, responseDict);
|
|
}
|
|
else {
|
|
NSString *message = responseDict ? responseDict[@"error"] : @"No response";
|
|
NSDictionary *userInfo = @{NSLocalizedDescriptionKey : message};
|
|
error = [NSError errorWithDomain:CDEErrorDomain code:CDEErrorCodeServerError userInfo:userInfo];
|
|
if (completion) completion(error, nil);
|
|
}
|
|
}];
|
|
}
|
|
|
|
@end
|
|
|
|
|