@@ -110,35 +110,41 @@ + (NSMutableURLRequest *)requestWithBaseURLString:(NSString *const)baseURLString
110
110
return request;
111
111
}
112
112
113
- + (NSData *)parameterDataForParameters : (NSDictionary <NSString *, NSString *> *)parameters
113
+ + (NSString *)parameterStringForParameters : (NSDictionary <NSString *, NSString *> *)parameters
114
114
{
115
- NSData *parameterData = nil ;
115
+ NSString *parameterString = nil ;
116
116
if (parameters.count > 0 ) {
117
117
NSError *error = nil ;
118
- parameterData = [NSJSONSerialization dataWithJSONObject: parameters options: 0 error: &error];
118
+ NSData * const parameterData = [NSJSONSerialization dataWithJSONObject: parameters options: 0 error: &error];
119
119
if (error) {
120
120
NSLog (@" [TJDropbox] - Error in %s : %@ " , __PRETTY_FUNCTION__, error);
121
+ } else {
122
+ parameterString = [[NSString alloc ] initWithData: parameterData encoding: NSUTF8StringEncoding];
123
+
124
+ // Ugh http://stackoverflow.com/a/24807307
125
+ // Asian characters are formatted as ASCII using +asciiEncodeString:, which adds a leading '\'.
126
+ // NSJSONSerialization likes to turn '\' into '\\', which Dropbox doesn't tolerate.
127
+ // This is a gross way of fixing it, but it works.
128
+ // Sucks because we have to round trip from dictionary -> data -> string -> data in a lot of cases.
129
+ parameterString = [parameterString stringByReplacingOccurrencesOfString: @" \\\\ " withString: @" \\ " ];
121
130
}
122
131
}
123
- return parameterData ;
132
+ return parameterString ;
124
133
}
125
134
126
135
+ (NSURLRequest *)apiRequestWithPath : (NSString *const )path accessToken : (NSString *const )accessToken parameters : (NSDictionary <NSString *, NSString *> *const )parameters
127
136
{
128
137
NSMutableURLRequest *const request = [self requestWithBaseURLString: @" https://api.dropboxapi.com" path: path accessToken: accessToken];
129
138
[request addValue: @" application/json" forHTTPHeaderField: @" Content-Type" ];
130
- request.HTTPBody = [self parameterDataForParameters : parameters];
139
+ request.HTTPBody = [[ self parameterStringForParameters : parameters] dataUsingEncoding: NSUTF8StringEncoding ];
131
140
return request;
132
141
}
133
142
134
143
+ (NSMutableURLRequest *)contentRequestWithPath : (NSString *const )path accessToken : (NSString *const )accessToken parameters : (NSDictionary <NSString *, NSString *> *const )parameters
135
144
{
136
145
NSMutableURLRequest *const request = [self requestWithBaseURLString: @" https://content.dropboxapi.com" path: path accessToken: accessToken];
137
- NSData *const parameterData = [self parameterDataForParameters: parameters];
138
- if (parameterData) {
139
- NSString *const parameterString = [[NSString alloc ] initWithData: parameterData encoding: NSUTF8StringEncoding];
140
- [request setValue: parameterString forHTTPHeaderField: @" Dropbox-API-Arg" ];
141
- }
146
+ NSString *const parameterString = [self parameterStringForParameters: parameters];
147
+ [request setValue: parameterString forHTTPHeaderField: @" Dropbox-API-Arg" ];
142
148
return request;
143
149
}
144
150
@@ -222,6 +228,30 @@ + (BOOL)processResultJSONData:(NSData *const)data response:(NSURLResponse *const
222
228
return *error == nil ;
223
229
}
224
230
231
+ + (NSString *)asciiEncodeString : (NSString *const )string
232
+ {
233
+ // Inspired by: https://github.com/dropbox/SwiftyDropbox/blob/6747041b04e337efe0de8f3be14acaf3b6d6d19b/Source/Client.swift#L90-L104
234
+ // Useful: http://stackoverflow.com/a/1775880
235
+ // Useful: https://www.objc.io/issues/9-strings/unicode/
236
+
237
+ NSMutableString *const result = string ? [NSMutableString new ] : nil ;
238
+
239
+ [string enumerateSubstringsInRange: NSMakeRange (0 , string.length) options: NSStringEnumerationByComposedCharacterSequences usingBlock: ^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
240
+ const unichar character = [substring characterAtIndex: 0 ];
241
+ NSString *stringToAppend = nil ;
242
+ if (character > 127 ) {
243
+ stringToAppend = [NSString stringWithFormat: @" \\ u%04x " , character];
244
+ } else {
245
+ stringToAppend = substring;
246
+ }
247
+ if (stringToAppend) {
248
+ [result appendString: stringToAppend];
249
+ }
250
+ }];
251
+
252
+ return result;
253
+ }
254
+
225
255
#pragma mark - File Inspection
226
256
227
257
+ (NSURLRequest *)listFolderRequestWithPath : (NSString *const )filePath accessToken : (NSString *const )accessToken cursor : (nullable NSString *const )cursor
@@ -231,7 +261,7 @@ + (NSURLRequest *)listFolderRequestWithPath:(NSString *const)filePath accessToke
231
261
if (cursor.length > 0 ) {
232
262
[parameters setObject: cursor forKey: @" cursor" ];
233
263
} else {
234
- [parameters setObject: filePath forKey: @" path" ];
264
+ [parameters setObject: [ self asciiEncodeString: filePath] forKey: @" path" ];
235
265
}
236
266
return [self apiRequestWithPath: urlPath accessToken: accessToken parameters: parameters];
237
267
}
@@ -278,7 +308,7 @@ + (void)listFolderWithPath:(NSString *const)path accessToken:(NSString *const)ac
278
308
+ (void )downloadFileAtPath : (NSString *const )remotePath toPath : (NSString *const )localPath accessToken : (NSString *const )accessToken completion : (void (^const )(NSDictionary *_Nullable parsedResponse, NSError *_Nullable error))completion
279
309
{
280
310
NSURLRequest *const request = [self contentRequestWithPath: @" /2/files/download" accessToken: accessToken parameters: @{
281
- @" path" : remotePath
311
+ @" path" : [ self asciiEncodeString: remotePath]
282
312
}];
283
313
284
314
NSURLSessionTask *const task = [[self session ] downloadTaskWithRequest: request completionHandler: ^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
@@ -300,7 +330,7 @@ + (void)downloadFileAtPath:(NSString *const)remotePath toPath:(NSString *const)l
300
330
+ (void )uploadFileAtPath : (NSString *const )localPath toPath : (NSString *const )remotePath accessToken : (NSString *const )accessToken completion : (void (^const )(NSDictionary *_Nullable parsedResponse, NSError *_Nullable error))completion
301
331
{
302
332
NSMutableURLRequest *const request = [self contentRequestWithPath: @" /2/files/upload" accessToken: accessToken parameters: @{
303
- @" path" : remotePath
333
+ @" path" : [ self asciiEncodeString: remotePath]
304
334
}];
305
335
306
336
NSURLSessionTask *const task = [[self session ] uploadTaskWithRequest: request fromFile: [NSURL fileURLWithPath: localPath] completionHandler: ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
@@ -316,7 +346,7 @@ + (void)uploadFileAtPath:(NSString *const)localPath toPath:(NSString *const)remo
316
346
+ (void )deleteFileAtPath : (NSString *const )path accessToken : (NSString *const )accessToken completion : (void (^const )(NSDictionary *_Nullable parsedResponse, NSError *_Nullable error))completion
317
347
{
318
348
NSURLRequest *const request = [self apiRequestWithPath: @" /2/files/delete" accessToken: accessToken parameters: @{
319
- @" path" : path
349
+ @" path" : [ self asciiEncodeString: path]
320
350
}];
321
351
[self performAPIRequest: request withCompletion: completion];
322
352
}
@@ -326,7 +356,7 @@ + (void)deleteFileAtPath:(NSString *const)path accessToken:(NSString *const)acce
326
356
+ (void )getSharedLinkForFileAtPath : (NSString *const )path accessToken : (NSString *const )accessToken completion : (void (^const )(NSString *_Nullable urlString))completion
327
357
{
328
358
NSURLRequest *const request = [self apiRequestWithPath: @" /2/sharing/create_shared_link_with_settings" accessToken: accessToken parameters: @{
329
- @" path" : path
359
+ @" path" : [ self asciiEncodeString: path]
330
360
}];
331
361
[self performAPIRequest: request withCompletion: ^(NSDictionary * _Nullable parsedResponse, NSError * _Nullable error) {
332
362
completion (parsedResponse[@" url" ]);
0 commit comments