1/* 2 Copyright (C) 2000-2005 SKYRIX Software AG 3 4 This file is part of SOPE. 5 6 SOPE is free software; you can redistribute it and/or modify it under 7 the terms of the GNU Lesser General Public License as published by the 8 Free Software Foundation; either version 2, or (at your option) any 9 later version. 10 11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 14 License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with SOPE; see the file COPYING. If not, write to the 18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 19 02111-1307, USA. 20*/ 21 22#import "common.h" 23#import "NGHttpHeaderFieldParser.h" 24#import "NGHttpHeaderFields.h" 25#import "NGHttpCookie.h" 26 27static Class NSArrayClass = Nil; 28 29@implementation NGHttpStringHeaderFieldParser 30 31- (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { 32 unsigned len = 0; 33 const unsigned char *src = NULL; 34 id value = nil; 35 NSString *str = nil; 36 37 if ([_data isKindOfClass:[NSData class]]) { 38 len = [_data length]; 39 src = (unsigned char *)[_data bytes]; 40 } 41 else { 42 len = [_data cStringLength]; 43 src = (const unsigned char *)[_data cString]; 44 } 45 if (len == 0) { 46#if DEBUG 47 NSLog(@"WARNING: empty value for header field %@ ..", _field); 48#endif 49 return nil; 50 } 51 52 // strip leading spaces 53 while (isRfc822_LWSP(*src) && (len > 0)) { 54 src++; 55 len--; 56 } 57 58 str = [[NSString alloc] initWithCString:(char *)src length:len]; 59 NSAssert(str, @"string allocation failed .."); 60 61 if ([_field isEqualToString:@"host"]) 62 value = [[NGHttpHostHeaderField alloc] initWithString:str]; 63 else if ([_field isEqualToString:@"user-agent"]) 64 value = [[NGHttpUserAgent alloc] initWithString:str]; 65 else if ([_field isEqualToString:@"connection"]) 66 value = [[NGHttpConnectionHeaderField alloc] initWithString:str]; 67 else 68 value = RETAIN(str); 69 70 RELEASE(str); str = nil; 71 72 return AUTORELEASE(value); 73} 74 75@end /* NGHttpStringHeaderFieldParser */ 76 77@implementation NGHttpCredentialsFieldParser 78 79- (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { 80 unsigned len = 0; 81 const unsigned char *src = NULL; 82 NSString *scheme = nil; 83 NSData *data = nil; 84 85 if ([_data isKindOfClass:[NSData class]]) { 86 len = [_data length]; 87 src = (unsigned char *)[_data bytes]; 88 } 89 else { 90 len = [_data cStringLength]; 91 src = (const unsigned char *)[_data cString]; 92 } 93 94 if (len == 0) { 95 NSLog(@"WARNING: empty value for header field %@ ..", _field); 96 return nil; 97 } 98 99 // strip leading spaces 100 while (isRfc822_LWSP(*src) && (len > 0)) { 101 src++; 102 len--; 103 } 104 if (len == 0) { 105 NSLog(@"WARNING: empty value for header field %@ ..", _field); 106 return nil; 107 } 108 109 // find name 110 { 111 const unsigned char *start = src; 112 113 while (!isRfc822_LWSP(*src) && (len > 0)) { 114 src++; 115 len--; 116 } 117 scheme = [NSString stringWithCString:(char *)start length:(src - start)]; 118 } 119 120 // skip spaces 121 while (isRfc822_LWSP(*src) && (len > 0)) { 122 src++; 123 len--; 124 } 125 if (len == 0) { 126 NSLog(@"WARNING: invalid credentials header field %@ .. (missing credentials)", 127 _field); 128 return nil; 129 } 130 131 // make credentials 132 data = [NSData dataWithBytes:src length:len]; 133 134 return [NGHttpCredentials credentialsWithScheme:[scheme lowercaseString] 135 credentials:data]; 136} 137 138@end /* NGHttpCredentialsFieldParser */ 139 140@implementation NGHttpStringArrayHeaderFieldParser 141 142- (id)initWithSplitChar:(unsigned char)_c { 143 if ((self = [super init])) { 144 self->splitChar = _c; 145 } 146 return self; 147} 148- (id)init { 149 return [self initWithSplitChar:',']; 150} 151 152- (id)parseValuePart:(const char *)_b length:(unsigned)_len 153 zone:(NSZone *)_zone 154{ 155 return [[NSString allocWithZone:_zone] 156 initWithCString:_b length:_len]; 157} 158 159- (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { 160 unsigned len = 0; 161 const unsigned char *src = NULL; 162 NSMutableArray *array = nil; 163 164 if ([_data isKindOfClass:[NSData class]]) { 165 len = [_data length]; 166 src = (unsigned char *)[_data bytes]; 167 } 168 else { 169 len = [_data cStringLength]; 170 src = (const unsigned char *)[_data cString]; 171 } 172 173 if (len == 0) { 174#if DEBUG 175 NSLog(@"WARNING: empty value for header field %@ ..", _field); 176#endif 177 return nil; 178 } 179 180#if 0 && DEBUG 181 NSLog(@"field %@ is %@", 182 _field, 183 [[NSString alloc] initWithData:_data encoding:NSASCIIStringEncoding]); 184#endif 185 186 187 array = [NSMutableArray arrayWithCapacity:16]; 188 NSAssert(array, @"array allocation failed .."); 189 do { 190 const unsigned char *startPos = NULL; 191 192 // strip leading spaces 193 while ((len > 0) && (*src != '\0') && isRfc822_LWSP(*src)) { 194 src++; 195 len--; 196 } 197 if (len <= 0) 198 break; 199 else 200 startPos = src; 201 202 while ((len > 0) && (*src != self->splitChar) && !isRfc822_LWSP(*src)) { 203 src++; 204 len--; 205 } 206 207 if (src > startPos) { 208 id part = nil; 209 unsigned partLen; 210 211 partLen = (src - startPos); 212#if DEBUG && 0 213 NSLog(@"field %@: current len=%i %s(%i)", _field, len, startPos, partLen); 214#endif 215 216 part = [self parseValuePart:(const char *)startPos 217 length:partLen 218 zone:[array zone]]; 219 if (part) { 220 [array addObject:part]; 221 RELEASE(part); part = nil; 222 } 223 } 224 225 if (len > 0) { 226 if (isRfc822_LWSP(*src)) { // skip until splitchar or to len .. 227 while ((*src != '\0') && (*src != self->splitChar) && (len > 0)) { 228 src++; 229 len--; 230 } 231 } 232 else if (*src == self->splitChar) { // skip ',' 233 src++; 234 len--; 235 } 236 } 237 } 238 while ((len > 0) && (*src != '\0')); 239 240 return array; 241} 242 243@end /* NGHttpStringArrayHeaderFieldParser */ 244 245@implementation NGHttpCharsetHeaderFieldParser 246 247- (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { 248 id value = nil; 249 250 if ((value = [super parseValue:_data ofHeaderField:_field]) == nil) 251 return nil; 252 253 if (NSArrayClass == Nil) 254 NSArrayClass = [NSArray class]; 255 256 NSAssert([value isKindOfClass:NSArrayClass], @"invalid value .."); 257 258 value = [[NGHttpCharsetHeaderField alloc] initWithArray:value]; 259 value = [value autorelease]; 260 return value; 261} 262 263@end /* NGHttpCharsetHeaderFieldParser */ 264 265@implementation NGHttpTypeArrayHeaderFieldParser 266 267- (id)parseValuePart:(const char *)_b length:(unsigned)_len zone:(NSZone *)_zone { 268 NSString *typeString = [[NSString alloc] initWithCString:_b length:_len]; 269 NGMimeType *type = nil; 270 271 type = typeString ? [NGMimeType mimeType:typeString] : nil; 272 RELEASE(typeString); 273 274 return RETAIN(type); 275} 276 277- (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { 278 id value = nil; 279 280 value = [super parseValue:_data ofHeaderField:_field]; 281 if (value) { 282 if (NSArrayClass == Nil) 283 NSArrayClass = [NSArray class]; 284 285 NSAssert([value isKindOfClass:NSArrayClass], @"invalid value .."); 286 287 value = [[NGHttpTypeSetHeaderField alloc] initWithArray:value]; 288 value = AUTORELEASE(value); 289 } 290 return value; 291} 292 293@end /* NGHttpTypeArrayHeaderFieldParser */ 294 295@implementation NGHttpLanguageArrayHeaderFieldParser 296 297- (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { 298 id value = nil; 299 300 value = [super parseValue:_data ofHeaderField:_field]; 301 if (value) { 302 if (NSArrayClass == Nil) 303 NSArrayClass = [NSArray class]; 304 305 NSAssert([value isKindOfClass:NSArrayClass], @"invalid value .."); 306 307 value = [[NGHttpLanguageSetHeaderField alloc] initWithArray:value]; 308 value = AUTORELEASE(value); 309 } 310 return value; 311} 312 313@end /* NGHttpLanguageArrayHeaderFieldParser */ 314 315@implementation NGHttpCookieFieldParser 316 317- (id)init { 318 return [self initWithSplitChar:';']; 319} 320- (id)initWithSplitChar:(unsigned char)_splitChar { 321 if ((self = [super initWithSplitChar:_splitChar])) { 322 self->fetchedCookies = NSCreateMapTable(NSObjectMapKeyCallBacks, 323 NSObjectMapValueCallBacks, 324 16); 325 self->isRunning = NO; 326 self->foundInvalidPairs = NO; 327 } 328 return self; 329} 330 331#if !LIB_FOUNDATION_BOEHM_GC 332- (void)dealloc { 333 if (self->fetchedCookies) { 334 NSFreeMapTable(self->fetchedCookies); 335 self->fetchedCookies = NULL; 336 } 337 [super dealloc]; 338} 339#endif 340 341- (id)parseValuePart:(const char *)_bytes length:(unsigned)_len 342 zone:(NSZone *)_z 343{ 344 NGHttpCookie *cookie = nil; 345 unsigned pos, toGo; 346 347 for (pos = 0, toGo = _len; (toGo > 0) && (_bytes[pos] != '='); toGo--, pos++) 348 ; 349 350 if (toGo > 0) { 351 NSString *name = nil; 352 NSString *value = nil; 353 354 // NSLog(@"pos=%i toGo=%i", pos, toGo); 355 356 name = [[NSString allocWithZone:_z] 357 initWithCString:_bytes 358 length:pos]; 359 value = [[NSString allocWithZone:_z] 360 initWithCString:&(_bytes[pos + 1]) 361 length:(toGo - 1)]; 362 363 //NSLog(@"pair='%@'", [NSString stringWithCString:_bytes length:_len]); 364 //NSLog(@"name='%@' value='%@'", name, value); 365 366 if (name == nil) { 367 NSLog(@"ERROR: invalid cookie pair missing name: %@", 368 [NSString stringWithCString:_bytes length:_len]); 369 RELEASE(name); 370 RELEASE(value); 371 return nil; 372 } 373 else if (value == nil) { 374 NSLog(@"ERROR: invalid cookie pair missing value (name=%@): %@", 375 name, 376 [NSString stringWithCString:_bytes length:_len]); 377 RELEASE(name); 378 RELEASE(value); 379 return nil; 380 } 381 else { 382 cookie = (id)NSMapGet(self->fetchedCookies, name); 383 384 if (cookie) { 385 [cookie addAdditionalValue:[value stringByUnescapingURL]]; 386 cookie = nil; 387 } 388 else { 389 cookie = [[NGHttpCookie allocWithZone:_z] 390 initWithName:[name stringByUnescapingURL] 391 value:[value stringByUnescapingURL]]; 392 NSMapInsert(self->fetchedCookies, name, cookie); 393 } 394 } 395 396 RELEASE(name); name = nil; 397 RELEASE(value); value = nil; 398 } 399#if DEBUG 400 else { 401 NSLog(@"ERROR(%s:%i): invalid cookie pair: %@", 402 __PRETTY_FUNCTION__, __LINE__, 403 [NSString stringWithCString:_bytes length:_len]); 404 self->foundInvalidPairs = YES; 405 } 406#endif 407 return cookie; 408} 409 410- (id)parseValue:(id)_data ofHeaderField:(NSString *)_field { 411 id value = nil; 412 413 if (NSArrayClass == Nil) 414 NSArrayClass = [NSArray class]; 415 416 NSAssert(self->isRunning == NO, @"parser used in multiple threads !"); 417 self->foundInvalidPairs = NO; 418 419#if 0 && DEBUG 420 NSLog(@"cookie: field %@ is %@", 421 _field, 422 [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding]); 423#endif 424 425 self->isRunning = YES; // semi-lock 426 { 427 value = [super parseValue:_data ofHeaderField:_field]; 428 NSResetMapTable(self->fetchedCookies); 429 } 430 self->isRunning = NO; // semi-unlock 431 432 if (self->foundInvalidPairs) { 433#if DEBUG 434 NSString *s; 435 436 if ([_data isKindOfClass:[NSData class]]) 437 s = [[NSString alloc] initWithData:_data encoding:NSASCIIStringEncoding]; 438 else 439 s = _data; 440 441 NSLog(@"ERROR(%s:%i): got invalid cookie pairs for field %@ data %@.", 442 __PRETTY_FUNCTION__, __LINE__, 443 _field, s); 444 RELEASE(s); 445#endif 446 // return nil; 447 } 448 449 if (value) { 450 NSAssert1([value isKindOfClass:NSArrayClass], 451 @"invalid value '%@' ..", value); 452 453 //value = [[NGHttpTypeSetHeaderField alloc] initWithArray:value]; 454 //AUTORELEASE(value); 455 } 456 return value; 457} 458 459@end /* NGHttpCookieFieldParser */ 460 461@implementation NGMimeHeaderFieldParserSet(HttpFieldParserSet) 462 463static NGMimeHeaderFieldParserSet *httpSet = nil; 464 465static inline void NGRegisterParser(NSString *_field, NSString *_parserClass) { 466 id parser = [[NSClassFromString(_parserClass) alloc] init]; 467 468 if (parser) { 469 [httpSet setParser:parser forField:_field]; 470 RELEASE(parser); 471 parser = nil; 472 } 473 else { 474 NSLog(@"WARNING: did not find header field parser %@", _parserClass); 475 } 476} 477 478+ (id)defaultHttpHeaderFieldParserSet { 479 if (httpSet == nil) { 480 id parser = nil; 481 482 httpSet = [[self alloc] initWithParseSet: 483 [NGMimeHeaderFieldParserSet defaultRfc822HeaderFieldParserSet]]; 484 485 parser = [[NGHttpStringHeaderFieldParser alloc] init]; 486 [httpSet setParser:parser forField:@"host"]; 487 [httpSet setParser:parser forField:@"user-agent"]; 488 [httpSet setParser:parser forField:@"connection"]; 489 RELEASE(parser); parser = nil; 490 491 NGRegisterParser(@"accept-charset", @"NGHttpCharsetHeaderFieldParser"); 492 NGRegisterParser(@"accept-language", @"NGHttpLanguageArrayHeaderFieldParser"); 493 NGRegisterParser(@"accept", @"NGHttpTypeArrayHeaderFieldParser"); 494 NGRegisterParser(@"accept-encoding", @"NGHttpStringArrayHeaderFieldParser"); 495 NGRegisterParser(@"cookie", @"NGHttpCookieFieldParser"); 496 NGRegisterParser(@"authorization", @"NGHttpCredentialsFieldParser"); 497 } 498 return httpSet; 499} 500 501@end /* NGMimeHeaderFieldParserSet(HttpFieldParserSet) */ 502