DBRestClient.m 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373
  1. //
  2. // DBRestClient.m
  3. // DropboxSDK
  4. //
  5. // Created by Brian Smith on 4/9/10.
  6. // Copyright 2010 Dropbox, Inc. All rights reserved.
  7. //
  8. #import "DBRestClient.h"
  9. #import "DBAccountInfo.h"
  10. #import "DBError.h"
  11. #import "DBDeltaEntry.h"
  12. #import "DBLog.h"
  13. #import "DBMetadata.h"
  14. #import "DBRequest.h"
  15. #import "MPOAuthURLRequest.h"
  16. #import "MPURLRequestParameter.h"
  17. #import "MPOAuthSignatureParameter.h"
  18. #import "NSString+URLEscapingAdditions.h"
  19. @interface DBRestClient ()
  20. // This method escapes all URI escape characters except /
  21. + (NSString*)escapePath:(NSString*)path;
  22. + (NSString *)bestLanguage;
  23. + (NSString *)userAgent;
  24. - (NSMutableURLRequest*)requestWithHost:(NSString*)host path:(NSString*)path
  25. parameters:(NSDictionary*)params;
  26. - (NSMutableURLRequest*)requestWithHost:(NSString*)host path:(NSString*)path
  27. parameters:(NSDictionary*)params method:(NSString*)method;
  28. - (void)checkForAuthenticationFailure:(DBRequest*)request;
  29. @property (nonatomic, readonly) MPOAuthCredentialConcreteStore *credentialStore;
  30. @end
  31. @interface DBMetadata ()
  32. + (NSDateFormatter *)dateFormatter;
  33. @end
  34. @implementation DBRestClient
  35. - (id)initWithSession:(DBSession*)aSession userId:(NSString *)theUserId {
  36. if (!aSession) {
  37. DBLogError(@"DropboxSDK: cannot initialize a DBRestClient with a nil session");
  38. return nil;
  39. }
  40. if ((self = [super init])) {
  41. session = [aSession retain];
  42. userId = [theUserId retain];
  43. root = [aSession.root retain];
  44. requests = [[NSMutableSet alloc] init];
  45. loadRequests = [[NSMutableDictionary alloc] init];
  46. imageLoadRequests = [[NSMutableDictionary alloc] init];
  47. uploadRequests = [[NSMutableDictionary alloc] init];
  48. }
  49. return self;
  50. }
  51. - (id)initWithSession:(DBSession *)aSession {
  52. NSString *uid = [aSession.userIds count] > 0 ? [aSession.userIds objectAtIndex:0] : nil;
  53. return [self initWithSession:aSession userId:uid];
  54. }
  55. - (void)cancelAllRequests {
  56. for (DBRequest* request in requests) {
  57. [request cancel];
  58. }
  59. [requests removeAllObjects];
  60. for (DBRequest* request in [loadRequests allValues]) {
  61. [request cancel];
  62. }
  63. [loadRequests removeAllObjects];
  64. for (DBRequest* request in [imageLoadRequests allValues]) {
  65. [request cancel];
  66. }
  67. [imageLoadRequests removeAllObjects];
  68. for (DBRequest* request in [uploadRequests allValues]) {
  69. [request cancel];
  70. }
  71. [uploadRequests removeAllObjects];
  72. }
  73. - (void)dealloc {
  74. [self cancelAllRequests];
  75. [requests release];
  76. [loadRequests release];
  77. [imageLoadRequests release];
  78. [uploadRequests release];
  79. [session release];
  80. [userId release];
  81. [root release];
  82. [super dealloc];
  83. }
  84. @synthesize delegate;
  85. - (void)loadMetadata:(NSString*)path withParams:(NSDictionary *)params
  86. {
  87. NSString* fullPath = [NSString stringWithFormat:@"/metadata/%@%@", root, path];
  88. NSURLRequest* urlRequest =
  89. [self requestWithHost:kDBDropboxAPIHost path:fullPath parameters:params];
  90. DBRequest* request =
  91. [[[DBRequest alloc]
  92. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadMetadata:)]
  93. autorelease];
  94. NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:path forKey:@"path"];
  95. if (params) {
  96. [userInfo addEntriesFromDictionary:params];
  97. }
  98. request.userInfo = userInfo;
  99. [requests addObject:request];
  100. }
  101. - (void)loadMetadata:(NSString*)path
  102. {
  103. [self loadMetadata:path withParams:nil];
  104. }
  105. - (void)loadMetadata:(NSString*)path withHash:(NSString*)hash {
  106. NSDictionary *params = nil;
  107. if (hash) {
  108. params = [NSDictionary dictionaryWithObject:hash forKey:@"hash"];
  109. }
  110. [self loadMetadata:path withParams:params];
  111. }
  112. - (void)loadMetadata:(NSString *)path atRev:(NSString *)rev {
  113. NSDictionary *params = nil;
  114. if (rev) {
  115. params = [NSDictionary dictionaryWithObject:rev forKey:@"rev"];
  116. }
  117. [self loadMetadata:path withParams:params];
  118. }
  119. - (void)requestDidLoadMetadata:(DBRequest*)request
  120. {
  121. [[self retain] autorelease];
  122. if (request.statusCode == 304) {
  123. if ([delegate respondsToSelector:@selector(restClient:metadataUnchangedAtPath:)]) {
  124. NSString* path = [request.userInfo objectForKey:@"path"];
  125. [delegate restClient:self metadataUnchangedAtPath:path];
  126. }
  127. } else if (request.error) {
  128. [self checkForAuthenticationFailure:request];
  129. if ([delegate respondsToSelector:@selector(restClient:loadMetadataFailedWithError:)]) {
  130. [delegate restClient:self loadMetadataFailedWithError:request.error];
  131. }
  132. } else {
  133. SEL sel = @selector(parseMetadataWithRequest:resultThread:);
  134. NSMethodSignature *sig = [self methodSignatureForSelector:sel];
  135. NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
  136. [inv setTarget:self];
  137. [inv setSelector:sel];
  138. [inv setArgument:&request atIndex:2];
  139. NSThread *currentThread = [NSThread currentThread];
  140. [inv setArgument:&currentThread atIndex:3];
  141. [inv retainArguments];
  142. [inv performSelectorInBackground:@selector(invoke) withObject:nil];
  143. }
  144. [requests removeObject:request];
  145. }
  146. - (void)parseMetadataWithRequest:(DBRequest*)request resultThread:(NSThread *)thread {
  147. NSAutoreleasePool* pool = [NSAutoreleasePool new];
  148. NSDictionary* result = (NSDictionary*)[request resultJSON];
  149. DBMetadata* metadata = [[[DBMetadata alloc] initWithDictionary:result] autorelease];
  150. if (metadata) {
  151. [self performSelector:@selector(didParseMetadata:) onThread:thread withObject:metadata waitUntilDone:NO];
  152. } else {
  153. [self performSelector:@selector(parseMetadataFailedForRequest:) onThread:thread
  154. withObject:request waitUntilDone:NO];
  155. }
  156. [pool drain];
  157. }
  158. - (void)didParseMetadata:(DBMetadata*)metadata {
  159. if ([delegate respondsToSelector:@selector(restClient:loadedMetadata:)]) {
  160. [delegate restClient:self loadedMetadata:metadata];
  161. }
  162. }
  163. - (void)parseMetadataFailedForRequest:(DBRequest *)request {
  164. NSError *error =
  165. [NSError errorWithDomain:DBErrorDomain code:DBErrorInvalidResponse userInfo:request.userInfo];
  166. DBLogWarning(@"DropboxSDK: error parsing metadata");
  167. if ([delegate respondsToSelector:@selector(restClient:loadMetadataFailedWithError:)]) {
  168. [delegate restClient:self loadMetadataFailedWithError:error];
  169. }
  170. }
  171. - (void)loadDelta:(NSString *)cursor {
  172. NSDictionary *params = nil;
  173. if (cursor) {
  174. params = [NSDictionary dictionaryWithObject:cursor forKey:@"cursor"];
  175. }
  176. NSString *fullPath = [NSString stringWithFormat:@"/delta"];
  177. NSMutableURLRequest* urlRequest =
  178. [self requestWithHost:kDBDropboxAPIHost path:fullPath parameters:params method:@"POST"];
  179. DBRequest* request =
  180. [[[DBRequest alloc]
  181. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadDelta:)]
  182. autorelease];
  183. request.userInfo = params;
  184. [requests addObject:request];
  185. }
  186. - (void)requestDidLoadDelta:(DBRequest *)request {
  187. [[self retain] autorelease];
  188. if (request.error) {
  189. [self checkForAuthenticationFailure:request];
  190. if ([delegate respondsToSelector:@selector(restClient:loadDeltaFailedWithError:)]) {
  191. [delegate restClient:self loadDeltaFailedWithError:request.error];
  192. }
  193. } else {
  194. SEL sel = @selector(parseDeltaWithRequest:resultThread:);
  195. NSMethodSignature *sig = [self methodSignatureForSelector:sel];
  196. NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
  197. [inv setTarget:self];
  198. [inv setSelector:sel];
  199. [inv setArgument:&request atIndex:2];
  200. NSThread *currentThread = [NSThread currentThread];
  201. [inv setArgument:&currentThread atIndex:3];
  202. [inv retainArguments];
  203. [inv performSelectorInBackground:@selector(invoke) withObject:nil];
  204. }
  205. [requests removeObject:request];
  206. }
  207. - (void)parseDeltaWithRequest:(DBRequest *)request resultThread:(NSThread *)thread {
  208. NSAutoreleasePool* pool = [NSAutoreleasePool new];
  209. NSDictionary* result = [request parseResponseAsType:[NSDictionary class]];
  210. if (result) {
  211. NSArray *entryArrays = [result objectForKey:@"entries"];
  212. NSMutableArray *entries = [NSMutableArray arrayWithCapacity:[entryArrays count]];
  213. for (NSArray *entryArray in entryArrays) {
  214. DBDeltaEntry *entry = [[DBDeltaEntry alloc] initWithArray:entryArray];
  215. [entries addObject:entry];
  216. [entry release];
  217. }
  218. BOOL reset = [[result objectForKey:@"reset"] boolValue];
  219. NSString *cursor = [result objectForKey:@"cursor"];
  220. BOOL hasMore = [[result objectForKey:@"has_more"] boolValue];
  221. SEL sel = @selector(restClient:loadedDeltaEntries:reset:cursor:hasMore:);
  222. if ([delegate respondsToSelector:sel]) {
  223. NSMethodSignature *sig = [(NSObject *)delegate methodSignatureForSelector:sel];
  224. NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
  225. [inv setTarget:delegate];
  226. [inv setSelector:sel];
  227. [inv setArgument:&self atIndex:2];
  228. [inv setArgument:&entries atIndex:3];
  229. [inv setArgument:&reset atIndex:4];
  230. [inv setArgument:&cursor atIndex:5];
  231. [inv setArgument:&hasMore atIndex:6];
  232. [inv retainArguments];
  233. [inv performSelector:@selector(invoke) onThread:thread withObject:nil waitUntilDone:NO];
  234. }
  235. } else {
  236. [self performSelector:@selector(parseDeltaFailedForRequest:) onThread:thread
  237. withObject:request waitUntilDone:NO];
  238. }
  239. [pool drain];
  240. }
  241. - (void)parseDeltaFailedForRequest:(DBRequest *)request {
  242. NSError *error =
  243. [NSError errorWithDomain:DBErrorDomain code:DBErrorInvalidResponse userInfo:request.userInfo];
  244. DBLogWarning(@"DropboxSDK: error parsing metadata");
  245. if ([delegate respondsToSelector:@selector(restClient:loadDeltaFailedWithError:)]) {
  246. [delegate restClient:self loadDeltaFailedWithError:error];
  247. }
  248. }
  249. - (void)loadFile:(NSString *)path atRev:(NSString *)rev intoPath:(NSString *)destPath
  250. {
  251. NSString* fullPath = [NSString stringWithFormat:@"/files/%@%@", root, path];
  252. NSDictionary *params = nil;
  253. if (rev) {
  254. params = [NSDictionary dictionaryWithObject:rev forKey:@"rev"];
  255. }
  256. NSURLRequest* urlRequest =
  257. [self requestWithHost:kDBDropboxAPIContentHost path:fullPath parameters:params];
  258. DBRequest* request =
  259. [[[DBRequest alloc]
  260. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadFile:)]
  261. autorelease];
  262. request.resultFilename = destPath;
  263. request.downloadProgressSelector = @selector(requestLoadProgress:);
  264. request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  265. path, @"path",
  266. destPath, @"destinationPath",
  267. rev, @"rev", nil];
  268. [loadRequests setObject:request forKey:path];
  269. }
  270. - (void)loadFile:(NSString *)path intoPath:(NSString *)destPath {
  271. [self loadFile:path atRev:nil intoPath:destPath];
  272. }
  273. - (void)cancelFileLoad:(NSString*)path {
  274. DBRequest* outstandingRequest = [loadRequests objectForKey:path];
  275. if (outstandingRequest) {
  276. [outstandingRequest cancel];
  277. [loadRequests removeObjectForKey:path];
  278. }
  279. }
  280. - (void)requestLoadProgress:(DBRequest*)request {
  281. if ([delegate respondsToSelector:@selector(restClient:loadProgress:forFile:)]) {
  282. [delegate restClient:self loadProgress:request.downloadProgress forFile:request.resultFilename];
  283. }
  284. }
  285. - (void)restClient:(DBRestClient*)restClient loadedFile:(NSString*)destPath
  286. contentType:(NSString*)contentType eTag:(NSString*)eTag {
  287. // Empty selector to get the signature from
  288. }
  289. - (void)requestDidLoadFile:(DBRequest*)request {
  290. [[self retain] autorelease];
  291. NSString* path = [request.userInfo objectForKey:@"path"];
  292. if (request.error) {
  293. [self checkForAuthenticationFailure:request];
  294. if ([delegate respondsToSelector:@selector(restClient:loadFileFailedWithError:)]) {
  295. [delegate restClient:self loadFileFailedWithError:request.error];
  296. }
  297. } else {
  298. NSString* filename = request.resultFilename;
  299. NSDictionary* headers = [request.response allHeaderFields];
  300. NSString* contentType = [headers objectForKey:@"Content-Type"];
  301. NSDictionary* metadataDict = [request xDropboxMetadataJSON];
  302. NSString* eTag = [headers objectForKey:@"Etag"];
  303. if ([delegate respondsToSelector:@selector(restClient:loadedFile:)]) {
  304. [delegate restClient:self loadedFile:filename];
  305. } else if ([delegate respondsToSelector:@selector(restClient:loadedFile:contentType:metadata:)]) {
  306. DBMetadata* metadata = [[[DBMetadata alloc] initWithDictionary:metadataDict] autorelease];
  307. [delegate restClient:self loadedFile:filename contentType:contentType metadata:metadata];
  308. } else if ([delegate respondsToSelector:@selector(restClient:loadedFile:contentType:)]) {
  309. // This callback is deprecated and this block exists only for backwards compatibility.
  310. [delegate restClient:self loadedFile:filename contentType:contentType];
  311. } else if ([delegate respondsToSelector:@selector(restClient:loadedFile:contentType:eTag:)]) {
  312. // This code is for the official Dropbox client to get eTag information from the server
  313. NSMethodSignature* signature =
  314. [self methodSignatureForSelector:@selector(restClient:loadedFile:contentType:eTag:)];
  315. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
  316. [invocation setTarget:delegate];
  317. [invocation setSelector:@selector(restClient:loadedFile:contentType:eTag:)];
  318. [invocation setArgument:&self atIndex:2];
  319. [invocation setArgument:&filename atIndex:3];
  320. [invocation setArgument:&contentType atIndex:4];
  321. [invocation setArgument:&eTag atIndex:5];
  322. [invocation invoke];
  323. }
  324. }
  325. [loadRequests removeObjectForKey:path];
  326. }
  327. - (NSString*)thumbnailKeyForPath:(NSString*)path size:(NSString*)size {
  328. return [NSString stringWithFormat:@"%@##%@", path, size];
  329. }
  330. - (void)loadThumbnail:(NSString *)path ofSize:(NSString *)size intoPath:(NSString *)destinationPath
  331. {
  332. NSString* fullPath = [NSString stringWithFormat:@"/thumbnails/%@%@", root, path];
  333. NSString* format = @"JPEG";
  334. if ([path length] > 4) {
  335. NSString* extension = [[path substringFromIndex:[path length] - 4] uppercaseString];
  336. if ([[NSSet setWithObjects:@".PNG", @".GIF", nil] containsObject:extension]) {
  337. format = @"PNG";
  338. }
  339. }
  340. NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObject:format forKey:@"format"];
  341. if(size) {
  342. [params setObject:size forKey:@"size"];
  343. }
  344. NSURLRequest* urlRequest =
  345. [self requestWithHost:kDBDropboxAPIContentHost path:fullPath parameters:params];
  346. DBRequest* request =
  347. [[[DBRequest alloc]
  348. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadThumbnail:)]
  349. autorelease];
  350. request.resultFilename = destinationPath;
  351. request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  352. root, @"root",
  353. path, @"path",
  354. destinationPath, @"destinationPath",
  355. size, @"size", nil];
  356. [imageLoadRequests setObject:request forKey:[self thumbnailKeyForPath:path size:size]];
  357. }
  358. - (void)requestDidLoadThumbnail:(DBRequest*)request
  359. {
  360. [[self retain] autorelease];
  361. if (request.error) {
  362. [self checkForAuthenticationFailure:request];
  363. if ([delegate respondsToSelector:@selector(restClient:loadThumbnailFailedWithError:)]) {
  364. [delegate restClient:self loadThumbnailFailedWithError:request.error];
  365. }
  366. } else {
  367. NSString* filename = request.resultFilename;
  368. NSDictionary* metadataDict = [request xDropboxMetadataJSON];
  369. if ([delegate respondsToSelector:@selector(restClient:loadedThumbnail:metadata:)]) {
  370. DBMetadata* metadata = [[[DBMetadata alloc] initWithDictionary:metadataDict] autorelease];
  371. [delegate restClient:self loadedThumbnail:filename metadata:metadata];
  372. } else if ([delegate respondsToSelector:@selector(restClient:loadedThumbnail:)]) {
  373. // This callback is deprecated and this block exists only for backwards compatibility.
  374. [delegate restClient:self loadedThumbnail:filename];
  375. }
  376. }
  377. NSString* path = [request.userInfo objectForKey:@"path"];
  378. NSString* size = [request.userInfo objectForKey:@"size"];
  379. [imageLoadRequests removeObjectForKey:[self thumbnailKeyForPath:path size:size]];
  380. }
  381. - (void)cancelThumbnailLoad:(NSString*)path size:(NSString*)size {
  382. NSString* key = [self thumbnailKeyForPath:path size:size];
  383. DBRequest* request = [imageLoadRequests objectForKey:key];
  384. if (request) {
  385. [request cancel];
  386. [imageLoadRequests removeObjectForKey:key];
  387. }
  388. }
  389. - (NSString *)signatureForParams:(NSArray *)params url:(NSURL *)baseUrl {
  390. NSMutableArray* paramList = [NSMutableArray arrayWithArray:params];
  391. [paramList sortUsingSelector:@selector(compare:)];
  392. NSString* paramString = [MPURLRequestParameter parameterStringForParameters:paramList];
  393. MPOAuthURLRequest* oauthRequest =
  394. [[[MPOAuthURLRequest alloc] initWithURL:baseUrl andParameters:paramList] autorelease];
  395. oauthRequest.HTTPMethod = @"POST";
  396. MPOAuthSignatureParameter *signatureParameter =
  397. [[[MPOAuthSignatureParameter alloc]
  398. initWithText:paramString andSecret:self.credentialStore.signingKey
  399. forRequest:oauthRequest usingMethod:self.credentialStore.signatureMethod]
  400. autorelease];
  401. return [signatureParameter URLEncodedParameterString];
  402. }
  403. - (NSMutableURLRequest *)requestForParams:(NSArray *)params urlString:(NSString *)urlString
  404. signature:(NSString *)sig {
  405. NSMutableArray *paramList = [NSMutableArray arrayWithArray:params];
  406. // Then rebuild request using that signature
  407. [paramList sortUsingSelector:@selector(compare:)];
  408. NSMutableString* realParamString = [[[NSMutableString alloc] initWithString:
  409. [MPURLRequestParameter parameterStringForParameters:paramList]]
  410. autorelease];
  411. [realParamString appendFormat:@"&%@", sig];
  412. NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", urlString, realParamString]];
  413. NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url];
  414. urlRequest.HTTPMethod = @"POST";
  415. return urlRequest;
  416. }
  417. - (void)uploadFile:(NSString*)filename toPath:(NSString*)path fromPath:(NSString *)sourcePath
  418. params:(NSDictionary *)params
  419. {
  420. BOOL isDir = NO;
  421. BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:sourcePath isDirectory:&isDir];
  422. NSDictionary *fileAttrs =
  423. [[NSFileManager defaultManager] attributesOfItemAtPath:sourcePath error:nil];
  424. if (!fileExists || isDir || !fileAttrs) {
  425. NSString* destPath = [path stringByAppendingPathComponent:filename];
  426. NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  427. sourcePath, @"sourcePath",
  428. destPath, @"destinationPath", nil];
  429. NSInteger errorCode = isDir ? DBErrorIllegalFileType : DBErrorFileNotFound;
  430. NSError* error =
  431. [NSError errorWithDomain:DBErrorDomain code:errorCode userInfo:userInfo];
  432. NSString *errorMsg = isDir ? @"Unable to upload folders" : @"File does not exist";
  433. DBLogWarning(@"DropboxSDK: %@ (%@)", errorMsg, sourcePath);
  434. if ([delegate respondsToSelector:@selector(restClient:uploadFileFailedWithError:)]) {
  435. [delegate restClient:self uploadFileFailedWithError:error];
  436. }
  437. return;
  438. }
  439. NSString *destPath = [path stringByAppendingPathComponent:filename];
  440. NSString *urlString =
  441. [NSString stringWithFormat:@"%@://%@/%@/files_put/%@%@",
  442. kDBProtocolHTTPS, kDBDropboxAPIContentHost, kDBDropboxAPIVersion, root,
  443. [DBRestClient escapePath:destPath]];
  444. #if !TARGET_OS_IPHONE
  445. // Set appropriate parameters to disable notification of updates.
  446. NSMutableDictionary * mutableParams = [[params mutableCopy] autorelease];
  447. [mutableParams setObject:@"1" forKey:@"mute"];
  448. params = mutableParams;
  449. #endif
  450. NSArray *paramList =
  451. [[self.credentialStore oauthParameters]
  452. arrayByAddingObjectsFromArray:[MPURLRequestParameter parametersFromDictionary:params]];
  453. NSString *sig = [self signatureForParams:paramList url:[NSURL URLWithString:urlString]];
  454. NSMutableURLRequest *urlRequest = [self requestForParams:paramList urlString:urlString signature:sig];
  455. NSString* contentLength = [NSString stringWithFormat: @"%qu", [fileAttrs fileSize]];
  456. [urlRequest addValue:contentLength forHTTPHeaderField: @"Content-Length"];
  457. [urlRequest addValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
  458. [urlRequest setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:sourcePath]];
  459. DBRequest *request =
  460. [[[DBRequest alloc]
  461. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidUploadFile:)]
  462. autorelease];
  463. request.uploadProgressSelector = @selector(requestUploadProgress:);
  464. request.userInfo =
  465. [NSDictionary dictionaryWithObjectsAndKeys:sourcePath, @"sourcePath", destPath, @"destinationPath", nil];
  466. request.sourcePath = sourcePath;
  467. [uploadRequests setObject:request forKey:destPath];
  468. }
  469. - (void)uploadFile:(NSString*)filename toPath:(NSString*)path fromPath:(NSString *)sourcePath
  470. {
  471. [self uploadFile:filename toPath:path fromPath:sourcePath params:nil];
  472. }
  473. - (void)uploadFile:(NSString *)filename toPath:(NSString *)path withParentRev:(NSString *)parentRev
  474. fromPath:(NSString *)sourcePath {
  475. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObject:@"false" forKey:@"overwrite"];
  476. if (parentRev) {
  477. [params setObject:parentRev forKey:@"parent_rev"];
  478. }
  479. [self uploadFile:filename toPath:path fromPath:sourcePath params:params];
  480. }
  481. - (void)requestUploadProgress:(DBRequest*)request {
  482. NSString* sourcePath = [(NSDictionary*)request.userInfo objectForKey:@"sourcePath"];
  483. NSString* destPath = [request.userInfo objectForKey:@"destinationPath"];
  484. if ([delegate respondsToSelector:@selector(restClient:uploadProgress:forFile:from:)]) {
  485. [delegate restClient:self uploadProgress:request.uploadProgress
  486. forFile:destPath from:sourcePath];
  487. }
  488. }
  489. - (void)requestDidUploadFile:(DBRequest*)request {
  490. [[self retain] autorelease];
  491. NSDictionary *result = [request parseResponseAsType:[NSDictionary class]];
  492. if (!result) {
  493. [self checkForAuthenticationFailure:request];
  494. if ([delegate respondsToSelector:@selector(restClient:uploadFileFailedWithError:)]) {
  495. [delegate restClient:self uploadFileFailedWithError:request.error];
  496. }
  497. } else {
  498. DBMetadata *metadata = [[[DBMetadata alloc] initWithDictionary:result] autorelease];
  499. NSString* sourcePath = [request.userInfo objectForKey:@"sourcePath"];
  500. NSString* destPath = [request.userInfo objectForKey:@"destinationPath"];
  501. if ([delegate respondsToSelector:@selector(restClient:uploadedFile:from:metadata:)]) {
  502. [delegate restClient:self uploadedFile:destPath from:sourcePath metadata:metadata];
  503. } else if ([delegate respondsToSelector:@selector(restClient:uploadedFile:from:)]) {
  504. [delegate restClient:self uploadedFile:destPath from:sourcePath];
  505. }
  506. }
  507. [uploadRequests removeObjectForKey:[request.userInfo objectForKey:@"destinationPath"]];
  508. }
  509. - (void)cancelFileUpload:(NSString *)path {
  510. DBRequest *request = [uploadRequests objectForKey:path];
  511. if (request) {
  512. [request cancel];
  513. [uploadRequests removeObjectForKey:path];
  514. }
  515. }
  516. - (void)uploadFileChunk:(NSString *)uploadId offset:(unsigned long long)offset fromPath:(NSString *)localPath {
  517. NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:localPath];
  518. if (!file) {
  519. if ([delegate respondsToSelector:@selector(restClient:uploadFileChunkFailedWithError:)]) {
  520. NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  521. localPath, @"fromPath",
  522. [NSNumber numberWithLongLong:offset], @"offset",
  523. uploadId, @"upload_id", nil];
  524. NSError *error = [NSError errorWithDomain:DBErrorDomain code:DBErrorFileNotFound userInfo:userInfo];
  525. [delegate restClient:self uploadFileChunkFailedWithError:error];
  526. } else {
  527. DBLogWarning(@"DropboxSDK: unable to read file in -[DBRestClient uploadFileChunk:offset:fromPath:] (fromPath=%@)", localPath);
  528. }
  529. return;
  530. }
  531. [file seekToFileOffset:offset];
  532. NSData *data = [file readDataOfLength:2*1024*1024];
  533. if (![data length]) {
  534. DBLogWarning(@"DropboxSDK: did not read any data from file (fromPath=%@)", localPath);
  535. }
  536. NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
  537. [NSString stringWithFormat:@"%qu", offset], @"offset",
  538. uploadId, @"upload_id",
  539. nil];
  540. NSString *urlStr = [NSString stringWithFormat:@"%@://%@/%@/chunked_upload",
  541. kDBProtocolHTTPS, kDBDropboxAPIContentHost, kDBDropboxAPIVersion];
  542. NSArray *paramList = [[self.credentialStore oauthParameters]
  543. arrayByAddingObjectsFromArray:[MPURLRequestParameter parametersFromDictionary:params]];
  544. NSString *sig = [self signatureForParams:paramList url:[NSURL URLWithString:urlStr]];
  545. NSMutableURLRequest *urlRequest = [self requestForParams:paramList urlString:urlStr signature:sig];
  546. NSString *contentLength = [NSString stringWithFormat:@"%lu", (unsigned long)[data length]];
  547. [urlRequest addValue:contentLength forHTTPHeaderField:@"Content-Length"];
  548. [urlRequest addValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
  549. [urlRequest setHTTPBody:data];
  550. DBRequest *request =
  551. [[[DBRequest alloc]
  552. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidUploadChunk:)]
  553. autorelease];
  554. request.uploadProgressSelector = @selector(requestChunkedUploadProgress:);
  555. NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  556. [NSNumber numberWithLongLong:offset], @"offset",
  557. localPath, @"fromPath",
  558. uploadId, @"upload_id", nil];
  559. request.userInfo = userInfo;
  560. request.sourcePath = localPath;
  561. [requests addObject:request];
  562. }
  563. - (void)requestChunkedUploadProgress:(DBRequest*)request {
  564. NSString *uploadId = [request.userInfo objectForKey:@"upload_id"];
  565. unsigned long long offset = [[request.userInfo objectForKey:@"offset"] longLongValue];
  566. NSString *fromPath = [request.userInfo objectForKey:@"fromPath"];
  567. if ([delegate respondsToSelector:@selector(restClient:uploadFileChunkProgress:forFile:offset:fromPath:)]) {
  568. [delegate restClient:self uploadFileChunkProgress:request.uploadProgress
  569. forFile:uploadId offset:offset fromPath:fromPath];
  570. }
  571. }
  572. - (void)requestDidUploadChunk:(DBRequest *)request {
  573. [[self retain] autorelease];
  574. NSDictionary *resp = [request parseResponseAsType:[NSDictionary class]];
  575. if (!resp) {
  576. if ([delegate respondsToSelector:@selector(restClient:uploadFileChunkFailedWithError:)]) {
  577. [delegate restClient:self uploadFileChunkFailedWithError:request.error];
  578. }
  579. } else {
  580. NSString *uploadId = [resp objectForKey:@"upload_id"];
  581. unsigned long long newOffset = [[resp objectForKey:@"offset"] longLongValue];
  582. NSString *localPath = [request.userInfo objectForKey:@"fromPath"];
  583. NSDateFormatter *dateFormatter = [DBMetadata dateFormatter];
  584. NSDate *expires = [dateFormatter dateFromString:[resp objectForKey:@"expires"]];
  585. if ([delegate respondsToSelector:@selector(restClient:uploadedFileChunk:newOffset:fromFile:expires:)]) {
  586. [delegate restClient:self uploadedFileChunk:uploadId newOffset:newOffset fromFile:localPath expires:expires];
  587. }
  588. }
  589. [requests removeObject:request];
  590. }
  591. - (void)uploadFile:(NSString *)filename toPath:(NSString *)parentFolder withParentRev:(NSString *)parentRev
  592. fromUploadId:(NSString *)uploadId {
  593. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  594. uploadId, @"upload_id",
  595. @"false", @"overwrite", nil];
  596. if (parentRev) {
  597. [params setObject:parentRev forKey:@"parent_rev"];
  598. }
  599. #if !TARGET_OS_IPHONE
  600. [params setObject:@"1" forKey:@"mute"];
  601. #endif
  602. if (![parentFolder hasSuffix:@"/"]) {
  603. parentFolder = [NSString stringWithFormat:@"%@/", parentFolder];
  604. }
  605. NSString *destPath = [NSString stringWithFormat:@"%@%@", parentFolder, filename];
  606. NSString *urlPath = [NSString stringWithFormat:@"/commit_chunked_upload/%@%@", root, destPath];
  607. NSURLRequest *urlRequest = [self requestWithHost:kDBDropboxAPIContentHost path:urlPath parameters:params method:@"POST"];
  608. DBRequest *request = [[[DBRequest alloc]
  609. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidUploadFromUploadId:)]
  610. autorelease];
  611. request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  612. uploadId, @"uploadId",
  613. destPath, @"destPath",
  614. parentRev, @"parentRev",
  615. nil];
  616. [requests addObject:request];
  617. }
  618. - (void)requestDidUploadFromUploadId:(DBRequest *)request {
  619. [[self retain] autorelease];
  620. NSDictionary *resp = [request parseResponseAsType:[NSDictionary class]];
  621. if (!resp) {
  622. if ([delegate respondsToSelector:@selector(restClient:uploadFromUploadIdFailedWithError:)]) {
  623. [delegate restClient:self uploadFromUploadIdFailedWithError:request.error];
  624. }
  625. } else {
  626. NSString *destPath = [request.userInfo objectForKey:@"destPath"];
  627. NSString *uploadId = [request.userInfo objectForKey:@"uploadId"];
  628. DBMetadata *metadata = [[[DBMetadata alloc] initWithDictionary:resp] autorelease];
  629. if ([delegate respondsToSelector:@selector(restClient:uploadedFile:fromUploadId:metadata:)]) {
  630. [delegate restClient:self uploadedFile:destPath fromUploadId:uploadId metadata:metadata];
  631. }
  632. }
  633. [requests removeObject:request];
  634. }
  635. - (void)loadRevisionsForFile:(NSString *)path {
  636. [self loadRevisionsForFile:path limit:10];
  637. }
  638. - (void)loadRevisionsForFile:(NSString *)path limit:(NSInteger)limit {
  639. NSString *fullPath = [NSString stringWithFormat:@"/revisions/%@%@", root, path];
  640. NSString *limitStr = [NSString stringWithFormat:@"%ld", (long)limit];
  641. NSDictionary *params = [NSDictionary dictionaryWithObject:limitStr forKey:@"rev_limit"];
  642. NSURLRequest* urlRequest =
  643. [self requestWithHost:kDBDropboxAPIHost path:fullPath parameters:params];
  644. DBRequest* request =
  645. [[[DBRequest alloc]
  646. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadRevisions:)]
  647. autorelease];
  648. request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  649. path, @"path",
  650. [NSNumber numberWithInteger:limit], @"limit", nil];
  651. [requests addObject:request];
  652. }
  653. - (void)requestDidLoadRevisions:(DBRequest *)request {
  654. [[self retain] autorelease];
  655. NSArray *resp = [request parseResponseAsType:[NSArray class]];
  656. if (!resp) {
  657. if ([delegate respondsToSelector:@selector(restClient:loadRevisionsFailedWithError:)]) {
  658. [delegate restClient:self loadRevisionsFailedWithError:request.error];
  659. }
  660. } else {
  661. NSMutableArray *revisions = [NSMutableArray arrayWithCapacity:[resp count]];
  662. for (NSDictionary *dict in resp) {
  663. DBMetadata *metadata = [[DBMetadata alloc] initWithDictionary:dict];
  664. [revisions addObject:metadata];
  665. [metadata release];
  666. }
  667. NSString *path = [request.userInfo objectForKey:@"path"];
  668. if ([delegate respondsToSelector:@selector(restClient:loadedRevisions:forFile:)]) {
  669. [delegate restClient:self loadedRevisions:revisions forFile:path];
  670. }
  671. }
  672. [requests removeObject:request];
  673. }
  674. - (void)restoreFile:(NSString *)path toRev:(NSString *)rev {
  675. NSString *fullPath = [NSString stringWithFormat:@"/restore/%@%@", root, path];
  676. NSDictionary *params = [NSDictionary dictionaryWithObject:rev forKey:@"rev"];
  677. NSURLRequest* urlRequest =
  678. [self requestWithHost:kDBDropboxAPIHost path:fullPath parameters:params];
  679. DBRequest* request =
  680. [[[DBRequest alloc]
  681. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidRestoreFile:)]
  682. autorelease];
  683. request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  684. path, @"path",
  685. rev, @"rev", nil];
  686. [requests addObject:request];
  687. }
  688. - (void)requestDidRestoreFile:(DBRequest *)request {
  689. [[self retain] autorelease];
  690. NSDictionary *dict = [request parseResponseAsType:[NSDictionary class]];
  691. if (!dict) {
  692. if ([delegate respondsToSelector:@selector(restClient:restoreFileFailedWithError:)]) {
  693. [delegate restClient:self restoreFileFailedWithError:request.error];
  694. }
  695. } else {
  696. DBMetadata *metadata = [[[DBMetadata alloc] initWithDictionary:dict] autorelease];
  697. if ([delegate respondsToSelector:@selector(restClient:restoredFile:)]) {
  698. [delegate restClient:self restoredFile:metadata];
  699. }
  700. }
  701. [requests removeObject:request];
  702. }
  703. - (void)moveFrom:(NSString*)from_path toPath:(NSString *)to_path
  704. {
  705. NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:
  706. root, @"root",
  707. from_path, @"from_path",
  708. to_path, @"to_path", nil];
  709. NSMutableURLRequest* urlRequest =
  710. [self requestWithHost:kDBDropboxAPIHost path:@"/fileops/move"
  711. parameters:params method:@"POST"];
  712. DBRequest* request =
  713. [[[DBRequest alloc]
  714. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidMovePath:)]
  715. autorelease];
  716. request.userInfo = params;
  717. [requests addObject:request];
  718. }
  719. - (void)requestDidMovePath:(DBRequest*)request {
  720. [[self retain] autorelease];
  721. if (request.error) {
  722. [self checkForAuthenticationFailure:request];
  723. if ([delegate respondsToSelector:@selector(restClient:movePathFailedWithError:)]) {
  724. [delegate restClient:self movePathFailedWithError:request.error];
  725. }
  726. } else {
  727. NSDictionary *params = (NSDictionary *)request.userInfo;
  728. NSDictionary *result = [request parseResponseAsType:[NSDictionary class]];
  729. DBMetadata *metadata = [[[DBMetadata alloc] initWithDictionary:result] autorelease];
  730. if ([delegate respondsToSelector:@selector(restClient:movedPath:to:)]) {
  731. [delegate restClient:self movedPath:[params valueForKey:@"from_path"] to:metadata];
  732. }
  733. }
  734. [requests removeObject:request];
  735. }
  736. - (void)copyFrom:(NSString*)from_path toPath:(NSString *)to_path
  737. {
  738. NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:
  739. root, @"root",
  740. from_path, @"from_path",
  741. to_path, @"to_path", nil];
  742. NSMutableURLRequest* urlRequest =
  743. [self requestWithHost:kDBDropboxAPIHost path:@"/fileops/copy"
  744. parameters:params method:@"POST"];
  745. DBRequest* request =
  746. [[[DBRequest alloc]
  747. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidCopyPath:)]
  748. autorelease];
  749. request.userInfo = params;
  750. [requests addObject:request];
  751. }
  752. - (void)requestDidCopyPath:(DBRequest*)request {
  753. [[self retain] autorelease];
  754. if (request.error) {
  755. [self checkForAuthenticationFailure:request];
  756. if ([delegate respondsToSelector:@selector(restClient:copyPathFailedWithError:)]) {
  757. [delegate restClient:self copyPathFailedWithError:request.error];
  758. }
  759. } else {
  760. NSDictionary *params = (NSDictionary *)request.userInfo;
  761. NSDictionary *result = [request parseResponseAsType:[NSDictionary class]];
  762. DBMetadata *metadata = [[[DBMetadata alloc] initWithDictionary:result] autorelease];
  763. if ([delegate respondsToSelector:@selector(restClient:copiedPath:to:)]) {
  764. [delegate restClient:self copiedPath:[params valueForKey:@"from_path"] to:metadata];
  765. }
  766. }
  767. [requests removeObject:request];
  768. }
  769. - (void)createCopyRef:(NSString *)path {
  770. NSDictionary* userInfo = [NSDictionary dictionaryWithObject:path forKey:@"path"];
  771. NSString *fullPath = [NSString stringWithFormat:@"/copy_ref/%@%@", root, path];
  772. NSMutableURLRequest* urlRequest =
  773. [self requestWithHost:kDBDropboxAPIHost path:fullPath parameters:nil method:@"POST"];
  774. DBRequest* request =
  775. [[[DBRequest alloc]
  776. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidCreateCopyRef:)]
  777. autorelease];
  778. request.userInfo = userInfo;
  779. [requests addObject:request];
  780. }
  781. - (void)requestDidCreateCopyRef:(DBRequest *)request {
  782. [[self retain] autorelease];
  783. NSDictionary *result = [request parseResponseAsType:[NSDictionary class]];
  784. if (!result) {
  785. [self checkForAuthenticationFailure:request];
  786. if ([delegate respondsToSelector:@selector(restClient:createCopyRefFailedWithError:)]) {
  787. [delegate restClient:self createCopyRefFailedWithError:request.error];
  788. }
  789. } else {
  790. NSString *copyRef = [result objectForKey:@"copy_ref"];
  791. NSString *path = [request.userInfo objectForKey:@"path"];
  792. if ([delegate respondsToSelector:@selector(restClient:createdCopyRef:forPath:)]) {
  793. [delegate restClient:self createdCopyRef:copyRef forPath:path];
  794. } else if ([delegate respondsToSelector:@selector(restClient:createdCopyRef:)]) {
  795. [delegate restClient:self createdCopyRef:copyRef];
  796. }
  797. }
  798. [requests removeObject:request];
  799. }
  800. - (void)copyFromRef:(NSString*)copyRef toPath:(NSString *)toPath {
  801. NSDictionary *params =
  802. [NSDictionary dictionaryWithObjectsAndKeys:
  803. copyRef, @"from_copy_ref",
  804. root, @"root",
  805. toPath, @"to_path", nil];
  806. NSString *fullPath = [NSString stringWithFormat:@"/fileops/copy/"];
  807. NSMutableURLRequest* urlRequest =
  808. [self requestWithHost:kDBDropboxAPIHost path:fullPath parameters:params method:@"POST"];
  809. DBRequest* request =
  810. [[[DBRequest alloc]
  811. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidCopyFromRef:)]
  812. autorelease];
  813. request.userInfo = params;
  814. [requests addObject:request];
  815. }
  816. - (void)requestDidCopyFromRef:(DBRequest *)request {
  817. [[self retain] autorelease];
  818. NSDictionary *result = [request parseResponseAsType:[NSDictionary class]];
  819. if (!result) {
  820. [self checkForAuthenticationFailure:request];
  821. if ([delegate respondsToSelector:@selector(restClient:copyFromRefFailedWithError:)]) {
  822. [delegate restClient:self copyFromRefFailedWithError:request.error];
  823. }
  824. } else {
  825. NSString *copyRef = [request.userInfo objectForKey:@"from_copy_ref"];
  826. DBMetadata *metadata = [[[DBMetadata alloc] initWithDictionary:result] autorelease];
  827. if ([delegate respondsToSelector:@selector(restClient:copiedRef:to:)]) {
  828. [delegate restClient:self copiedRef:copyRef to:metadata];
  829. }
  830. }
  831. [requests removeObject:request];
  832. }
  833. - (void)deletePath:(NSString*)path {
  834. NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:
  835. root, @"root",
  836. path, @"path", nil];
  837. NSMutableURLRequest* urlRequest =
  838. [self requestWithHost:kDBDropboxAPIHost path:@"/fileops/delete"
  839. parameters:params method:@"POST"];
  840. DBRequest* request =
  841. [[[DBRequest alloc]
  842. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidDeletePath:)]
  843. autorelease];
  844. request.userInfo = params;
  845. [requests addObject:request];
  846. }
  847. - (void)requestDidDeletePath:(DBRequest*)request {
  848. [[self retain] autorelease];
  849. if (request.error) {
  850. [self checkForAuthenticationFailure:request];
  851. if ([delegate respondsToSelector:@selector(restClient:deletePathFailedWithError:)]) {
  852. [delegate restClient:self deletePathFailedWithError:request.error];
  853. }
  854. } else {
  855. if ([delegate respondsToSelector:@selector(restClient:deletedPath:)]) {
  856. NSString* path = [request.userInfo objectForKey:@"path"];
  857. [delegate restClient:self deletedPath:path];
  858. }
  859. }
  860. [requests removeObject:request];
  861. }
  862. - (void)createFolder:(NSString*)path
  863. {
  864. NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:
  865. root, @"root",
  866. path, @"path", nil];
  867. NSString* fullPath = @"/fileops/create_folder";
  868. NSMutableURLRequest* urlRequest =
  869. [self requestWithHost:kDBDropboxAPIHost path:fullPath
  870. parameters:params method:@"POST"];
  871. DBRequest* request =
  872. [[[DBRequest alloc]
  873. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidCreateDirectory:)]
  874. autorelease];
  875. request.userInfo = params;
  876. [requests addObject:request];
  877. }
  878. - (void)requestDidCreateDirectory:(DBRequest*)request {
  879. [[self retain] autorelease];
  880. if (request.error) {
  881. [self checkForAuthenticationFailure:request];
  882. if ([delegate respondsToSelector:@selector(restClient:createFolderFailedWithError:)]) {
  883. [delegate restClient:self createFolderFailedWithError:request.error];
  884. }
  885. } else {
  886. NSDictionary* result = (NSDictionary*)[request resultJSON];
  887. DBMetadata* metadata = [[[DBMetadata alloc] initWithDictionary:result] autorelease];
  888. if ([delegate respondsToSelector:@selector(restClient:createdFolder:)]) {
  889. [delegate restClient:self createdFolder:metadata];
  890. }
  891. }
  892. [requests removeObject:request];
  893. }
  894. - (void)loadAccountInfo
  895. {
  896. NSURLRequest* urlRequest =
  897. [self requestWithHost:kDBDropboxAPIHost path:@"/account/info" parameters:nil];
  898. DBRequest* request =
  899. [[[DBRequest alloc]
  900. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadAccountInfo:)]
  901. autorelease];
  902. request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:root, @"root", nil];
  903. [requests addObject:request];
  904. }
  905. - (void)requestDidLoadAccountInfo:(DBRequest*)request
  906. {
  907. [[self retain] autorelease];
  908. if (request.error) {
  909. [self checkForAuthenticationFailure:request];
  910. if ([delegate respondsToSelector:@selector(restClient:loadAccountInfoFailedWithError:)]) {
  911. [delegate restClient:self loadAccountInfoFailedWithError:request.error];
  912. }
  913. } else {
  914. NSDictionary* result = (NSDictionary*)[request resultJSON];
  915. DBAccountInfo* accountInfo = [[[DBAccountInfo alloc] initWithDictionary:result] autorelease];
  916. if ([delegate respondsToSelector:@selector(restClient:loadedAccountInfo:)]) {
  917. [delegate restClient:self loadedAccountInfo:accountInfo];
  918. }
  919. }
  920. [requests removeObject:request];
  921. }
  922. - (void)searchPath:(NSString*)path forKeyword:(NSString*)keyword {
  923. NSDictionary* params = [NSDictionary dictionaryWithObject:keyword forKey:@"query"];
  924. NSString* fullPath = [NSString stringWithFormat:@"/search/%@%@", root, path];
  925. NSURLRequest* urlRequest =
  926. [self requestWithHost:kDBDropboxAPIHost path:fullPath parameters:params];
  927. DBRequest* request =
  928. [[[DBRequest alloc]
  929. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidSearchPath:)]
  930. autorelease];
  931. request.userInfo =
  932. [NSDictionary dictionaryWithObjectsAndKeys:path, @"path", keyword, @"keyword", nil];
  933. [requests addObject:request];
  934. }
  935. - (void)requestDidSearchPath:(DBRequest*)request {
  936. [[self retain] autorelease];
  937. if (request.error) {
  938. [self checkForAuthenticationFailure:request];
  939. if ([delegate respondsToSelector:@selector(restClient:searchFailedWithError:)]) {
  940. [delegate restClient:self searchFailedWithError:request.error];
  941. }
  942. } else {
  943. NSMutableArray* results = nil;
  944. if ([[request resultJSON] isKindOfClass:[NSArray class]]) {
  945. NSArray* response = (NSArray*)[request resultJSON];
  946. results = [NSMutableArray arrayWithCapacity:[response count]];
  947. for (NSDictionary* dict in response) {
  948. DBMetadata* metadata = [[DBMetadata alloc] initWithDictionary:dict];
  949. [results addObject:metadata];
  950. [metadata release];
  951. }
  952. }
  953. NSString* path = [request.userInfo objectForKey:@"path"];
  954. NSString* keyword = [request.userInfo objectForKey:@"keyword"];
  955. if ([delegate respondsToSelector:@selector(restClient:loadedSearchResults:forPath:keyword:)]) {
  956. [delegate restClient:self loadedSearchResults:results forPath:path keyword:keyword];
  957. }
  958. }
  959. [requests removeObject:request];
  960. }
  961. - (void)loadSharableLinkForFile:(NSString *)path {
  962. [self loadSharableLinkForFile:path shortUrl:YES];
  963. }
  964. - (void)loadSharableLinkForFile:(NSString*)path shortUrl:(BOOL)createShortUrl {
  965. NSString* fullPath = [NSString stringWithFormat:@"/shares/%@%@", root, path];
  966. NSString *shortUrlVal = createShortUrl ? @"true" : @"false";
  967. NSDictionary *params = [NSDictionary dictionaryWithObject:shortUrlVal forKey:@"short_url"];
  968. NSURLRequest* urlRequest =
  969. [self requestWithHost:kDBDropboxAPIHost path:fullPath parameters:params];
  970. DBRequest* request =
  971. [[[DBRequest alloc]
  972. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadSharableLink:)]
  973. autorelease];
  974. request.userInfo = [NSDictionary dictionaryWithObject:path forKey:@"path"];
  975. [requests addObject:request];
  976. }
  977. - (void)requestDidLoadSharableLink:(DBRequest*)request {
  978. [[self retain] autorelease];
  979. if (request.error) {
  980. [self checkForAuthenticationFailure:request];
  981. if ([delegate respondsToSelector:@selector(restClient:loadSharableLinkFailedWithError:)]) {
  982. [delegate restClient:self loadSharableLinkFailedWithError:request.error];
  983. }
  984. } else {
  985. NSString* sharableLink = [(NSDictionary*)request.resultJSON objectForKey:@"url"];
  986. NSString* path = [request.userInfo objectForKey:@"path"];
  987. if ([delegate respondsToSelector:@selector(restClient:loadedSharableLink:forFile:)]) {
  988. [delegate restClient:self loadedSharableLink:sharableLink forFile:path];
  989. }
  990. }
  991. [requests removeObject:request];
  992. }
  993. - (void)loadStreamableURLForFile:(NSString *)path {
  994. NSString* fullPath = [NSString stringWithFormat:@"/media/%@%@", root, path];
  995. NSURLRequest* urlRequest =
  996. [self requestWithHost:kDBDropboxAPIHost path:fullPath parameters:nil];
  997. DBRequest *request =
  998. [[[DBRequest alloc]
  999. initWithURLRequest:urlRequest andInformTarget:self selector:@selector(requestDidLoadStreamableURL:)]
  1000. autorelease];
  1001. request.userInfo = [NSDictionary dictionaryWithObject:path forKey:@"path"];
  1002. [requests addObject:request];
  1003. }
  1004. - (void)requestDidLoadStreamableURL:(DBRequest *)request {
  1005. [[self retain] autorelease];
  1006. if (request.error) {
  1007. [self checkForAuthenticationFailure:request];
  1008. if ([delegate respondsToSelector:@selector(restClient:loadStreamableURLFailedWithError:)]) {
  1009. [delegate restClient:self loadStreamableURLFailedWithError:request.error];
  1010. }
  1011. } else {
  1012. NSDictionary *response = [request parseResponseAsType:[NSDictionary class]];
  1013. NSURL *url = [NSURL URLWithString:[response objectForKey:@"url"]];
  1014. NSString *path = [request.userInfo objectForKey:@"path"];
  1015. if ([delegate respondsToSelector:@selector(restClient:loadedStreamableURL:forFile:)]) {
  1016. [delegate restClient:self loadedStreamableURL:url forFile:path];
  1017. }
  1018. }
  1019. [requests removeObject:request];
  1020. }
  1021. - (NSUInteger)requestCount {
  1022. return [requests count] + [loadRequests count] + [imageLoadRequests count] + [uploadRequests count];
  1023. }
  1024. #pragma mark private methods
  1025. + (NSString*)escapePath:(NSString*)path {
  1026. CFStringEncoding encoding = CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding);
  1027. NSString *escapedPath =
  1028. (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
  1029. (CFStringRef)path,
  1030. NULL,
  1031. (CFStringRef)@":?=,!$&'()*+;[]@#~",
  1032. encoding);
  1033. return [escapedPath autorelease];
  1034. }
  1035. + (NSString *)bestLanguage {
  1036. static NSString *preferredLang = nil;
  1037. if (!preferredLang) {
  1038. NSString *lang = [[NSLocale preferredLanguages] objectAtIndex:0];
  1039. if ([[[NSBundle mainBundle] localizations] containsObject:lang])
  1040. preferredLang = [lang copy];
  1041. else
  1042. preferredLang = @"en";
  1043. }
  1044. return preferredLang;
  1045. }
  1046. + (NSString *)userAgent {
  1047. static NSString *userAgent;
  1048. if (!userAgent) {
  1049. NSBundle *bundle = [NSBundle mainBundle];
  1050. NSString *appName = [[bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"]
  1051. stringByReplacingOccurrencesOfString:@" " withString:@""];
  1052. NSString *appVersion = [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
  1053. userAgent =
  1054. [[NSString alloc] initWithFormat:@"%@/%@ OfficialDropboxIosSdk/%@", appName, appVersion, kDBSDKVersion];
  1055. }
  1056. return userAgent;
  1057. }
  1058. - (NSMutableURLRequest*)requestWithHost:(NSString*)host path:(NSString*)path
  1059. parameters:(NSDictionary*)params {
  1060. return [self requestWithHost:host path:path parameters:params method:nil];
  1061. }
  1062. - (NSMutableURLRequest*)requestWithHost:(NSString*)host path:(NSString*)path
  1063. parameters:(NSDictionary*)params method:(NSString*)method {
  1064. NSString* escapedPath = [DBRestClient escapePath:path];
  1065. NSString* urlString = [NSString stringWithFormat:@"%@://%@/%@%@",
  1066. kDBProtocolHTTPS, host, kDBDropboxAPIVersion, escapedPath];
  1067. NSURL* url = [NSURL URLWithString:urlString];
  1068. NSMutableDictionary *allParams =
  1069. [NSMutableDictionary dictionaryWithObject:[DBRestClient bestLanguage] forKey:@"locale"];
  1070. if (params) {
  1071. [allParams addEntriesFromDictionary:params];
  1072. }
  1073. NSArray *extraParams = [MPURLRequestParameter parametersFromDictionary:allParams];
  1074. NSArray *paramList =
  1075. [[self.credentialStore oauthParameters] arrayByAddingObjectsFromArray:extraParams];
  1076. MPOAuthURLRequest* oauthRequest =
  1077. [[[MPOAuthURLRequest alloc] initWithURL:url andParameters:paramList] autorelease];
  1078. if (method) {
  1079. oauthRequest.HTTPMethod = method;
  1080. }
  1081. NSMutableURLRequest* urlRequest = [oauthRequest
  1082. urlRequestSignedWithSecret:self.credentialStore.signingKey
  1083. usingMethod:self.credentialStore.signatureMethod];
  1084. [urlRequest setTimeoutInterval:20];
  1085. [urlRequest setValue:[DBRestClient userAgent] forHTTPHeaderField:@"User-Agent"];
  1086. return urlRequest;
  1087. }
  1088. - (void)checkForAuthenticationFailure:(DBRequest*)request {
  1089. if (request.error && request.error.code == 401 && [request.error.domain isEqual:DBErrorDomain]) {
  1090. [session.delegate sessionDidReceiveAuthorizationFailure:session userId:userId];
  1091. }
  1092. }
  1093. - (MPOAuthCredentialConcreteStore *)credentialStore {
  1094. return [session credentialStoreForUserId:userId];
  1095. }
  1096. @end