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/WORequest.h>
23 #include <NGObjWeb/WOSession.h>
24 #include <NGObjWeb/WOContext.h>
25 #include <NGObjWeb/WOApplication.h>
26 #include <NGObjWeb/WOSession.h>
27 #include <NGObjWeb/WOCookie.h>
28 #include <NGHttp/NGHttp.h>
29 #include "NGHttp+WO.h"
30 #include <NGExtensions/NSString+Ext.h>
34 #if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY
35 @interface NSObject(Miss)
36 - (id)notImplemented:(SEL)cmd;
40 NGObjWeb_DECLARE NSString *WORequestValueData = @"wodata";
41 NGObjWeb_DECLARE NSString *WORequestValueInstance = @"woinst";
42 NGObjWeb_DECLARE NSString *WORequestValuePageName = @"wopage";
43 NGObjWeb_DECLARE NSString *WORequestValueContextID = @"_c";
44 NGObjWeb_DECLARE NSString *WORequestValueSenderID = @"_i";
45 NGObjWeb_DECLARE NSString *WORequestValueSessionID = @"wosid";
46 NGObjWeb_DECLARE NSString *WONoSelectionString = @"WONoSelectionString";
48 @interface WOCoreApplication(Resources)
49 + (NSString *)findNGObjWebResource:(NSString *)_name ofType:(NSString *)_ext;
52 @implementation WORequest
54 static BOOL debugOn = NO;
57 return [super version] + 2 /* v7 */;
61 static BOOL isInitialized = NO;
62 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
63 NSDictionary *langMap;
66 if (isInitialized) return;
69 NSAssert2([super version] == 5,
70 @"invalid superclass (%@) version %i !",
71 NSStringFromClass([self superclass]), [super version]);
73 debugOn = [WOApplication isDebuggingEnabled];
75 /* apply defaults on some globals ... */
77 apath = [ud stringForKey:@"WORequestValueSessionID"];
78 if ([apath length] > 0)
79 WORequestValueSessionID = [apath copy];
80 apath = [ud stringForKey:@"WORequestValueInstance"];
81 if ([apath length] > 0)
82 WORequestValueInstance = [apath copy];
83 apath = [ud stringForKey:@"WONoSelectionString"];
84 if ([apath length] > 0)
85 WONoSelectionString = [apath copy];
87 /* load language mappings */
89 apath = [WOApplication findNGObjWebResource:@"Languages" ofType:@"plist"];
91 NSLog(@"ERROR: cannot find Languages.plist resource "
92 @"of NGObjWeb library !");
96 langMap = [NSDictionary dictionaryWithContentsOfFile:apath];
101 defs = [NSDictionary dictionaryWithObject:langMap
102 forKey:@"WOBrowserLanguageMappings"];
103 [ud registerDefaults:defs];
106 NSLog(@"WARNING: did not register browser language mappings: %@", apath);
117 uriLen = [self->_uri cStringLength];
119 uriBuf = uri = malloc(uriLen + 1);
120 [self->_uri getCString:uriBuf]; uriBuf[uriLen] = '\0';
122 /* determine adaptor prefix */
124 if ((serverUrl = [self headerForKey:@"x-webobjects-adaptor-prefix"]))
125 self->adaptorPrefix = [serverUrl copyWithZone:[self zone]];
127 if (self->adaptorPrefix == nil)
128 self->adaptorPrefix = @"";
132 const char *start = NULL;
134 /* skip adaptor prefix */
135 if (self->adaptorPrefix)
136 uri += [self->adaptorPrefix cStringLength];
137 if (*uri == '\0') goto done;
139 /* parse application name */
143 while ((*uri != '\0') && (*uri != '/') && (*uri != '.'))
148 [[NSString alloc] initWithCString:start length:(uri - start)];
151 else if (*uri == '.') {
153 [[NSString alloc] initWithCString:start length:(uri - start)];
155 // skip appname trailer (eg .woa)
156 while ((*uri != '\0') && (*uri != '/'))
158 if (*uri == '\0') goto done;
161 else if (*uri == '/') {
163 [[NSString alloc] initWithCString:start length:(uri - start)];
167 goto done; // invalid state !
169 if (*uri == '\0') goto done;
171 /* parse request handler key */
174 while ((*uri != '\0') && (*uri != '/') && (*uri != '?'))
176 self->requestHandlerKey =
177 [[NSString alloc] initWithCString:start length:(uri - start)];
178 if (*uri == '\0') goto done;
181 /* parse request handler path */
184 while (*uri != '\0' && (*uri != '?'))
186 self->requestHandlerPath =
187 [[NSString alloc] initWithCString:start length:(uri - start)];
190 /* parsing done (found '\0') */
192 ; // required for MacOSX-S
193 if (uriBuf) free(uriBuf);
197 - (id)initWithMethod:(NSString *)_method
198 uri:(NSString *)__uri
199 httpVersion:(NSString *)_version
200 headers:(NSDictionary *)_headers
201 content:(NSData *)_body
202 userInfo:(NSDictionary *)_userInfo
204 if ((self = [super init])) {
205 self->_uri = [__uri copy];
206 self->method = [_method copy];
210 [self setHTTPVersion:_version];
211 [self setContent:_body];
212 [self setUserInfo:_userInfo];
213 [self setHeaders:_headers];
219 [self->startDate release];
220 [self->startStatistics release];
221 [self->method release];
222 [self->_uri release];
223 [self->adaptorPrefix release];
224 [self->requestHandlerKey release];
225 [self->requestHandlerPath release];
226 [self->appName release];
227 [self->formContent release];
228 [self->request release];
234 - (void)_setHttpRequest:(NGHttpRequest *)_request {
235 ASSIGN(self->request, _request);
237 - (NGHttpRequest *)httpRequest {
238 if (self->request == nil) {
239 /* construct request 'on-demand' */
241 [[NSClassFromString(@"NGHttpRequest") alloc] initWithWORequest:self];
243 return self->request;
246 /* request handler */
248 - (void)setRequestHandlerKey:(NSString *)_key {
249 ASSIGNCOPY(self->requestHandlerKey, _key);
251 - (NSString *)requestHandlerKey { // new in WO4
252 if ([self isProxyRequest])
254 return self->requestHandlerKey;
257 - (void)setRequestHandlerPath:(NSString *)_path {
258 ASSIGNCOPY(self->requestHandlerPath, _path);
260 - (NSString *)requestHandlerPath { // new in WO4
261 return self->requestHandlerPath;
264 - (NSArray *)requestHandlerPathArray { // new in WO4
265 NSMutableArray *array = nil;
270 clen = [self->requestHandlerPath cStringLength];
274 cstrBuf = cstr = malloc(clen + 1);
275 [self->requestHandlerPath getCString:cstrBuf]; cstrBuf[clen] = '\0';
278 NSString *component = nil;
279 register char *tmp = cstr;
281 while ((*tmp != '\0') && (*tmp != '?') && (*tmp != '/'))
284 component = ((tmp - cstr) == 0)
286 : [[NSString alloc] initWithCString:cstr length:(tmp - cstr)];
289 if (array == nil) array = [NSMutableArray arrayWithCapacity:64];
290 [array addObject:component];
291 [component release]; component = nil;
295 if (*cstr == '/') cstr++; // skip '/'
297 while ((*cstr != '\0') && (*cstr != '?'));
300 return [[array copy] autorelease];
305 - (BOOL)isFromClientComponent {
309 - (NSString *)sessionID { // deprecated in WO4
310 return [self cookieValueForKey:self->appName];
312 - (NSString *)senderID { // deprecated in WO4
314 return [[[WOApplication application] context] senderID];
317 - (NSString *)contextID {
318 return [[[WOApplication application] context] contextID];
319 //return self->contextID;
322 - (NSString *)applicationName {
323 return self->appName;
325 - (NSString *)applicationHost {
326 return [[NSHost currentHost] name];
329 - (NSString *)adaptorPrefix {
330 return self->adaptorPrefix;
333 - (NSString *)method {
336 - (void)_hackSetURI:(NSString *)_vuri {
337 /* be careful, used by the WebDAV dispatcher for ZideLook range queries */
338 ASSIGNCOPY(self->_uri, _vuri);
343 - (BOOL)isProxyRequest {
344 return [[self uri] isAbsoluteURL];
347 - (void)setStartDate:(NSCalendarDate *)_startDate {
348 ASSIGNCOPY(self->startDate, _startDate);
350 - (NSCalendarDate *)startDate {
351 return self->startDate;
353 - (id)startStatistics {
354 return self->startStatistics;
359 - (NSStringEncoding)formValueEncoding {
360 return NSUTF8StringEncoding;
363 - (void)setDefaultFormValueEncoding:(NSStringEncoding)_enc {
364 if (_enc != NSUTF8StringEncoding || _enc != NSASCIIStringEncoding)
365 [self notImplemented:_cmd];
367 - (NSStringEncoding)defaultFormValueEncoding {
368 return NSUTF8StringEncoding;
371 - (void)setFormValueEncodingDetectionEnabled:(BOOL)_flag {
372 if (_flag) [self notImplemented:_cmd];
374 - (BOOL)isFormValueEncodingDetectionEnabled {
378 - (void)_parseQueryParameters:(NSString *)_s intoMap:(NGMutableHashMap *)_map {
382 e = [[_s componentsSeparatedByString:@"&"] objectEnumerator];
383 while ((part = [e nextObject])) {
385 NSString *key, *value;
387 r = [part rangeOfString:@"="];
389 /* missing value of query parameter */
390 key = [part stringByUnescapingURL];
394 key = [[part substringToIndex:r.location] stringByUnescapingURL];
395 value = [[part substringFromIndex:(r.location + r.length)]
396 stringByUnescapingURL];
399 [self->formContent addObject:value forKey:key];
403 - (NGHashMap *)_getFormParameters {
404 if (self->formContent)
405 return self->formContent;
407 if (self->request == nil) {
409 TODO: add parsing of form values
412 a/blah?name=login&pwd=j
415 Content-Type: application/x-www-form-urlencoded
416 browserconfig=%7BisJavaScriptEnabled%3DYES%3B%7D&login=r&button=login
421 BOOL isMultiPartContent = NO, isFormContent = NO;
423 r = [self->_uri rangeOfString:@"?"];
424 query = (r.length > 0)
425 ? [self->_uri substringFromIndex:(r.location + r.length)]
428 if ((ctype = [self headerForKey:@"content-type"]) != nil) {
429 isFormContent = [ctype hasPrefix:@"application/x-www-form-urlencoded"];
431 isMultiPartContent = [ctype hasPrefix:@"multipart/form-data"];
434 if (query != nil || isFormContent || isMultiPartContent) {
435 NSAutoreleasePool *pool;
437 pool = [[NSAutoreleasePool alloc] init];
438 self->formContent = [[NGMutableHashMap alloc] init];
440 /* parse query string */
442 [self _parseQueryParameters:query intoMap:self->formContent];
444 /* parse content (if form content) */
446 [self _parseQueryParameters:[self contentAsString]
447 intoMap:self->formContent];
449 else if (isMultiPartContent) {
451 @"ERROR: missing NGHttpRequest, cannot parse multipart"];
457 self->formContent = [[NGHashMap alloc] init];
460 self->formContent = [[self->request formParameters] retain];
461 return self->formContent;
464 - (NSArray *)formValueKeys {
465 id paras = [self _getFormParameters];
467 if ([paras respondsToSelector:@selector(allKeys)])
468 return [paras allKeys];
473 - (NSString *)formValueForKey:(NSString *)_key {
478 paras = [self _getFormParameters];
479 if ([paras respondsToSelector:@selector(objectForKey:)])
480 value = [(NSDictionary *)paras objectForKey:_key];
484 - (NSArray *)formValuesForKey:(NSString *)_key {
485 id paras = [self _getFormParameters];
486 return [paras respondsToSelector:@selector(objectsForKey:)]
487 ? [paras objectsForKey:_key]
491 - (NSDictionary *)formValues {
494 if ((paras = [self _getFormParameters]) == nil)
497 /* check class, could change with different HTTP adaptor */
499 if ([paras isKindOfClass:[NGHashMap class]])
500 return [paras asDictionaryWithArraysForValues];
501 if ([paras isKindOfClass:[NSDictionary class]])
505 @"ERROR(%s): don't know how to deal with form object: %@", paras];
509 // ******************** Headers ******************
511 - (NSString *)languageForBrowserLanguageCode:(NSString *)_e {
512 static NSDictionary *langMap = nil;
515 if (_e == nil) return nil;
517 if (langMap == nil) {
518 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
520 langMap = [[ud dictionaryForKey:@"WOBrowserLanguageMappings"] copy];
521 if (langMap == nil) {
522 [self debugWithFormat:
523 @"WARNING: did not find browser language mappings!"];
527 _e = [_e lowercaseString];
529 lang = [langMap objectForKey:_e];
530 if (lang == nil && [_e length] > 2) {
531 /* process constructs like 'de-ch' */
532 if ([_e characterAtIndex:2] == '-') {
535 ek = [_e substringToIndex:2];
536 lang = [langMap objectForKey:ek];
539 if (lang == nil && ![_e isEqualToString:@"*"]) {
540 [self debugWithFormat:@"did not find '%@' in map: %@",
541 _e, [[langMap allKeys] componentsJoinedByString:@", "]];
546 - (NSString *)_languageFromUserAgent {
548 user-agent sometimes stores the browser-language,
549 eg: Opera/5.0 (Linux 2.2.18 i686; U) [en]
555 if ((ua = [self headerForKey:@"user-agent"]) == nil)
558 rng = [ua rangeOfString:@"["];
562 tmp = [ua substringFromIndex:(rng.location + rng.length)];
563 rng = [tmp rangeOfString:@"]"];
565 tmp = [tmp substringToIndex:rng.location];
567 return [self languageForBrowserLanguageCode:tmp];
570 - (NSArray *)browserLanguages { /* new in WO4 */
571 static NSArray *defLangs = nil;
574 NSMutableArray *languages;
578 languages = [NSMutableArray arrayWithCapacity:8];
580 e = [[self headersForKey:@"accept-language"] objectEnumerator];
581 while ((hheader = [e nextObject])) {
584 le = [[hheader componentsSeparatedByString:@","] objectEnumerator];
585 while ((language = [le nextObject])) {
589 /* split off the quality (eg 'en;0.96') */
590 r = [language rangeOfString:@";"];
592 language = [language substringToIndex:r.location];
593 language = [language stringByTrimmingSpaces];
596 if ((tmp = [self languageForBrowserLanguageCode:language]))
599 if ([languages containsObject:language])
602 [languages addObject:language];
606 if ((tmp = [self _languageFromUserAgent]))
607 [languages addObject:tmp];
609 if (defLangs == nil) {
610 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
611 defLangs = [[ud arrayForKey:@"WODefaultLanguages"] copy];
613 [languages addObjectsFromArray:defLangs];
615 //[self debugWithFormat:@"languages: %@", languages];
616 return [[languages copy] autorelease];
621 - (NSArray *)cookieValuesForKey:(NSString *)_key {
622 NSEnumerator *ecookies;
623 NSMutableArray *values;
626 values = [NSMutableArray arrayWithCapacity:8];
628 ecookies = [[self cookies] objectEnumerator];
629 while ((cookie = [ecookies nextObject])) {
630 if ([_key isEqualToString:[cookie name]])
631 [values addObject:[cookie value]];
637 - (NSString *)cookieValueForKey:(NSString *)_key {
638 NSEnumerator *ecookies;
641 ecookies = [[self cookies] objectEnumerator];
642 while ((cookie = [ecookies nextObject])) {
643 if ([_key isEqualToString:[cookie name]])
644 return [cookie value];
649 - (NSDictionary *)cookieValues {
650 NSEnumerator *ecookies;
651 NSMutableDictionary *values;
654 values = [NSMutableDictionary dictionaryWithCapacity:8];
656 ecookies = [[self cookies] objectEnumerator];
657 while ((cookie = [ecookies nextObject])) {
659 NSMutableArray *vArray;
661 name = [cookie name];
662 vArray = [values objectForKey:name];
665 vArray = [[NSMutableArray alloc] initWithCapacity:8];
666 [values setObject:vArray forKey:name];
670 [vArray addObject:[cookie value]];
678 - (BOOL)isDebuggingEnabled {
681 - (NSString *)loggingPrefix {
682 return [NSString stringWithFormat:@"|Rq:%@ 0x%08X|",
683 [self method], self];
688 - (NSString *)description {
689 NSMutableString *str;
691 str = [NSMutableString stringWithCapacity:256];
692 [str appendFormat:@"<%@[0x%08X]:", NSStringFromClass([self class]), self];
693 [str appendFormat:@" method=%@", [self method]];
694 [str appendFormat:@" uri=%@", [self uri]];
695 [str appendFormat:@" app=%@", self->appName];
696 [str appendFormat:@" rqKey=%@", [self requestHandlerKey]];
697 [str appendFormat:@" rqPath=%@", [self requestHandlerPath]];
698 [str appendString:@">"];