Index: src/OFURL.m ================================================================== --- src/OFURL.m +++ src/OFURL.m @@ -52,10 +52,11 @@ char *UTF8String, *UTF8String2 = NULL; self = [super init]; @try { + void *pool = objc_autoreleasePoolPush(); char *tmp, *tmp2; if ((UTF8String2 = of_strdup([string UTF8String])) == NULL) @throw [OFOutOfMemoryException exceptionWithRequestedSize: @@ -67,19 +68,21 @@ @throw [OFInvalidFormatException exception]; for (tmp2 = UTF8String; tmp2 < tmp; tmp2++) *tmp2 = tolower((int)*tmp2); - _scheme = [[OFString alloc] - initWithUTF8String: UTF8String - length: tmp - UTF8String]; + _scheme = [[[OFString stringWithUTF8String: UTF8String + length: tmp - UTF8String] + stringByURLDecoding] copy]; UTF8String = tmp + 3; if ([_scheme isEqual: @"file"]) { - _path = [[OFString alloc] - initWithUTF8String: UTF8String]; + _path = [[[OFString stringWithUTF8String: + UTF8String] stringByURLDecoding] copy]; + + objc_autoreleasePoolPop(pool); return self; } if ((tmp = strchr(UTF8String, '/')) != NULL) { *tmp = '\0'; @@ -94,17 +97,17 @@ if ((tmp3 = strchr(UTF8String, ':')) != NULL) { *tmp3 = '\0'; tmp3++; - _user = [[OFString alloc] - initWithUTF8String: UTF8String]; - _password = [[OFString alloc] - initWithUTF8String: tmp3]; + _user = [[[OFString stringWithUTF8String: + UTF8String] stringByURLDecoding] copy]; + _password = [[[OFString stringWithUTF8String: + tmp3] stringByURLDecoding] copy]; } else - _user = [[OFString alloc] - initWithUTF8String: UTF8String]; + _user = [[[OFString stringWithUTF8String: + UTF8String] stringByURLDecoding] copy]; UTF8String = tmp2; } if ((tmp2 = strchr(UTF8String, ':')) != NULL) { @@ -112,12 +115,12 @@ OFString *portString; *tmp2 = '\0'; tmp2++; - _host = [[OFString alloc] - initWithUTF8String: UTF8String]; + _host = [[[OFString stringWithUTF8String: + UTF8String] stringByURLDecoding] copy]; pool = objc_autoreleasePoolPush(); portString = [OFString stringWithUTF8String: tmp2]; if ([portString decimalValue] > 65535) @@ -125,12 +128,12 @@ _port = [portString decimalValue]; objc_autoreleasePoolPop(pool); } else { - _host = [[OFString alloc] - initWithUTF8String: UTF8String]; + _host = [[[OFString stringWithUTF8String: + UTF8String] stringByURLDecoding] copy]; if ([_scheme isEqual: @"http"]) _port = 80; else if ([_scheme isEqual: @"https"]) _port = 443; @@ -140,31 +143,33 @@ if ((UTF8String = tmp) != NULL) { if ((tmp = strchr(UTF8String, '#')) != NULL) { *tmp = '\0'; - _fragment = [[OFString alloc] - initWithUTF8String: tmp + 1]; + _fragment = [[[OFString stringWithUTF8String: + tmp + 1] stringByURLDecoding] copy]; } if ((tmp = strchr(UTF8String, '?')) != NULL) { *tmp = '\0'; - _query = [[OFString alloc] - initWithUTF8String: tmp + 1]; + _query = [[[OFString stringWithUTF8String: + tmp + 1] stringByURLDecoding] copy]; } if ((tmp = strchr(UTF8String, ';')) != NULL) { *tmp = '\0'; - _parameters = [[OFString alloc] - initWithUTF8String: tmp + 1]; + _parameters = [[[OFString stringWithUTF8String: + tmp + 1] stringByURLDecoding] copy]; } - _path = [[OFString alloc] - initWithUTF8String: UTF8String]; + _path = [[[OFString stringWithUTF8String: + UTF8String] stringByURLDecoding] copy]; } + + objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; } @finally { free(UTF8String2); @@ -182,10 +187,11 @@ return [self initWithString: string]; self = [super init]; @try { + void *pool = objc_autoreleasePoolPush(); char *tmp; _scheme = [URL->_scheme copy]; _host = [URL->_host copy]; _port = URL->_port; @@ -199,47 +205,46 @@ UTF8String = UTF8String2; if ((tmp = strchr(UTF8String, '#')) != NULL) { *tmp = '\0'; - _fragment = [[OFString alloc] - initWithUTF8String: tmp + 1]; + _fragment = [[[OFString stringWithUTF8String: + tmp + 1] stringByURLDecoding] copy]; } if ((tmp = strchr(UTF8String, '?')) != NULL) { *tmp = '\0'; - _query = [[OFString alloc] initWithUTF8String: tmp + 1]; + _query = [[[OFString stringWithUTF8String: + tmp + 1] stringByURLDecoding] copy]; } if ((tmp = strchr(UTF8String, ';')) != NULL) { *tmp = '\0'; - _parameters = [[OFString alloc] - initWithUTF8String: tmp + 1]; + _parameters = [[[OFString stringWithUTF8String: + tmp + 1] stringByURLDecoding] copy]; } if (*UTF8String == '/') - _path = [[OFString alloc] - initWithUTF8String: UTF8String + 1]; + _path = [[[OFString stringWithUTF8String: + UTF8String + 1] stringByURLDecoding] copy]; else { - void *pool; - OFString *s; + OFString *path, *s; - pool = objc_autoreleasePoolPush(); + path = [[[OFString stringWithUTF8String: + UTF8String] stringByURLDecoding] copy]; if ([URL->_path hasSuffix: @"/"]) - s = [OFString stringWithFormat: @"%@%s", - URL->_path, - UTF8String]; + s = [URL->_path stringByAppendingString: path]; else - s = [OFString stringWithFormat: @"%@/../%s", + s = [OFString stringWithFormat: @"%@/../%@", URL->_path, - UTF8String]; + path]; _path = [[s stringByStandardizingURLPath] copy]; - - objc_autoreleasePoolPop(pool); } + + objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; } @finally { free(UTF8String2); @@ -451,44 +456,53 @@ OF_SETTER(_fragment, fragment, true, 1) } - (OFString*)string { - OFMutableString *ret = [OFMutableString stringWithFormat: @"%@://", - _scheme]; + OFMutableString *ret = [OFMutableString string]; + void *pool = objc_autoreleasePoolPush(); + + [ret appendFormat: @"%@://", [_scheme stringByURLEncoding]]; if ([_scheme isEqual: @"file"]) { if (_path != nil) - [ret appendString: _path]; + [ret appendString: [_path + stringByURLEncodingWithIgnoredCharacters: "/"]]; + objc_autoreleasePoolPop(pool); return ret; } if (_user != nil && _password != nil) - [ret appendFormat: @"%@:%@@", _user, _password]; + [ret appendFormat: @"%@:%@@", + [_user stringByURLEncoding], + [_password stringByURLEncoding]]; else if (_user != nil) - [ret appendFormat: @"%@@", _user]; + [ret appendFormat: @"%@@", [_user stringByURLEncoding]]; if (_host != nil) - [ret appendString: _host]; + [ret appendString: [_host stringByURLEncoding]]; if (([_scheme isEqual: @"http"] && _port != 80) || ([_scheme isEqual: @"https"] && _port != 443) || ([_scheme isEqual: @"ftp"] && _port != 21)) [ret appendFormat: @":%u", _port]; if (_path != nil) - [ret appendFormat: @"/%@", _path]; + [ret appendFormat: @"/%@", + [_path stringByURLEncodingWithIgnoredCharacters: "/"]]; if (_parameters != nil) - [ret appendFormat: @";%@", _parameters]; + [ret appendFormat: @";%@", [_parameters stringByURLEncoding]]; if (_query != nil) - [ret appendFormat: @"?%@", _query]; + [ret appendFormat: @"?%@", [_query stringByURLEncoding]]; if (_fragment != nil) - [ret appendFormat: @"#%@", _fragment]; + [ret appendFormat: @"#%@", [_fragment stringByURLEncoding]]; + + objc_autoreleasePoolPop(pool); [ret makeImmutable]; return ret; } Index: tests/OFURLTests.m ================================================================== --- tests/OFURLTests.m +++ tests/OFURLTests.m @@ -23,11 +23,12 @@ #import "OFInvalidFormatException.h" #import "TestsAppDelegate.h" static OFString *module = @"OFURL"; -static OFString *url_str = @"http://u:p@h:1234/f;p?q#f"; +static OFString *url_str = @"ht%3Atp://us%3Aer:p%40w@ho%3Ast:1234/" + @"pa%3Bth;pa%3Fram?que%23ry#frag%23ment"; @implementation TestsAppDelegate (OFURLTests) - (void)URLTests { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; @@ -40,11 +41,11 @@ R(u4 = [OFURL URLWithString: @"file:///etc/passwd"])) TEST(@"+[URLWithString:relativeToURL:]", [[[OFURL URLWithString: @"/foo" relativeToURL: u1] string] isEqual: - @"http://u:p@h:1234/foo"] && + @"ht%3Atp://us%3Aer:p%40w@ho%3Ast:1234/foo"] && [[[OFURL URLWithString: @"foo/bar?q" relativeToURL: [OFURL URLWithString: @"http://h/qux/quux"]] string] isEqual: @"http://h/qux/foo/bar?q"] && [[[OFURL URLWithString: @"foo/bar" relativeToURL: [OFURL URLWithString: @"http://h/qux/?x"]] @@ -57,23 +58,24 @@ [[u2 string] isEqual: @"http://foo"] && [[u3 string] isEqual: @"http://bar/"] && [[u4 string] isEqual: @"file:///etc/passwd"]) TEST(@"-[scheme]", - [[u1 scheme] isEqual: @"http"] && [[u4 scheme] isEqual: @"file"]) - TEST(@"-[user]", [[u1 user] isEqual: @"u"] && [u4 user] == nil) + [[u1 scheme] isEqual: @"ht%3Atp"] && [[u4 scheme] isEqual: @"file"]) + TEST(@"-[user]", [[u1 user] isEqual: @"us:er"] && [u4 user] == nil) TEST(@"-[password]", - [[u1 password] isEqual: @"p"] && [u4 password] == nil) - TEST(@"-[host]", [[u1 host] isEqual: @"h"] && [u4 port] == 0) + [[u1 password] isEqual: @"p@w"] && [u4 password] == nil) + TEST(@"-[host]", [[u1 host] isEqual: @"ho:st"] && [u4 port] == 0) TEST(@"-[port]", [u1 port] == 1234) TEST(@"-[path]", - [[u1 path] isEqual: @"f"] && [[u4 path] isEqual: @"/etc/passwd"]) + [[u1 path] isEqual: @"pa;th"] && + [[u4 path] isEqual: @"/etc/passwd"]) TEST(@"-[parameters]", - [[u1 parameters] isEqual: @"p"] && [u4 parameters] == nil) - TEST(@"-[query]", [[u1 query] isEqual: @"q"] && [u4 query] == nil) + [[u1 parameters] isEqual: @"pa?ram"] && [u4 parameters] == nil) + TEST(@"-[query]", [[u1 query] isEqual: @"que#ry"] && [u4 query] == nil) TEST(@"-[fragment]", - [[u1 fragment] isEqual: @"f"] && [u4 fragment] == nil) + [[u1 fragment] isEqual: @"frag#ment"] && [u4 fragment] == nil) TEST(@"-[copy]", R(u4 = [[u1 copy] autorelease])) TEST(@"-[isEqual:]", [u1 isEqual: u4] && ![u2 isEqual: u3] && [[OFURL URLWithString: @"HTTP://bar/"] isEqual: u3])