]> err.no Git - sope/blob - sope-appserver/NGObjWeb/NGHttp/NGHttpHeaderFieldParser.m
increased element nesting depth
[sope] / sope-appserver / NGObjWeb / NGHttp / NGHttpHeaderFieldParser.m
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
27 static 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   NGHttpHostHeaderField *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 = [_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: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 = [_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: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 = [_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 ((*src != '\0') && isRfc822_LWSP(*src) && (len > 0)) {
194       src++;
195       len--;
196     }
197     if (len <= 0)
198       break;
199     else
200       startPos = src;
201     
202     while ((*src != self->splitChar) && !isRfc822_LWSP(*src) && (len > 0)) {
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: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 ((*src != '\0') && (len > 0));
239
240   return array;
241 }
242
243 @end /* NGHttpStringArrayHeaderFieldParser */
244
245 @implementation NGHttpCharsetHeaderFieldParser
246
247 - (id)parseValue:(NSData *)_data ofHeaderField:(NSString *)_field {
248   id value = nil;
249
250   value = [super parseValue:_data ofHeaderField:_field];
251   if (value) {
252     if (NSArrayClass == Nil)
253       NSArrayClass = [NSArray class];
254     
255     NSAssert([value isKindOfClass:NSArrayClass], @"invalid value ..");
256     
257     value = [[NGHttpCharsetHeaderField alloc] initWithArray:value];
258     value = AUTORELEASE(value);
259   }
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
463 static NGMimeHeaderFieldParserSet *httpSet = nil;
464
465 static 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) */