diff --git a/Couch/CouchDesignDocument.m b/Couch/CouchDesignDocument.m index 99aa210..a627801 100644 --- a/Couch/CouchDesignDocument.m +++ b/Couch/CouchDesignDocument.m @@ -251,4 +251,29 @@ - (RESTOperation*) saveChanges { } +#pragma mark - Overrides + +- (NSURL *)URL { + NSArray *relativePathComponents = [self.relativePath pathComponents]; + NSString *prefix = relativePathComponents[0]; + + // Init buffer with prefix and unescaped '/' + NSMutableString *escapedCompsBuffer; + escapedCompsBuffer = [[NSMutableString alloc] initWithString:[NSString stringWithFormat:@"%@/", prefix]]; + + // Go over rest of components, accumulating after escaping them + for (int i = 1; i < [relativePathComponents count]; i++) { + NSString *comp = relativePathComponents[i]; + comp = EscapeRelativePath(comp); + [escapedCompsBuffer appendString:comp]; + } + + // Add a forward slash, if it's missing, to the parent URL + NSURL *parentURL = [self.parent.URL URLByAppendingPathComponent:@""]; + NSURL *URL = [NSURL URLWithString:escapedCompsBuffer relativeToURL:parentURL]; + + return URL; +} + + @end diff --git a/Couch/CouchQuery.m b/Couch/CouchQuery.m index 017b592..0060b69 100644 --- a/Couch/CouchQuery.m +++ b/Couch/CouchQuery.m @@ -180,6 +180,31 @@ - (CouchLiveQuery*) asLiveQuery { } +#pragma mark - Overrides + +- (NSURL *)URL { + NSArray *relativePathComponents = [self.relativePath pathComponents]; + NSString *prefix = relativePathComponents[0]; + + // Init buffer with prefix and unescaped '/' + NSMutableString *escapedCompsBuffer; + escapedCompsBuffer = [[NSMutableString alloc] initWithString:[NSString stringWithFormat:@"%@/", prefix]]; + + // Go over rest of components, accumulating after escaping them + for (int i = 1; i<[relativePathComponents count]; i++) { + NSString *comp = relativePathComponents[i]; + comp = EscapeRelativePath(comp); + [escapedCompsBuffer appendString:comp]; + } + + // Add a forward slash, if it's missing, to the parent URL + NSURL *parentURL = [self.parent.URL URLByAppendingPathComponent:@""]; + NSURL *URL = [NSURL URLWithString:escapedCompsBuffer relativeToURL:parentURL]; + + return URL; +} + + @end diff --git a/REST/RESTInternal.h b/REST/RESTInternal.h index 3d9f1b2..62359c0 100644 --- a/REST/RESTInternal.h +++ b/REST/RESTInternal.h @@ -22,7 +22,7 @@ void RESTWarn(NSString* format, ...) __attribute__((format(__NSString__, 1, 2))) #define Warn RESTWarn extern BOOL gRESTWarnRaisesException; - +NSString *EscapeRelativePath(NSString *path); // Safe dynamic cast that returns nil if the object is not the expected class: #define $castIf(CLASSNAME,OBJ) ((CLASSNAME*)(RESTCastIf([CLASSNAME class],(OBJ)))) diff --git a/REST/RESTInternal.m b/REST/RESTInternal.m index 145caf3..5872cd2 100644 --- a/REST/RESTInternal.m +++ b/REST/RESTInternal.m @@ -59,6 +59,18 @@ id RESTCastIf( Class requiredClass, id object ) return array; } +NSString *EscapeRelativePath(NSString *path) { + /* + Escapes reserved URI characters. + RFC 3986 section 2.2 http://www.ietf.org/rfc/rfc3986.txt + */ + CFStringRef escapedPath = CFURLCreateStringByAddingPercentEscapes(NULL, + (CFStringRef)path, + NULL, + (CFStringRef)@":/?#[]@!$&'()*+,;=", + kCFStringEncodingUTF8); + return [(NSString *)escapedPath autorelease]; +} @implementation NSArray (RESTExtensions) diff --git a/REST/RESTResource.m b/REST/RESTResource.m index 4bbc44a..32c1fad 100644 --- a/REST/RESTResource.m +++ b/REST/RESTResource.m @@ -86,13 +86,16 @@ - (NSString*) description { } -- (NSURL*) URL { - if (_url) +- (NSURL *)URL { + if (_url) { return _url; - else if (_relativePath) - return [_parent.URL URLByAppendingPathComponent: _relativePath]; - else + } else if (_relativePath) { + // add a forward slash if it's missing + NSURL *parentURL = [_parent.URL URLByAppendingPathComponent:@""]; + return [NSURL URLWithString:EscapeRelativePath(_relativePath) relativeToURL:parentURL]; + } else { return nil; + } }