diff --git a/ChangeLog b/ChangeLog index 63a4c03b9..5141b9fa5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ * Headers/Foundation/NSURLHandle.h: Added some more property keys. * Source/GSMime.m: Update to add method for general decoding of different transfer encoding types including chunked (for http1.1). + Recognise end of data in chunked encoding or by content length. * Source/GSHTTPURLHandle.m: Added new class for http and https support. * Source/NSURLHandle.m: Register class for http and https support. * Source/externs.m: Added property keys for URL handles. diff --git a/Headers/gnustep/base/GSMime.h b/Headers/gnustep/base/GSMime.h index 4871a6894..6a2f944cc 100644 --- a/Headers/gnustep/base/GSMime.h +++ b/Headers/gnustep/base/GSMime.h @@ -80,6 +80,8 @@ unsigned lineStart; unsigned lineEnd; unsigned input; + unsigned expect; + unsigned rawBodyLength; BOOL inBody; BOOL complete; NSData *boundary; diff --git a/Source/GSHTTPURLHandle.m b/Source/GSHTTPURLHandle.m index d07b5fcc8..f8f45f49d 100644 --- a/Source/GSHTTPURLHandle.m +++ b/Source/GSHTTPURLHandle.m @@ -38,7 +38,7 @@ #include #include -static NSString *httpVersion = @"1.0"; +static NSString *httpVersion = @"1.1"; char emp[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M', @@ -162,7 +162,7 @@ static NSLock *urlLock = nil; d = [dict objectForKey: NSFileHandleNotificationDataItem]; [parser parse: d]; - if ([d length] == 0) + if ([parser isComplete] == YES) { NSDictionary *info; NSString *val; @@ -215,6 +215,16 @@ static NSLock *urlLock = nil; str = [str initWithData: dat encoding: NSASCIIStringEncoding]; range = [str rangeOfString: @"\n\n" options: NSCaseInsensitiveSearch]; + if (range.length == 0) + { + range = [str rangeOfString: @"\r\n\r\n" + options: NSCaseInsensitiveSearch]; + if (range.length == 0) + { + range = [str rangeOfString: @"\r\r" + options: NSCaseInsensitiveSearch]; + } + } if ([d length] == 0 || range.length > 0) { [nc removeObserver: self @@ -375,12 +385,12 @@ static NSLock *urlLock = nil; if ([url port] == nil) { - cmd = [NSString stringWithFormat: @"CONNECT %@:443 HTTP/%@\n\n", + cmd = [NSString stringWithFormat: @"CONNECT %@:443 HTTP/%@\r\n\r\n", [url host], httpVersion]; } else { - cmd = [NSString stringWithFormat: @"CONNECT %@:%@ HTTP/%@\n\n", + cmd = [NSString stringWithFormat: @"CONNECT %@:%@ HTTP/%@\r\n\r\n", [url host], [url port], httpVersion]; } @@ -409,30 +419,35 @@ static NSLock *urlLock = nil; return; } } + + /* + * Set up request - differs for proxy version unless tunneling via ssl. + */ if ([request objectForKey: GSHTTPPropertyMethodKey] == nil) { [request setObject: @"GET" forKey: GSHTTPPropertyMethodKey]; } - if ([request objectForKey: GSHTTPPropertyProxyHostKey] != nil) + if ([request objectForKey: GSHTTPPropertyProxyHostKey] != nil + && [[url scheme] isEqualToString: @"https"] == NO) { - s = [[NSMutableString alloc] initWithFormat: @"\n%@ http://%@%@", + s = [[NSMutableString alloc] initWithFormat: @"%@ http://%@%@", [request objectForKey: GSHTTPPropertyMethodKey], [url host], [url path]]; if ([[url query] length] > 0) { [s appendFormat: @"?%@", [url query]]; } - [s appendFormat: @" HTTP/%@\n", httpVersion]; + [s appendFormat: @" HTTP/%@\r\n", httpVersion]; } else // no proxy { - s = [[NSMutableString alloc] initWithFormat: @"\n%@ %@", + s = [[NSMutableString alloc] initWithFormat: @"%@ %@", [request objectForKey: GSHTTPPropertyMethodKey], [url path]]; if ([[url query] length] > 0) { [s appendFormat: @"?%@", [url query]]; } - [s appendFormat: @" HTTP/%@\nHost: %@\n", [url host], httpVersion]; + [s appendFormat: @" HTTP/%@\nHost: %@\r\n", httpVersion, [url host]]; } while ((key = [wpEnumerator nextObject])) diff --git a/Source/GSMime.m b/Source/GSMime.m index 67a7f5a07..1f7107422 100644 --- a/Source/GSMime.m +++ b/Source/GSMime.m @@ -794,7 +794,6 @@ parseCharacterSet(NSString *token) { data = [[NSMutableData alloc] init]; document = [[GSMimeDocument alloc] init]; - context = [[GSMimeCodingContext alloc] init]; } return self; } @@ -1507,7 +1506,53 @@ parseCharacterSet(NSString *token) - (BOOL) _decodeBody: (NSData*)d { - if (boundary == nil) + BOOL result = NO; + + rawBodyLength += [d length]; + + if (context == nil) + { + NSDictionary *hdr; + + /* + * Check for expected content length. + */ + hdr = [document headerNamed: @"content-length"]; + if (hdr != nil) + { + expect = [[hdr objectForKey: @"BaseValue"] intValue]; + } + + /* + * Set up context for decoding data. + */ + hdr = [document headerNamed: @"transfer-encoding"]; + if (hdr == nil) + { + hdr = [document headerNamed: @"content-transfer-encoding"]; + } + else if ([[hdr objectForKey: @"Value"] isEqual: @"chunked"] == YES) + { + /* + * Chunked transfer encoding overrides any content length spec. + */ + expect = 0; + } + context = [self contextFor: hdr]; + RETAIN(context); + } + + if ([context atEnd] == YES) + { + inBody = NO; + complete = YES; + if ([d length] > 0) + { + NSLog(@"Additional data ignored after parse complete"); + } + result = YES; /* Nothing more to do */ + } + else if (boundary == nil) { NSDictionary *typeInfo; NSString *type; @@ -1517,25 +1562,19 @@ parseCharacterSet(NSString *token) if ([type isEqualToString: @"multipart"] == YES) { NSLog(@"multipart decode attempt without boundary"); - return NO; + inBody = NO; + complete = YES; + result = NO; } else { - if ([context atEnd] == YES) - { - if ([d length] > 0) - { - NSLog(@"Additional data ignored after parse complete"); - } - return YES; /* Nothing more to do */ - } - [self decodeData: d fromRange: NSMakeRange(0, [d length]) intoData: data withContext: context]; - if ([context atEnd] == YES) + if ([context atEnd] == YES + || (expect > 0 && rawBodyLength >= expect)) { inBody = NO; complete = YES; @@ -1570,7 +1609,7 @@ parseCharacterSet(NSString *token) [document setContent: AUTORELEASE([data copy])]; } } - return YES; + result = YES; } } else @@ -1691,9 +1730,17 @@ parseCharacterSet(NSString *token) sectionStart = 0; } } - return YES; + /* + * Check to see if we have reached content length. + */ + if (expect > 0 && rawBodyLength >= expect) + { + complete = YES; + inBody = NO; + } + result = YES; } - return NO; + return result; } - (BOOL) _unfoldHeader @@ -1738,7 +1785,6 @@ parseCharacterSet(NSString *token) if (lineEnd == lineStart) { unsigned lengthRemaining; - NSDictionary *hdr; /* * Overwrite the header data with the body, ready to start @@ -1756,19 +1802,7 @@ parseCharacterSet(NSString *token) lineStart = 0; lineEnd = 0; input = 0; - - /* - * At end of headers - set up context for decoding data. - */ inBody = YES; - DESTROY(context); - hdr = [document headerNamed: @"content-transfer-encoding"]; - if (hdr == nil) - { - hdr = [document headerNamed: @"transfer-encoding"]; - } - context = [self contextFor: hdr]; - RETAIN(context); } } }