CocoaShare/Classes/Dropbox/DropboxSDK/DBRestClient.m
2011-02-01 10:19:26 -06:00

847 lines
31 KiB
Objective-C

#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<DBRestClientDelegate>)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