Skip to content

Commit 382e865

Browse files
committedMay 27, 2016
Fix issue causing file paths with non-ASCII characters to not work.
1 parent d7d8ff3 commit 382e865

File tree

1 file changed

+45
-15
lines changed

1 file changed

+45
-15
lines changed
 

‎TJDropbox/TJDropbox.m

+45-15
Original file line numberDiff line numberDiff line change
@@ -110,35 +110,41 @@ + (NSMutableURLRequest *)requestWithBaseURLString:(NSString *const)baseURLString
110110
return request;
111111
}
112112

113-
+ (NSData *)parameterDataForParameters:(NSDictionary<NSString *, NSString *> *)parameters
113+
+ (NSString *)parameterStringForParameters:(NSDictionary<NSString *, NSString *> *)parameters
114114
{
115-
NSData *parameterData = nil;
115+
NSString *parameterString = nil;
116116
if (parameters.count > 0) {
117117
NSError *error = nil;
118-
parameterData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&error];
118+
NSData *const parameterData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&error];
119119
if (error) {
120120
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:@"\\"];
121130
}
122131
}
123-
return parameterData;
132+
return parameterString;
124133
}
125134

126135
+ (NSURLRequest *)apiRequestWithPath:(NSString *const)path accessToken:(NSString *const)accessToken parameters:(NSDictionary<NSString *, NSString *> *const)parameters
127136
{
128137
NSMutableURLRequest *const request = [self requestWithBaseURLString:@"https://api.dropboxapi.com" path:path accessToken:accessToken];
129138
[request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
130-
request.HTTPBody = [self parameterDataForParameters:parameters];
139+
request.HTTPBody = [[self parameterStringForParameters:parameters] dataUsingEncoding:NSUTF8StringEncoding];
131140
return request;
132141
}
133142

134143
+ (NSMutableURLRequest *)contentRequestWithPath:(NSString *const)path accessToken:(NSString *const)accessToken parameters:(NSDictionary<NSString *, NSString *> *const)parameters
135144
{
136145
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"];
142148
return request;
143149
}
144150

@@ -222,6 +228,30 @@ + (BOOL)processResultJSONData:(NSData *const)data response:(NSURLResponse *const
222228
return *error == nil;
223229
}
224230

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+
225255
#pragma mark - File Inspection
226256

227257
+ (NSURLRequest *)listFolderRequestWithPath:(NSString *const)filePath accessToken:(NSString *const)accessToken cursor:(nullable NSString *const)cursor
@@ -231,7 +261,7 @@ + (NSURLRequest *)listFolderRequestWithPath:(NSString *const)filePath accessToke
231261
if (cursor.length > 0) {
232262
[parameters setObject:cursor forKey:@"cursor"];
233263
} else {
234-
[parameters setObject:filePath forKey:@"path"];
264+
[parameters setObject:[self asciiEncodeString:filePath] forKey:@"path"];
235265
}
236266
return [self apiRequestWithPath:urlPath accessToken:accessToken parameters:parameters];
237267
}
@@ -278,7 +308,7 @@ + (void)listFolderWithPath:(NSString *const)path accessToken:(NSString *const)ac
278308
+ (void)downloadFileAtPath:(NSString *const)remotePath toPath:(NSString *const)localPath accessToken:(NSString *const)accessToken completion:(void (^const)(NSDictionary *_Nullable parsedResponse, NSError *_Nullable error))completion
279309
{
280310
NSURLRequest *const request = [self contentRequestWithPath:@"/2/files/download" accessToken:accessToken parameters:@{
281-
@"path": remotePath
311+
@"path": [self asciiEncodeString:remotePath]
282312
}];
283313

284314
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
300330
+ (void)uploadFileAtPath:(NSString *const)localPath toPath:(NSString *const)remotePath accessToken:(NSString *const)accessToken completion:(void (^const)(NSDictionary *_Nullable parsedResponse, NSError *_Nullable error))completion
301331
{
302332
NSMutableURLRequest *const request = [self contentRequestWithPath:@"/2/files/upload" accessToken:accessToken parameters:@{
303-
@"path": remotePath
333+
@"path": [self asciiEncodeString:remotePath]
304334
}];
305335

306336
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
316346
+ (void)deleteFileAtPath:(NSString *const)path accessToken:(NSString *const)accessToken completion:(void (^const)(NSDictionary *_Nullable parsedResponse, NSError *_Nullable error))completion
317347
{
318348
NSURLRequest *const request = [self apiRequestWithPath:@"/2/files/delete" accessToken:accessToken parameters:@{
319-
@"path": path
349+
@"path": [self asciiEncodeString:path]
320350
}];
321351
[self performAPIRequest:request withCompletion:completion];
322352
}
@@ -326,7 +356,7 @@ + (void)deleteFileAtPath:(NSString *const)path accessToken:(NSString *const)acce
326356
+ (void)getSharedLinkForFileAtPath:(NSString *const)path accessToken:(NSString *const)accessToken completion:(void (^const)(NSString *_Nullable urlString))completion
327357
{
328358
NSURLRequest *const request = [self apiRequestWithPath:@"/2/sharing/create_shared_link_with_settings" accessToken:accessToken parameters:@{
329-
@"path": path
359+
@"path": [self asciiEncodeString:path]
330360
}];
331361
[self performAPIRequest:request withCompletion:^(NSDictionary * _Nullable parsedResponse, NSError * _Nullable error) {
332362
completion(parsedResponse[@"url"]);

0 commit comments

Comments
 (0)
Please sign in to comment.