#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // // DBRestClient.m // DropboxSDK // // Created by Brian Smith on 4/9/10. // Copyright 2010 Dropbox, Inc. All rights reserved. // #import "DBRestClient.h" #import "DBAccountInfo.h" #import "DBError.h" #import "DBMetadata.h" #import "DBRequest.h" #import "MPOAuthURLRequest.h" #import "MPURLRequestParameter.h" #import "MPOAuthSignatureParameter.h" #import "NSString+URLEscapingAdditions.h" NSString* kDBProtocolHTTP = @"http"; NSString* kDBProtocolHTTPS = @"https"; @interface NSString (Addons) - (NSString *)replace:(NSString *)targetString with:(NSString *)replaceString; @end @implementation NSString (Addons) - (NSString *)replace:(NSString *)targetString with:(NSString *)replaceString { NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSMutableString *temp = [NSMutableString new]; NSRange replaceRange = NSMakeRange(0, [self length]); NSRange rangeInOriginalString = replaceRange; int replaced = 0; while (1) { NSRange rangeToCopy; NSRange foundRange = [self rangeOfString:targetString options:0 range:rangeInOriginalString]; if (foundRange.length == 0) break; rangeToCopy = NSMakeRange(rangeInOriginalString.location, foundRange.location - rangeInOriginalString.location); [temp appendString:[self substringWithRange:rangeToCopy]]; [temp appendString:replaceString]; rangeInOriginalString.length -= NSMaxRange(foundRange) - rangeInOriginalString.location; rangeInOriginalString.location = NSMaxRange(foundRange); replaced++; if (replaced % 100 == 0) { [pool drain]; pool = [NSAutoreleasePool new]; } } if (rangeInOriginalString.length > 0) [temp appendString:[self substringWithRange:rangeInOriginalString]]; [pool drain]; return [temp autorelease]; } @end @interface DBRestClient () // This method escapes all URI escape characters except / + (NSString*)escapePath:(NSString*)path; - (NSMutableURLRequest*)requestWithProtocol:(NSString*)protocol host:(NSString*)host path:(NSString*)path parameters:(NSDictionary*)params; - (NSMutableURLRequest*)requestWithProtocol:(NSString*)protocol host:(NSString*)host path:(NSString*)path parameters:(NSDictionary*)params method:(NSString*)method; - (void)checkForAuthenticationFailure:(DBRequest*)request; @end @implementation DBRestClient - (id)initWithSession:(DBSession*)aSession { return [self initWithSession:aSession root:@"dropbox"]; } - (id)initWithSession:(DBSession*)aSession root:(NSString*)aRoot { if ((self = [super init])) { session = [aSession retain]; root = [aRoot retain]; requests = [[NSMutableSet alloc] init]; loadRequests = [[NSMutableDictionary alloc] init]; } return self; } - (void)dealloc { NSArray *requestsArray = [requests allObjects]; for (int i=0; i<[requestsArray count]; i++) { [[requestsArray objectAtIndex:i] cancel]; } [requests release]; for (int i=0; i<[[loadRequests allValues] count]; i++) { [[[loadRequests allValues] objectAtIndex:i] cancel]; } [loadRequests release]; [session release]; [root release]; [super dealloc]; } - (void)setDelegate:(id)theDelegate { delegate = theDelegate; } - (id)delegate { return delegate; } - (void)loginWithEmail:(NSString*)email password:(NSString*)password { NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys: email, @"email", password, @"password", nil]; NSURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTPS host:kDBDropboxAPIHost path:@"/token" parameters:params]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLogin:)] autorelease]; [requests addObject:request]; } - (void)requestDidLogin:(DBRequest*)request { if (request.error) { if ([delegate respondsToSelector:@selector(restClient:loginFailedWithError:)]) { [delegate restClient:self loginFailedWithError:request.error]; } } else { NSDictionary* result = (NSDictionary*)request.resultJSON; NSString* token = [result objectForKey:@"token"]; NSString* secret = [result objectForKey:@"secret"]; [session updateAccessToken:token accessTokenSecret:secret]; if ([delegate respondsToSelector:@selector(restClientDidLogin:)]) { [delegate restClientDidLogin:self]; } } [requests removeObject:request]; } - (void)loadMetadata:(NSString*)path withHash:(NSString*)hash { NSDictionary* params = nil; if (hash) { params = [NSDictionary dictionaryWithObject:hash forKey:@"hash"]; } NSString* fullPath = [NSString stringWithFormat:@"/metadata/%@%@", root, path]; NSURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTP host:kDBDropboxAPIHost path:fullPath parameters:params]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadMetadata:)] autorelease]; request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:root, @"root", path, @"path", nil]; [requests addObject:request]; } - (void)loadMetadata:(NSString*)path { [self loadMetadata:path withHash:nil]; } - (void)requestDidLoadMetadata:(DBRequest*)request { if (request.statusCode == 304) { if ([delegate respondsToSelector:@selector(restClient:metadataUnchangedAtPath:)]) { NSString* path = [request.userInfo objectForKey:@"path"]; [delegate restClient:self metadataUnchangedAtPath:path]; } } else if (request.error) { [self checkForAuthenticationFailure:request]; if ([delegate respondsToSelector:@selector(restClient:loadMetadataFailedWithError:)]) { [delegate restClient:self loadMetadataFailedWithError:request.error]; } } else { [NSThread detachNewThreadSelector:@selector(parseMetadataWithRequest:) toTarget:self withObject:request]; } [requests removeObject:request]; } - (void)parseMetadataWithRequest:(DBRequest*)request { NSAutoreleasePool* pool = [NSAutoreleasePool new]; NSDictionary* result = (NSDictionary*)[request resultJSON]; DBMetadata* metadata = [[[DBMetadata alloc] initWithDictionary:result] autorelease]; [self performSelectorOnMainThread:@selector(didParseMetadata:) withObject:metadata waitUntilDone:NO]; [pool drain]; } - (void)didParseMetadata:(DBMetadata*)metadata { if ([delegate respondsToSelector:@selector(restClient:loadedMetadata:)]) { [delegate restClient:self loadedMetadata:metadata]; } } - (void)loadFile:(NSString *)path intoPath:(NSString *)destinationPath { NSString* fullPath = [NSString stringWithFormat:@"/files/%@%@", root, path]; NSURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTPS host:kDBDropboxAPIContentHost path:fullPath parameters:nil]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadFile:)] autorelease]; request.resultFilename = destinationPath; request.downloadProgressSelector = @selector(requestLoadProgress:); request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys: root, @"root", path, @"path", destinationPath, @"destinationPath", nil]; [loadRequests setObject:request forKey:path]; } - (void)cancelFileLoad:(NSString*)path { DBRequest* outstandingRequest = [loadRequests objectForKey:path]; if (outstandingRequest) { [outstandingRequest cancel]; [loadRequests removeObjectForKey:path]; } } - (void)requestLoadProgress:(DBRequest*)request { if ([delegate respondsToSelector:@selector(restClient:loadProgress:forFile:)]) { [delegate restClient:self loadProgress:request.downloadProgress forFile:request.resultFilename]; } } - (void)restClient:(DBRestClient*)restClient loadedFile:(NSString*)destPath contentType:(NSString*)contentType eTag:(NSString*)eTag { // Empty selector to get the signature from } - (void)requestDidLoadFile:(DBRequest*)request { NSString* path = [request.userInfo objectForKey:@"path"]; if (request.error) { [self checkForAuthenticationFailure:request]; if ([delegate respondsToSelector:@selector(restClient:loadFileFailedWithError:)]) { [delegate restClient:self loadFileFailedWithError:request.error]; } } else { NSString* filename = request.resultFilename; NSDictionary* headers = [request.response allHeaderFields]; NSString* contentType = [headers objectForKey:@"Content-Type"]; NSString* eTag = [headers objectForKey:@"Etag"]; if ([delegate respondsToSelector:@selector(restClient:loadedFile:)]) { [delegate restClient:self loadedFile:filename]; } else if ([delegate respondsToSelector:@selector(restClient:loadedFile:contentType:)]) { [delegate restClient:self loadedFile:filename contentType:contentType]; } else if ([delegate respondsToSelector:@selector(restClient:loadedFile:contentType:eTag:)]) { // This code is for the official Dropbox client to get eTag information from the server NSMethodSignature* signature = [self methodSignatureForSelector:@selector(restClient:loadedFile:contentType:eTag:)]; NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget:delegate]; [invocation setSelector:@selector(restClient:loadedFile:contentType:eTag:)]; [invocation setArgument:&self atIndex:2]; [invocation setArgument:&filename atIndex:3]; [invocation setArgument:&contentType atIndex:4]; [invocation setArgument:&eTag atIndex:5]; [invocation invoke]; } } [loadRequests removeObjectForKey:path]; } - (void)loadThumbnail:(NSString *)path ofSize:(NSString *)size intoPath:(NSString *)destinationPath { NSString* fullPath = [NSString stringWithFormat:@"/thumbnails/%@%@", root, path]; NSDictionary *params = nil; if(size) { params = [NSDictionary dictionaryWithObjectsAndKeys: size, @"size", nil]; } NSURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTP host:kDBDropboxAPIContentHost path:fullPath parameters:params]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadThumbnail:)] autorelease]; request.resultFilename = destinationPath; request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys: root, @"root", path, @"path", destinationPath, @"destinationPath", nil]; [requests addObject:request]; } - (void)requestDidLoadThumbnail:(DBRequest*)request { if (request.error) { [self checkForAuthenticationFailure:request]; if ([delegate respondsToSelector:@selector(restClient:loadThumbnailFailedWithError:)]) { [delegate restClient:self loadThumbnailFailedWithError:request.error]; } } else { if ([delegate respondsToSelector:@selector(restClient:loadedThumbnail:)]) { [delegate restClient:self loadedThumbnail:request.resultFilename]; } } [requests removeObject:request]; } NSString *createFakeSignature(DBSession *session, NSArray *params, NSString *filename, NSURL *baseUrl) { NSArray* extraParams = [MPURLRequestParameter parametersFromDictionary: [NSDictionary dictionaryWithObject:filename forKey:@"file"]]; NSMutableArray* paramList = [NSMutableArray arrayWithArray:params]; [paramList addObjectsFromArray:extraParams]; [paramList sortUsingSelector:@selector(compare:)]; NSString* paramString = [MPURLRequestParameter parameterStringForParameters:paramList]; MPOAuthURLRequest* oauthRequest = [[[MPOAuthURLRequest alloc] initWithURL:baseUrl andParameters:paramList] autorelease]; oauthRequest.HTTPMethod = @"POST"; MPOAuthSignatureParameter *signatureParameter = [[[MPOAuthSignatureParameter alloc] initWithText:paramString andSecret:session.credentialStore.signingKey forRequest:oauthRequest usingMethod:session.credentialStore.signatureMethod] autorelease]; return [signatureParameter URLEncodedParameterString]; } NSMutableURLRequest *createRealRequest(DBSession *session, NSArray *params, NSString *urlString, NSString *signatureText) { NSMutableArray *paramList = [NSMutableArray arrayWithArray:params]; // Then rebuild request using that signature [paramList sortUsingSelector:@selector(compare:)]; NSMutableString* realParamString = [[[NSMutableString alloc] initWithString: [MPURLRequestParameter parameterStringForParameters:paramList]] autorelease]; [realParamString appendFormat:@"&%@", signatureText]; NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", urlString, realParamString]]; NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url]; urlRequest.HTTPMethod = @"POST"; return urlRequest; } // Returns DBErrorNone if no errors were encountered DBErrorCode addFileUploadToRequest(NSMutableURLRequest *urlRequest, NSString *filename, NSString *sourcePath) { // Create input stream CFUUIDRef uuid = CFUUIDCreate(NULL); NSString* stringBoundary = [(NSString*)CFUUIDCreateString(NULL, uuid) autorelease]; CFRelease(uuid); NSString* contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",stringBoundary]; [urlRequest addValue:contentType forHTTPHeaderField: @"Content-Type"]; NSString* tempFilename = [NSString stringWithFormat: @"%.0f.txt", [NSDate timeIntervalSinceReferenceDate] * 1000.0]; NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:tempFilename]; //setting up the body NSMutableData* bodyData = [NSMutableData data]; [bodyData appendData: [[NSString stringWithFormat:@"--%@\r\n", stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; // Add data to upload [bodyData appendData: [[NSString stringWithFormat: @"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]]; [bodyData appendData: [[NSString stringWithString:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; if (![[NSFileManager defaultManager] createFileAtPath:tempFilePath contents:bodyData attributes:nil]) { NSLog(@"DBRestClient#uploadFileToRoot:path:filename:fromPath: failed to create file"); return DBErrorGenericError; } NSFileHandle* bodyFile = [NSFileHandle fileHandleForWritingAtPath:tempFilePath]; [bodyFile seekToEndOfFile]; if ([[NSFileManager defaultManager] fileExistsAtPath:sourcePath]) { NSFileHandle* readFile = [NSFileHandle fileHandleForReadingAtPath:sourcePath]; NSData* readData; while ((readData = [readFile readDataOfLength:1024 * 512]) != nil && [readData length] > 0) { @try { [bodyFile writeData:readData]; } @catch (NSException* e) { NSLog(@"DBRestClient#uploadFileToRoot:path:filename:fromPath: failed to write data"); [readFile closeFile]; [bodyFile closeFile]; NSFileManager *manager = [NSFileManager defaultManager]; if ([manager respondsToSelector:@selector(removeFileAtPath:handler:)]) [manager removeFileAtPath:tempFilePath handler:nil]; else [manager removeItemAtPath:tempFilePath error:nil]; return DBErrorInsufficientDiskSpace; } } [readFile closeFile]; } else { NSLog(@"DBRestClient#uploadFileToRoot:path:filename:fromPath: unable to open sourceFile"); } @try { [bodyFile writeData: [[NSString stringWithFormat:@"\r\n--%@--\r\n", stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; } @catch (NSException* e) { NSLog(@"DBRestClient#uploadFileToRoot:path:filename:fromPath: failed to write end of data"); [bodyFile closeFile]; NSFileManager *manager = [NSFileManager defaultManager]; if ([manager respondsToSelector:@selector(removeFileAtPath:handler:)]) [manager removeFileAtPath:tempFilePath handler:nil]; else [manager removeItemAtPath:tempFilePath error:nil]; return DBErrorInsufficientDiskSpace; } NSString* contentLength = [NSString stringWithFormat: @"%qu", [bodyFile offsetInFile]]; [urlRequest addValue:contentLength forHTTPHeaderField: @"Content-Length"]; [bodyFile closeFile]; urlRequest.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:tempFilePath]; return DBErrorNone; } - (void)uploadFile:(NSString*)filename toPath:(NSString*)path fromPath:(NSString *)sourcePath { if (![[NSFileManager defaultManager] fileExistsAtPath:sourcePath]) { NSDictionary* userInfo = [NSDictionary dictionaryWithObject:sourcePath forKey:@"sourcePath"]; NSError* error = [NSError errorWithDomain:DBErrorDomain code:DBErrorFileNotFound userInfo:userInfo]; if ([delegate respondsToSelector:@selector(restClient:uploadFileFailedWithError:)]) { [delegate restClient:self uploadFileFailedWithError:error]; } return; } // path is the directory the file will be uploaded to, make sure it doesn't have a trailing / // (unless it's the root dir) and is properly escaped NSString* trimmedPath; if ([path length] > 1 && [path characterAtIndex:[path length]-1] == '/') { trimmedPath = [path substringToIndex:[path length]-1]; } else { trimmedPath = path; } NSString* escapedPath = [DBRestClient escapePath:trimmedPath]; NSString* urlString = [NSString stringWithFormat:@"%@://%@/%@/files/%@%@", kDBProtocolHTTPS, kDBDropboxAPIContentHost, kDBDropboxAPIVersion, root, escapedPath]; NSURL* baseUrl = [NSURL URLWithString:urlString]; NSArray* params = [session.credentialStore oauthParameters]; NSString *escapedFilename = [filename replace:@";" with:@"-"]; NSString *signatureText = createFakeSignature(session, params, escapedFilename, baseUrl); NSMutableURLRequest *urlRequest = createRealRequest(session, params, urlString, signatureText); DBErrorCode errorCode = addFileUploadToRequest(urlRequest, escapedFilename, sourcePath); if(errorCode == DBErrorNone) { DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidUploadFile:)] autorelease]; request.uploadProgressSelector = @selector(requestUploadProgress:); NSString* dropboxPath = [path stringByAppendingPathComponent:filename]; request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys: root, @"root", path, @"path", dropboxPath, @"destinationPath", sourcePath, @"sourcePath", nil]; [requests addObject:request]; } else { NSDictionary* userInfo = [NSDictionary dictionaryWithObject:sourcePath forKey:@"sourcePath"]; NSError* error = [NSError errorWithDomain:DBErrorDomain code:errorCode userInfo:userInfo]; if ([delegate respondsToSelector:@selector(restClient:uploadFileFailedWithError:)]) { [delegate restClient:self uploadFileFailedWithError:error]; } } } - (void)requestUploadProgress:(DBRequest*)request { NSString* sourcePath = [(NSDictionary*)request.userInfo objectForKey:@"sourcePath"]; NSString* destPath = [request.userInfo objectForKey:@"destinationPath"]; if ([delegate respondsToSelector:@selector(restClient:uploadProgress:forFile:from:)]) { [delegate restClient:self uploadProgress:request.uploadProgress forFile:destPath from:sourcePath]; } else if ([delegate respondsToSelector:@selector(restClient:uploadProgress:forFile:)]) { [delegate restClient:self uploadProgress:request.uploadProgress forFile:sourcePath]; } } - (void)requestDidUploadFile:(DBRequest*)request { if (request.error) { [self checkForAuthenticationFailure:request]; if ([delegate respondsToSelector:@selector(restClient:uploadFileFailedWithError:)]) { [delegate restClient:self uploadFileFailedWithError:request.error]; } } else { NSString* sourcePath = [(NSDictionary*)request.userInfo objectForKey:@"sourcePath"]; NSString* destPath = [request.userInfo objectForKey:@"destinationPath"]; if ([delegate respondsToSelector:@selector(restClient:uploadedFile:from:)]) { [delegate restClient:self uploadedFile:destPath from:sourcePath]; } else if ([delegate respondsToSelector:@selector(restClient:uploadedFile:)]) { [delegate restClient:self uploadedFile:sourcePath]; } } [requests removeObject:request]; } - (void)moveFrom:(NSString*)from_path toPath:(NSString *)to_path { NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys: root, @"root", from_path, @"from_path", to_path, @"to_path", nil]; NSMutableURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTP host:kDBDropboxAPIHost path:@"/fileops/move" parameters:params method:@"POST"]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidMovePath:)] autorelease]; request.userInfo = params; [requests addObject:request]; } - (void)requestDidMovePath:(DBRequest*)request { if (request.error) { [self checkForAuthenticationFailure:request]; if ([delegate respondsToSelector:@selector(restClient:movePathFailedWithError:)]) { [delegate restClient:self movePathFailedWithError:request.error]; } } else { NSDictionary *params = (NSDictionary *)request.userInfo; if ([delegate respondsToSelector:@selector(restClient:movedPath:toPath:)]) { [delegate restClient:self movedPath:[params valueForKey:@"from_path"] toPath:[params valueForKey:@"to_path"]]; } } [requests removeObject:request]; } - (void)copyFrom:(NSString*)from_path toPath:(NSString *)to_path { NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys: root, @"root", from_path, @"from_path", to_path, @"to_path", nil]; NSMutableURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTP host:kDBDropboxAPIHost path:@"/fileops/copy" parameters:params method:@"POST"]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidCopyPath:)] autorelease]; request.userInfo = params; [requests addObject:request]; } - (void)requestDidCopyPath:(DBRequest*)request { if (request.error) { [self checkForAuthenticationFailure:request]; if ([delegate respondsToSelector:@selector(restClient:copyPathFailedWithError:)]) { [delegate restClient:self copyPathFailedWithError:request.error]; } } else { NSDictionary *params = (NSDictionary *)request.userInfo; if ([delegate respondsToSelector:@selector(restClient:copiedPath:toPath:)]) { [delegate restClient:self copiedPath:[params valueForKey:@"from_path"] toPath:[params valueForKey:@"to_path"]]; } } [requests removeObject:request]; } - (void)deletePath:(NSString*)path { NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys: root, @"root", path, @"path", nil]; NSMutableURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTP host:kDBDropboxAPIHost path:@"/fileops/delete" parameters:params method:@"POST"]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidDeletePath:)] autorelease]; request.userInfo = params; [requests addObject:request]; } - (void)requestDidDeletePath:(DBRequest*)request { if (request.error) { [self checkForAuthenticationFailure:request]; if ([delegate respondsToSelector:@selector(restClient:deletePathFailedWithError:)]) { [delegate restClient:self deletePathFailedWithError:request.error]; } } else { if ([delegate respondsToSelector:@selector(restClient:deletedPath:)]) { NSString* path = [request.userInfo objectForKey:@"path"]; [delegate restClient:self deletedPath:path]; } } [requests removeObject:request]; } - (void)createFolder:(NSString*)path { NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys: root, @"root", path, @"path", nil]; NSString* fullPath = @"/fileops/create_folder"; NSMutableURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTP host:kDBDropboxAPIHost path:fullPath parameters:params method:@"POST"]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidCreateDirectory:)] autorelease]; request.userInfo = params; [requests addObject:request]; } - (void)requestDidCreateDirectory:(DBRequest*)request { if (request.error) { [self checkForAuthenticationFailure:request]; if ([delegate respondsToSelector:@selector(restClient:createFolderFailedWithError:)]) { [delegate restClient:self createFolderFailedWithError:request.error]; } } else { NSDictionary* result = (NSDictionary*)[request resultJSON]; DBMetadata* metadata = [[[DBMetadata alloc] initWithDictionary:result] autorelease]; if ([delegate respondsToSelector:@selector(restClient:createdFolder:)]) { [delegate restClient:self createdFolder:metadata]; } } [requests removeObject:request]; } - (void)loadAccountInfo { NSURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTP host:kDBDropboxAPIHost path:@"/account/info" parameters:nil]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadAccountInfo:)] autorelease]; request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:root, @"root", nil]; [requests addObject:request]; } - (void)requestDidLoadAccountInfo:(DBRequest*)request { if (request.error) { [self checkForAuthenticationFailure:request]; if ([delegate respondsToSelector:@selector(restClient:loadAccountInfoFailedWithError:)]) { [delegate restClient:self loadAccountInfoFailedWithError:request.error]; } } else { NSDictionary* result = (NSDictionary*)[request resultJSON]; DBAccountInfo* accountInfo = [[[DBAccountInfo alloc] initWithDictionary:result] autorelease]; if ([delegate respondsToSelector:@selector(restClient:loadedAccountInfo:)]) { [delegate restClient:self loadedAccountInfo:accountInfo]; } } [requests removeObject:request]; } - (void)createAccount:(NSString *)email password:(NSString *)password firstName:(NSString *)firstName lastName:(NSString *)lastName { NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys: email, @"email", password, @"password", firstName, @"first_name", lastName, @"last_name", nil]; NSString* fullPath = @"/account"; NSMutableURLRequest* urlRequest = [self requestWithProtocol:kDBProtocolHTTPS host:kDBDropboxAPIHost path:fullPath parameters:params method:@"POST"]; DBRequest* request = [[[DBRequest alloc] initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidCreateAccount:)] autorelease]; request.userInfo = params; [requests addObject:request]; } - (void)requestDidCreateAccount:(DBRequest *)request { if(request.error) { if([delegate respondsToSelector:@selector(restClient:createAccountFailedWithError:)]) { [delegate restClient:self createAccountFailedWithError:request.error]; } } else { if ([delegate respondsToSelector:@selector(restClientCreatedAccount:)]) { [delegate restClientCreatedAccount:self]; } } [requests removeObject:request]; } #pragma mark private methods + (NSString*)escapePath:(NSString*)path { CFStringEncoding encoding = CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding); NSString *escapedPath = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)path, NULL, (CFStringRef)@":?=,!$&'()*+;[]@#~", encoding); return [escapedPath autorelease]; } - (NSMutableURLRequest*)requestWithProtocol:(NSString*)protocol host:(NSString*)host path:(NSString*)path parameters:(NSDictionary*)params { return [self requestWithProtocol:protocol host:host path:path parameters:params method:nil]; } - (NSMutableURLRequest*)requestWithProtocol:(NSString*)protocol host:(NSString*)host path:(NSString*)path parameters:(NSDictionary*)params method:(NSString*)method { NSString* escapedPath = [DBRestClient escapePath:path]; NSString* urlString = [NSString stringWithFormat:@"%@://%@/%@%@", protocol, host, kDBDropboxAPIVersion, escapedPath]; NSURL* url = [NSURL URLWithString:urlString]; NSArray* paramList = [session.credentialStore oauthParameters]; if ([params count] > 0) { NSArray* extraParams = [MPURLRequestParameter parametersFromDictionary:params]; paramList = [paramList arrayByAddingObjectsFromArray:extraParams]; } MPOAuthURLRequest* oauthRequest = [[[MPOAuthURLRequest alloc] initWithURL:url andParameters:paramList] autorelease]; if (method) { oauthRequest.HTTPMethod = method; } NSMutableURLRequest* urlRequest = [oauthRequest urlRequestSignedWithSecret:session.credentialStore.signingKey usingMethod:session.credentialStore.signatureMethod]; return urlRequest; } - (void)checkForAuthenticationFailure:(DBRequest*)request { if (request.error && request.error.code == 401 && [request.error.domain isEqual:@"dropbox.com"]) { [session.delegate sessionDidReceiveAuthorizationFailure:session]; } } @end