Merge branch 'master' of github.com:gnustep/libs-base

This commit is contained in:
Richard Frith-Macdonald 2020-04-21 09:03:25 +01:00
commit ccfb05525e
6 changed files with 195 additions and 87 deletions

View file

@ -34,7 +34,7 @@
also contain commas, most notably in the Expires field (which is not quoted also contain commas, most notably in the Expires field (which is not quoted
and can contain spaces as well). The last key/value does not have to have a and can contain spaces as well). The last key/value does not have to have a
semi-colon, so this can be tricky to parse if another cookie occurs semi-colon, so this can be tricky to parse if another cookie occurs
after this (See GSRangeOfCookie). after this (See GSCookieStrings).
*/ */
#import "common.h" #import "common.h"
@ -114,7 +114,7 @@ static const unsigned char whitespace[32] = {
#define GS_IS_WHITESPACE(X) IS_BIT_SET(whitespace[(X)/8], (X) % 8) #define GS_IS_WHITESPACE(X) IS_BIT_SET(whitespace[(X)/8], (X) % 8)
static id GSPropertyListFromCookieFormat(NSString *string, int version); static id GSPropertyListFromCookieFormat(NSString *string, int version);
static NSRange GSRangeOfCookie(NSString *string); static NSMutableArray *GSCookieStrings(NSString *string);
@implementation NSHTTPCookie @implementation NSHTTPCookie
@ -142,35 +142,41 @@ static NSRange GSRangeOfCookie(NSString *string);
andURL: (NSURL *)url andURL: (NSURL *)url
{ {
int version; int version;
NSString *defaultPath, *defaultDomain; NSString *defaultPath;
NSMutableArray *a; NSString *defaultDomain;
NSMutableArray *cookies;
NSUInteger count;
if ([header isEqual: @"Set-Cookie"]) if ([header isEqual: @"Set-Cookie"])
{
version = 0; version = 0;
}
else if ([header isEqual: @"Set-Cookie2"]) else if ([header isEqual: @"Set-Cookie2"])
{
version = 1; version = 1;
}
else else
{
return nil; return nil;
}
a = [NSMutableArray array];
defaultDomain = [url host]; defaultDomain = [url host];
defaultPath = [url path]; defaultPath = [url path];
if ([[url absoluteString] hasSuffix: @"/"] == NO) if ([[url absoluteString] hasSuffix: @"/"] == NO)
{
defaultPath = [defaultPath stringByDeletingLastPathComponent]; defaultPath = [defaultPath stringByDeletingLastPathComponent];
}
cookies = GSCookieStrings(field);
count = [cookies count];
/* We could use an NSScanner here, but this string could contain all /* We could use an NSScanner here, but this string could contain all
sorts of odd stuff. It's not quite a property list either - it has sorts of odd stuff. It's not quite a property list either - it has
dates and also could have tokens without values. */ dates and also could have tokens without values. */
while (1) while (count-- > 0)
{ {
NSHTTPCookie *cookie; NSHTTPCookie *cookie;
NSMutableDictionary *dict; NSMutableDictionary *dict;
NSString *onecookie; NSString *onecookie = [cookies objectAtIndex: count];
NSRange range = GSRangeOfCookie(field);
if (range.location == NSNotFound)
break;
onecookie = [field substringWithRange: range];
NS_DURING NS_DURING
dict = GSPropertyListFromCookieFormat(onecookie, version); dict = GSPropertyListFromCookieFormat(onecookie, version);
NS_HANDLER NS_HANDLER
@ -179,18 +185,25 @@ static NSRange GSRangeOfCookie(NSString *string);
if ([dict count]) if ([dict count])
{ {
if ([dict objectForKey: NSHTTPCookiePath] == nil) if ([dict objectForKey: NSHTTPCookiePath] == nil)
{
[dict setObject: defaultPath forKey: NSHTTPCookiePath]; [dict setObject: defaultPath forKey: NSHTTPCookiePath];
}
if ([dict objectForKey: NSHTTPCookieDomain] == nil) if ([dict objectForKey: NSHTTPCookieDomain] == nil)
{
[dict setObject: defaultDomain forKey: NSHTTPCookieDomain]; [dict setObject: defaultDomain forKey: NSHTTPCookieDomain];
}
cookie = [NSHTTPCookie cookieWithProperties: dict]; cookie = [NSHTTPCookie cookieWithProperties: dict];
if (cookie) if (cookie)
[a addObject: cookie]; {
[cookies replaceObjectAtIndex: count withObject: cookie];
} }
if ([field length] <= NSMaxRange(range)) else
break; {
field = [field substringFromIndex: NSMaxRange(range)+1]; [cookies removeObjectAtIndex: count];
} }
return a; }
}
return cookies;
} }
+ (NSArray *) cookiesWithResponseHeaderFields: (NSDictionary *)headerFields + (NSArray *) cookiesWithResponseHeaderFields: (NSDictionary *)headerFields
@ -833,72 +846,139 @@ GSPropertyListFromCookieFormat(NSString *string, int version)
return AUTORELEASE(dict); return AUTORELEASE(dict);
} }
/* Look for the comma that separates cookies. Commas can also occur in /* Split a string containing comma seprated cookeie settings into an array
date strings, like "expires", but perhaps it can occur other places. * of individual cookie specifications.
For instance, the key/value pair key=value1,value2 is not really * Look for the comma that separates cookies. Commas can also occur in
valid, but should we handle it anyway? Definitely we should handle the * date strings, like "expires", but perhaps it can occur other places.
perfectly normal case of: * For instance, the key/value pair key=value1,value2 is not really
* valid, but should we handle it anyway? Definitely we should handle the
Set-Cookie: domain=test.com; expires=Thu, 12-Sep-2109 14:58:04 GMT; * perfectly normal case of:
session=foo *
Set-Cookie: bar=baz * Set-Cookie: domain=test.com; expires=Thu, 12-Sep-2109 14:58:04 GMT;
* session=foo
which gets concatenated into something like: * Set-Cookie: bar=baz
*
Set-Cookie: domain=test.com; expires=Thu, 12-Sep-2109 14:58:04 GMT; * which gets concatenated into something like:
session=foo,bar=baz *
* Set-Cookie: domain=test.com; expires=Thu, 12-Sep-2109 14:58:04 GMT;
*/ * session=foo,bar=baz
static NSRange
GSRangeOfCookie(NSString *string)
{
pldata _pld;
pldata *pld = &_pld;
NSData *d;
NSRange range;
/*
* An empty string is a nil property list.
*/ */
range = NSMakeRange(NSNotFound, NSNotFound); static NSMutableArray*
GSCookieStrings(NSString *string)
{
unsigned char *ptr;
unsigned pos;
unsigned end;
NSData *d;
NSMutableArray *cookies;
unsigned start = 0;
unsigned saved = 0;
if ([string length] == 0) if ([string length] == 0)
{ {
return range; return nil;
} }
d = [string dataUsingEncoding: NSUTF8StringEncoding]; d = [string dataUsingEncoding: NSUTF8StringEncoding];
NSCAssert(d, @"Couldn't get utf8 data from string."); NSCAssert(d, @"Couldn't get utf8 data from string.");
_pld.ptr = (unsigned char*)[d bytes]; ptr = (unsigned char*)[d bytes];
_pld.pos = 0; pos = 0;
_pld.end = [d length]; end = [d length];
_pld.err = nil;
_pld.lin = 0;
_pld.opt = 0;
_pld.key = NO;
_pld.old = YES; // OpenStep style
while (skipSpace(pld) == YES) cookies = [NSMutableArray arrayWithCapacity: 4];
while (pos < end)
{ {
if (pld->ptr[pld->pos] == ',') while (pos < end && isspace(ptr[pos]))
{
pos++;
}
start = pos;
while (pos < end)
{
if (ptr[pos] == ',')
{ {
/* Look ahead for something that will tell us if this is a /* Look ahead for something that will tell us if this is a
separate cookie or not */ * separate cookie or not. We look for something of the form
unsigned saved_pos = pld->pos; * ' token =' where a space represents any optional whitespace.
while (pld->ptr[pld->pos] != '=' && pld->ptr[pld->pos] != ';' */
&& pld->ptr[pld->pos] != ',' && pld->pos < pld->end ) saved = pos++;
pld->pos++; while (pos < end && isspace(ptr[pos]))
if (pld->ptr[pld->pos] == '=')
{ {
/* Separate comment */ pos++;
range = NSMakeRange(0, saved_pos-1);
break;
} }
pld->pos = saved_pos; if (pos < end)
} {
pld->pos++; const char *bad = "()<>@,;:\\\"/[]?={}";
} int c = ptr[pos];
if (range.location == NSNotFound)
range = NSMakeRange(0, [string length]);
return range; /* skip past token characters.
*/
while (c > 32 && c < 128 && strchr(bad, c) == 0)
{
pos++;
if (pos < end)
{
c = ptr[pos];
}
}
while (pos < end && isspace(ptr[pos]))
{
pos++;
}
if (pos < end && '=' == ptr[pos])
{
pos = saved + 1;
/* We found a comma separator: so make the text before
* the comma a cooke string as long as it is not empty.
*/
while (saved > start
&& isspace(ptr[saved - 1]))
{
saved--;
}
if (saved > start)
{
NSString *str = [NSString alloc];
str = [str initWithBytes: ptr + start
length: saved - start
encoding: NSUTF8StringEncoding];
[cookies addObject: str];
RELEASE(str);
}
start = saved = pos;
}
}
pos = saved;
}
pos++;
}
}
if (pos > start)
{
/* There was data after the last comma; make it into a cookie string
* as long as it's not empty after removing spaces
*/
saved = pos;
while (saved > start
&& isspace(ptr[saved - 1]))
{
saved--;
}
if (saved > start)
{
NSString *str = [NSString alloc];
/* There may not be room to add a nul terminator, so we use an
* initialiser which doesn't need one.
*/
str = [str initWithBytes: ptr + start
length: saved - start
encoding: NSUTF8StringEncoding];
[cookies addObject: str];
RELEASE(str);
}
}
return cookies; // The individual cookies
} }

