2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo 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
11 OGo 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.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include <NGObjWeb/WOMessage.h>
23 #include <NGExtensions/NGHashMap.h>
24 #include <NGExtensions/NSString+misc.h>
27 // #define STRIP_MULTIPLE_SPACES // this doesn't work with <pre> tags !
29 @implementation WOMessage
31 typedef struct _WOMessageProfileInfo {
37 } WOMessageProfileInfo;
39 static Class NSStringClass = Nil;
40 static BOOL printProfile = NO;
41 static int DEF_CONTENT_SIZE = 20000;
42 static NSStringEncoding defaultEncoding = 0;
44 static WOMessageProfileInfo profile = { 0, 0, 0, 0, 0 };
45 static WOMessageProfileInfo profilemax = { 0, 0, 0, 0, 0 };
46 static WOMessageProfileInfo profiletot = { 0, 0, 0, 0, 0 };
49 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
51 if (NSStringClass == Nil)
52 NSStringClass = [NSString class];
54 printProfile = [ud boolForKey:@"WOProfileResponse"];
57 //#warning default encoding is ISO Latin 1 ...
58 defaultEncoding = NSISOLatin1StringEncoding;
60 defaultEncoding = [NSStringClass defaultCStringEncoding];
64 static inline void _ensureBody(WOMessage *self) {
65 if (self->content == nil) {
66 self->content = [[NSMutableData alloc] initWithCapacity:DEF_CONTENT_SIZE];
67 self->addBytes = (void *)
68 [self->content methodForSelector:@selector(appendBytes:length:)];
72 static __inline__ NSMutableData *_checkBody(WOMessage *self) {
73 if (self->content == nil) {
74 self->content = [[NSMutableData alloc] initWithCapacity:DEF_CONTENT_SIZE];
75 self->addBytes = (void *)
76 [self->content methodForSelector:@selector(appendBytes:length:)];
85 + (void)setDefaultEncoding:(NSStringEncoding)_encoding {
86 defaultEncoding = _encoding;
88 + (NSStringEncoding)defaultEncoding {
89 return defaultEncoding;
93 if ((self = [super init])) {
94 self->contentEncoding = [[self class] defaultEncoding];
96 self->addChar = (void*)
97 [self methodForSelector:@selector(appendContentCharacter:)];
98 self->addStr = (void *)
99 [self methodForSelector:@selector(appendContentString:)];
100 self->addHStr = (void *)
101 [self methodForSelector:@selector(appendContentHTMLString:)];
102 self->addCStr = (void *)
103 [self methodForSelector:@selector(appendContentCString:)];
105 self->header = [[NGMutableHashMap allocWithZone:[self zone]] init];
106 self->version = @"HTTP/1.0";
112 [self->domCache release];
113 [self->contentStream release];
114 [self->cookies release];
115 [self->version release];
116 [self->content release];
117 [self->header release];
118 [self->userInfo release];
124 - (void)setUserInfo:(NSDictionary *)_userInfo {
125 ASSIGN(self->userInfo, _userInfo);
127 - (NSDictionary *)userInfo {
128 return self->userInfo;
133 - (void)setHTTPVersion:(NSString *)_httpVersion {
135 if (self->version == _httpVersion)
138 self->version = [_httpVersion copy];
141 if (self->version != nil && ![_httpVersion hasPrefix:@"HTTP/"]) {
143 @"WARNING: you apparently passed in an invalid HTTP version: '%@'",
147 - (void)setHttpVersion:(NSString *)_httpVersion {
149 [self setHTTPVersion:_httpVersion];
151 - (NSString *)httpVersion {
152 return self->version;
155 /* cookies (new in WO4) */
157 - (void)addCookie:(WOCookie *)_cookie {
158 if (self->cookies == nil)
159 self->cookies = [[NSMutableArray allocWithZone:[self zone]] init];
160 [self->cookies addObject:_cookie];
163 - (void)removeCookie:(WOCookie *)_cookie {
164 [self->cookies removeObject:_cookie];
167 - (NSArray *)cookies {
168 return self->cookies;
173 - (void)setHeaders:(NSDictionary *)_headers {
177 keys = [_headers keyEnumerator];
178 while ((key = [keys nextObject])) {
181 value = [_headers objectForKey:key];
182 if ([value isKindOfClass:[NSArray class]]) {
183 NSEnumerator *e = [value objectEnumerator];
185 while ((value = [e nextObject]))
186 [self appendHeader:value forKey:key];
189 [self appendHeader:value forKey:key];
193 - (void)setHeader:(NSString *)_header forKey:(NSString *)_key {
194 [self->header setObject:[_header stringValue] forKey:_key];
196 - (NSString *)headerForKey:(NSString *)_key {
197 return [[self->header objectEnumeratorForKey:_key] nextObject];
200 - (void)appendHeader:(NSString *)_header forKey:(NSString *)_key {
201 [self->header addObject:_header forKey:_key];
203 - (void)appendHeaders:(NSArray *)_headers forKey:(NSString *)_key {
204 [self->header addObjects:_headers forKey:_key];
207 - (void)setHeaders:(NSArray *)_headers forKey:(NSString *)_key {
211 e = [_headers objectEnumerator];
213 [self->header removeAllObjectsForKey:_key];
215 while ((value = [e nextObject]))
216 [self->header addObject:value forKey:_key];
218 - (NSArray *)headersForKey:(NSString *)_key {
219 NSEnumerator *values;
221 if ((values = [self->header objectEnumeratorForKey:_key])) {
222 NSMutableArray *array = nil;
225 array = [[NSMutableArray allocWithZone:[self zone]] init];
227 while ((value = [values nextObject]))
228 [array addObject:value];
230 return [array autorelease];
235 - (NSArray *)headerKeys {
236 NSEnumerator *values;
238 if ((values = [self->header keyEnumerator])) {
239 NSMutableArray *array = nil;
241 array = [[NSMutableArray alloc] init];
243 while ((name = [values nextObject]))
244 [array addObject:name];
249 return [name autorelease];
254 - (NSDictionary *)headers {
255 return [self->header asDictionary];
258 - (NSString *)headersAsString {
263 ms = [NSMutableString stringWithCapacity:1024];
266 keys = [[self headerKeys] objectEnumerator];
267 while ((key = [keys nextObject])) {
271 vals = [[self headersForKey:key] objectEnumerator];
272 while ((val = [vals nextObject])) {
273 [ms appendString:key];
274 [ms appendString:@": "];
275 [ms appendString:[val stringValue]];
276 [ms appendString:@"\r\n"];
284 - (void)_printProfile {
285 if (profile.append + profile.appendC + profile.appendChr +
286 profile.appendXML + profile.appendHTML == 0)
290 if (profile.append > profilemax.append)
291 profilemax.append = profile.append;
292 if (profile.appendC > profilemax.appendC)
293 profilemax.appendC = profile.appendC;
294 if (profile.appendHTML > profilemax.appendHTML)
295 profilemax.appendHTML = profile.appendHTML;
298 profiletot.append += profile.append;
299 profiletot.appendC += profile.appendC;
300 profiletot.appendChr += profile.appendChr;
301 profiletot.appendHTML += profile.appendHTML;
302 profiletot.appendXML += profile.appendXML;
306 [self logWithFormat:@"PROFILE: WOResponse:\n"
307 @" appendContentString: %8i max %8i total %8i\n"
308 @" appendContentCString: %8i max %8i total %8i\n"
309 @" appendContentCharacter: %8i max %8i total %8i\n"
310 @" appendContentXMLString: %8i max %8i total %8i\n"
311 @" appendContentHTMLString: %8i max %8i total %8i\n",
312 profile.append, profilemax.append, profiletot.append,
313 profile.appendC, profilemax.appendC, profiletot.appendC,
314 profile.appendChr, profilemax.appendChr, profiletot.appendChr,
315 profile.appendXML, profilemax.appendXML, profiletot.appendXML,
316 profile.appendHTML, profilemax.appendHTML, profiletot.appendHTML];
319 memset(&profile, 0, sizeof(profile));
322 /* generic content */
324 - (void)setContent:(NSData *)_data {
327 if (self->content == (id)_data)
331 self->content = [_data mutableCopy];
334 - (NSData *)content {
335 if (printProfile) [self _printProfile];
336 return self->content;
338 - (NSString *)contentAsString {
342 if ((c = [self content]) == nil)
345 s = [[NSString alloc] initWithData:c encoding:[self contentEncoding]];
346 return [s autorelease];
348 - (BOOL)doesStreamContent {
349 return self->contentStream != nil ? YES : NO;
352 - (void)setContentEncoding:(NSStringEncoding)_encoding {
353 self->contentEncoding = _encoding;
355 - (NSStringEncoding)contentEncoding {
356 return self->contentEncoding;
359 /* structured content */
361 - (void)appendContentBytes:(const void *)_bytes length:(unsigned)_l {
362 if (_bytes == NULL || _l == 0) return;
363 if (self->content == nil) _ensureBody(self);
364 self->addBytes(self->content, @selector(appendBytes:length:), _bytes, _l);
367 - (void)appendContentCharacter:(unichar)_c {
368 unsigned char bc[2] = {0, 0};
373 if (self->content == nil) _ensureBody(self);
375 switch (self->contentEncoding) {
376 case NSISOLatin1StringEncoding:
377 case NSASCIIStringEncoding:
378 /* those two encodings are == Unicode ... */
379 self->addBytes(self->content, @selector(appendBytes:length:), &(bc[0]), 1);
382 case NSUnicodeStringEncoding:
383 /* directly add 16-byte char ... */
384 self->addBytes(self->content, @selector(appendBytes:length:),
388 case NSUTF8StringEncoding:
389 /* directly add a byte if 1-byte char (<127 in UTF-8) */
391 self->addBytes(self->content, @selector(appendBytes:length:), &(bc[0]), 1);
394 /* *intended* fall-through !!! */
397 /* otherwise create string for char and ask string to convert to data */
402 @"WARNING: using NSString to add a character %i,0x%08X"
403 @"(slow, encoding=%i).", _c, _c, self->contentEncoding];
406 if ((s = [[NSStringClass alloc] initWithCharacters:&_c length:1])) {
407 self->addStr(self, @selector(appendContentString:), s);
414 - (void)appendContentData:(NSData *)_data {
415 if (_data == nil) return;
416 [_checkBody(self) appendData:_data];
419 - (void)appendContentHTMLAttributeValue:(NSString *)_value {
420 self->addStr(self, @selector(appendContentString:),
421 [_value stringByEscapingHTMLAttributeValue]);
422 profile.appendHTML++;
424 - (void)appendContentHTMLString:(NSString *)_value {
425 self->addStr(self, @selector(appendContentString:),
426 [_value stringByEscapingHTMLString]);
427 profile.appendHTML++;
430 - (void)appendContentXMLAttributeValue:(NSString *)_value {
431 self->addStr(self, @selector(appendContentString:),
432 [_value stringByEscapingXMLAttributeValue]);
435 - (void)appendContentXMLString:(NSString *)_value {
436 if (_value == nil) return;
437 self->addStr(self, @selector(appendContentString:),
438 [_value stringByEscapingXMLString]);
442 - (void)appendContentCString:(const unsigned char *)_value {
443 /* we assume that cString == ASCII !!! */
444 register unsigned len;
448 if (self->content == nil) _ensureBody(self);
449 if ((len = _value ? strlen(_value) : 0) == 0)
452 switch (self->contentEncoding) {
453 case NSISOLatin1StringEncoding:
454 case NSASCIIStringEncoding:
455 case NSUTF8StringEncoding:
456 self->addBytes(self->content, @selector(appendBytes:length:),
460 case NSUnicodeStringEncoding:
465 if ((s = [[NSString alloc] initWithCString:_value])) {
466 self->addStr(self, @selector(appendContentString:), s);
473 - (void)appendContentString:(NSString *)_value {
478 if ([_value length] == 0)
481 cdata = [_value dataUsingEncoding:self->contentEncoding
482 allowLossyConversion:NO];
484 if ([_value length] > 9000) {
489 cstr = [cdata bytes];
490 len = [cdata length];
492 cstr = [_value cString];
493 len = [_value cStringLength];
496 printf("\n\n*** add contentstring (value-enc=%i,%i,%i) "
497 "(len=%i, dlen=%i): '",
498 [_value smallestEncoding],
499 [_value fastestEncoding],
500 self->contentEncoding,
501 [_value length], len);
502 fwrite(cstr, 1, len, stdout);
505 for (i = len - 20; i < len; i++)
506 printf("%5i: 0x%08X %4i\n", i, cstr[i], cstr[i]);
511 NSLog(@"ERROR(%s): could not convert string non-lossy to encoding %i !",
512 __PRETTY_FUNCTION__, self->contentEncoding);
513 cdata = [_value dataUsingEncoding:self->contentEncoding
514 allowLossyConversion:YES];
516 [self appendContentData:cdata];
521 @implementation WOMessage(Escaping)
524 _escapeHtmlValue(unsigned char c, unsigned char *buf, int *pos)
529 buf[j] = '&'; j++; buf[j] = 'a'; j++; buf[j] = 'm'; j++;
530 buf[j] = 'p'; j++; buf[j] = ';';
533 buf[j] = '&'; j++; buf[j] = 'q'; j++; buf[j] = 'u'; j++;
534 buf[j] = 'o'; j++; buf[j] = 't'; j++; buf[j] = ';';
537 buf[j] = '&'; j++; buf[j] = 'l'; j++; buf[j] = 't'; j++;
541 buf[j] = '&'; j++; buf[j] = 'g'; j++; buf[j] = 't'; j++;
553 _escapeAttrValue(unsigned char c, unsigned char *buf, int *pos)
558 buf[j] = '&'; j++; buf[j] = 'a'; j++; buf[j] = 'm'; j++;
559 buf[j] = 'p'; j++; buf[j] = ';';
562 buf[j] = '&'; j++; buf[j] = 'q'; j++; buf[j] = 'u'; j++;
563 buf[j] = 'o'; j++; buf[j] = 't'; j++; buf[j] = ';';
566 buf[j] = '&'; j++; buf[j] = 'l'; j++; buf[j] = 't'; j++;
570 buf[j] = '&'; j++; buf[j] = 'g'; j++; buf[j] = 't'; j++;
575 buf[j] = '&'; j++; buf[j] = '#'; j++; buf[j] = '9'; j++;
579 buf[j] = '&'; j++; buf[j] = '#'; j++; buf[j] = '1'; j++;
580 buf[j] = '0'; j++; buf[j] = ';';
583 buf[j] = '&'; j++; buf[j] = '#'; j++; buf[j] = '1'; j++;
584 buf[j] = '3'; j++; buf[j] = ';';
595 + (NSString *)stringByEscapingHTMLString:(NSString *)_string {
596 return [_string stringByEscapingHTMLString];
599 + (NSString *)stringByEscapingHTMLAttributeValue:(NSString *)_string {
600 return [_string stringByEscapingHTMLAttributeValue];
603 @end /* WOMessage(Escaping) */