@@ -49,15 +49,19 @@ @interface OFURLPathAllowedCharacterSet: OFURLAllowedCharacterSetBase @end @interface OFURLQueryOrFragmentAllowedCharacterSet: OFURLAllowedCharacterSetBase @end + +@interface OFURLQueryKeyValueAllowedCharacterSet: OFURLAllowedCharacterSetBase +@end static OFCharacterSet *URLAllowedCharacterSet = nil; static OFCharacterSet *URLSchemeAllowedCharacterSet = nil; static OFCharacterSet *URLPathAllowedCharacterSet = nil; static OFCharacterSet *URLQueryOrFragmentAllowedCharacterSet = nil; +static OFCharacterSet *URLQueryKeyValueAllowedCharacterSet = nil; static of_once_t URLAllowedCharacterSetOnce = OF_ONCE_INIT; static of_once_t URLQueryOrFragmentAllowedCharacterSetOnce = OF_ONCE_INIT; static void @@ -84,10 +88,17 @@ initURLQueryOrFragmentAllowedCharacterSet(void) { URLQueryOrFragmentAllowedCharacterSet = [[OFURLQueryOrFragmentAllowedCharacterSet alloc] init]; } + +static void +initURLQueryKeyValueAllowedCharacterSet(void) +{ + URLQueryKeyValueAllowedCharacterSet = + [[OFURLQueryKeyValueAllowedCharacterSet alloc] init]; +} OF_DIRECT_MEMBERS @interface OFInvertedCharacterSetWithoutPercent: OFCharacterSet { OFCharacterSet *_characterSet; @@ -247,10 +258,41 @@ default: return false; } } @end + +@implementation OFURLQueryKeyValueAllowedCharacterSet +- (bool)characterIsMember: (of_unichar_t)character +{ + if (character < CHAR_MAX && of_ascii_isalnum(character)) + return true; + + switch (character) { + case '-': + case '.': + case '_': + case '~': + case '!': + case '$': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case ':': + case '@': + case '/': + case '?': + return true; + default: + return false; + } +} +@end @implementation OFInvertedCharacterSetWithoutPercent - (instancetype)initWithCharacterSet: (OFCharacterSet *)characterSet { self = [super init]; @@ -339,10 +381,18 @@ of_once(&URLQueryOrFragmentAllowedCharacterSetOnce, initURLQueryOrFragmentAllowedCharacterSet); return URLQueryOrFragmentAllowedCharacterSet; } + ++ (OFCharacterSet *)URLQueryKeyValueAllowedCharacterSet +{ + static of_once_t onceControl = OF_ONCE_INIT; + of_once(&onceControl, initURLQueryKeyValueAllowedCharacterSet); + + return URLQueryKeyValueAllowedCharacterSet; +} + (OFCharacterSet *)URLFragmentAllowedCharacterSet { of_once(&URLQueryOrFragmentAllowedCharacterSetOnce, initURLQueryOrFragmentAllowedCharacterSet); @@ -983,10 +1033,35 @@ - (OFString *)URLEncodedQuery { return _URLEncodedQuery; } + +- (OFDictionary OF_GENERIC(OFString *, OFString *) *)queryDictionary +{ + void *pool = objc_autoreleasePoolPush(); + OFArray *pairs = [_URLEncodedQuery componentsSeparatedByString: @"&"]; + OFMutableDictionary *ret = [OFMutableDictionary + dictionaryWithCapacity: pairs.count]; + + for (OFString *pair in pairs) { + OFArray *parts = [pair componentsSeparatedByString: @"="]; + + if (parts.count != 2) + @throw [OFInvalidFormatException exception]; + + [ret setObject: [[parts objectAtIndex: 1] stringByURLDecoding] + forKey: [[parts objectAtIndex: 0] stringByURLDecoding]]; + } + + [ret makeImmutable]; + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} - (OFString *)fragment { return _URLEncodedFragment.stringByURLDecoding; }