View file

@ -1100,10 +1100,19 @@ static NSOperationQueue *mainQueue = nil;
|| (pending > 0 && internal->threadCount < POOL)) || (pending > 0 && internal->threadCount < POOL))
{ {
internal->threadCount++; internal->threadCount++;
NS_DURING
{
[NSThread detachNewThreadSelector: @selector(_thread) [NSThread detachNewThreadSelector: @selector(_thread)
toTarget: self toTarget: self
withObject: nil]; withObject: nil];
} }
NS_HANDLER
{
NSLog(@"Failed to create thread for %@: %@",
self, localException);
}
NS_ENDHANDLER
}
/* Tell the thread pool that there is an operation to start. /* Tell the thread pool that there is an operation to start.
*/ */
[internal->cond unlockWithCondition: 1]; [internal->cond unlockWithCondition: 1];

View file

@ -114,12 +114,29 @@ typedef struct {
{ {
GSMimeHeader *h; GSMimeHeader *h;
/* Remove existing headers matching the ones we are setting.
*/
e = [(NSArray*)headers objectEnumerator]; e = [(NSArray*)headers objectEnumerator];
while ((h = [e nextObject]) != nil) while ((h = [e nextObject]) != nil)
{ {
NSString *n = [h namePreservingCase: YES]; NSString *n = [h namePreservingCase: YES];
[this->headers removeObjectForKey: n];
}
/* Set new headers, joining values where we have multiple headers
* with the same name.
*/
e = [(NSArray*)headers objectEnumerator];
while ((h = [e nextObject]) != nil)
{
NSString *n = [h namePreservingCase: YES];
NSString *o = [this->headers objectForKey: n];
NSString *v = [h fullValue]; NSString *v = [h fullValue];
if (nil != o)
{
n = [NSString stringWithFormat: @"%@, %@", o, n];
}
[self _setValue: v forHTTPHeaderField: n]; [self _setValue: v forHTTPHeaderField: n];
} }
} }

View file

@ -39,14 +39,15 @@ int main()
PASS(cookie == nil, "cookie without path returns nil"); PASS(cookie == nil, "cookie without path returns nil");
dict = [NSDictionary dictionaryWithObject: dict = [NSDictionary dictionaryWithObject:
@"S=calendar=R7tjDKqNB5L8YTZSvf29Bg;Expires=Wed, 09-Mar-2011 23:00:35 GMT" @"S=calendar=R7tjDKqNB5L8YTZSvf29Bg;Expires=Wed, 09-Mar-2011 23:00:35 GMT, "
@"S=xxxxxxxx=R7tjDKqNB5L8YTZSvf29Bg;Expires=Thu, 10-Mar-2011 23:00:35 GMT"
forKey: @"Set-Cookie"]; forKey: @"Set-Cookie"];
url = [NSURL URLWithString: @"http://www.google.com/calendar/feeds/default/"]; url = [NSURL URLWithString: @"http://www.google.com/calendar/feeds/default/"];
cookies= [NSHTTPCookie cookiesWithResponseHeaderFields: dict forURL: url]; cookies= [NSHTTPCookie cookiesWithResponseHeaderFields: dict forURL: url];
TEST_FOR_CLASS(@"NSArray", cookies, TEST_FOR_CLASS(@"NSArray", cookies,
"NSHTTPCookie +cookiesWithResponseHeaderFields: returns an NSArray"); "NSHTTPCookie +cookiesWithResponseHeaderFields: returns an NSArray");
PASS([cookies count ] == 1, "cookies array contains a cookie"); PASS([cookies count ] == 2, "cookies array contains two cookies");
cookie = [cookies objectAtIndex: 0]; cookie = [cookies objectAtIndex: 0];
PASS([[cookie name] isEqual: @"S"], "NSHTTPCookie returns proper name"); PASS([[cookie name] isEqual: @"S"], "NSHTTPCookie returns proper name");
PASS([[cookie value] isEqual: @"calendar=R7tjDKqNB5L8YTZSvf29Bg"], PASS([[cookie value] isEqual: @"calendar=R7tjDKqNB5L8YTZSvf29Bg"],
@ -56,6 +57,7 @@ int main()
PASS(![cookie isSecure], "Cookie is not secure"); PASS(![cookie isSecure], "Cookie is not secure");
PASS(![cookie isHTTPOnly], "Cookie is not http only"); PASS(![cookie isHTTPOnly], "Cookie is not http only");
cookies = [cookies subarrayWithRange: NSMakeRange(0, 1)];
dict = [NSHTTPCookie requestHeaderFieldsWithCookies: cookies]; dict = [NSHTTPCookie requestHeaderFieldsWithCookies: cookies];
PASS_EQUAL([dict objectForKey: @"Cookie"], PASS_EQUAL([dict objectForKey: @"Cookie"],
@"S=calendar=R7tjDKqNB5L8YTZSvf29Bg", @"S=calendar=R7tjDKqNB5L8YTZSvf29Bg",

2
configure vendored
View file

@ -2727,6 +2727,7 @@ fi
if test -z "$LIBRARY_COMBO"; then if test -z "$LIBRARY_COMBO"; then
LIBRARY_COMBO=`gnustep-config --variable=LIBRARY_COMBO 2>&5` LIBRARY_COMBO=`gnustep-config --variable=LIBRARY_COMBO 2>&5`
fi fi
OBJC_RUNTIME_LIB=`echo $LIBRARY_COMBO | tr '-' ' ' | awk '{print $1}'`
if test "$OBJC_RUNTIME_LIB" = "ng" -o "$OBJC_RUNTIME_LIB" = "apple"; then if test "$OBJC_RUNTIME_LIB" = "ng" -o "$OBJC_RUNTIME_LIB" = "apple"; then
nonfragile=yes nonfragile=yes
@ -6003,7 +6004,6 @@ esac
#-------------------------------------------------------------------- #--------------------------------------------------------------------
# Set Apple/Darwin/OSX/NeXT information for other tests # Set Apple/Darwin/OSX/NeXT information for other tests
#-------------------------------------------------------------------- #--------------------------------------------------------------------
OBJC_RUNTIME_LIB=`echo $LIBRARY_COMBO | tr '-' ' ' | awk '{print $1}'`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the Objective-C runtime" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking the Objective-C runtime" >&5
$as_echo_n "checking the Objective-C runtime... " >&6; } $as_echo_n "checking the Objective-C runtime... " >&6; }
if test "$OBJC_RUNTIME_LIB" = "nx" -o "$OBJC_RUNTIME_LIB" = "apple"; then if test "$OBJC_RUNTIME_LIB" = "nx" -o "$OBJC_RUNTIME_LIB" = "apple"; then

View file

@ -89,6 +89,7 @@ fi
if test -z "$LIBRARY_COMBO"; then if test -z "$LIBRARY_COMBO"; then
LIBRARY_COMBO=`gnustep-config --variable=LIBRARY_COMBO 2>&5` LIBRARY_COMBO=`gnustep-config --variable=LIBRARY_COMBO 2>&5`
fi fi
OBJC_RUNTIME_LIB=`echo $LIBRARY_COMBO | tr '-' ' ' | awk '{print $1}'`
if test "$OBJC_RUNTIME_LIB" = "ng" -o "$OBJC_RUNTIME_LIB" = "apple"; then if test "$OBJC_RUNTIME_LIB" = "ng" -o "$OBJC_RUNTIME_LIB" = "apple"; then
nonfragile=yes nonfragile=yes
@ -1254,7 +1255,6 @@ esac
#-------------------------------------------------------------------- #--------------------------------------------------------------------
# Set Apple/Darwin/OSX/NeXT information for other tests # Set Apple/Darwin/OSX/NeXT information for other tests
#-------------------------------------------------------------------- #--------------------------------------------------------------------
OBJC_RUNTIME_LIB=`echo $LIBRARY_COMBO | tr '-' ' ' | awk '{print $1}'`
AC_MSG_CHECKING(the Objective-C runtime) AC_MSG_CHECKING(the Objective-C runtime)
if test "$OBJC_RUNTIME_LIB" = "nx" -o "$OBJC_RUNTIME_LIB" = "apple"; then if test "$OBJC_RUNTIME_LIB" = "nx" -o "$OBJC_RUNTIME_LIB" = "apple"; then
AC_MSG_RESULT(NeXT) AC_MSG_RESULT(NeXT)