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
23 #include <NGObjWeb/WORequest.h>
24 #include <NGObjWeb/WOSession.h>
25 #include <NGObjWeb/WOContext.h>
26 #include <NGObjWeb/WOApplication.h>
27 #include <NGObjWeb/WOSession.h>
28 #include <NGObjWeb/WOCookie.h>
29 #include <NGHttp/NGHttp.h>
30 #include "NGHttp+WO.h"
31 #include <NGExtensions/NSString+Ext.h>
35 #if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY
36 @interface NSObject(Miss)
37 - (id)notImplemented:(SEL)cmd;
41 NGObjWeb_DECLARE NSString *WORequestValueData = @"wodata";
42 NGObjWeb_DECLARE NSString *WORequestValueInstance = @"woinst";
43 NGObjWeb_DECLARE NSString *WORequestValuePageName = @"wopage";
44 NGObjWeb_DECLARE NSString *WORequestValueContextID = @"_c";
45 NGObjWeb_DECLARE NSString *WORequestValueSenderID = @"_i";
46 NGObjWeb_DECLARE NSString *WORequestValueSessionID = @"wosid";
47 NGObjWeb_DECLARE NSString *WONoSelectionString = @"WONoSelectionString";
49 @implementation WORequest
51 static BOOL debugOn = NO;
54 return [super version] + 1 /* v5 */;
57 + (NSString *)lookupLanguagesPlist {
59 #if !COMPILE_AS_FRAMEWORK
60 NSFileManager *fm = [NSFileManager defaultManager];
67 #if COMPILE_AS_FRAMEWORK
68 bundle = [NSBundle bundleForClass:self];
69 apath = [bundle pathForResource:@"Languages" ofType:@"plist"];
71 env = [[NSProcessInfo processInfo] environment];
74 TODO: the following is a dirty hack because GNUstep people decided
75 to change the directory structure. SIGH!
77 #if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY || GNUSTEP_BASE_LIBRARY
78 relPath = @"Library/Libraries";
80 relPath = @"Libraries";
82 relPath = [relPath stringByAppendingPathComponent:@"Resources"];
83 relPath = [relPath stringByAppendingPathComponent:@"NGObjWeb"];
84 relPath = [relPath stringByAppendingPathComponent:@"Languages.plist"];
86 apath = [env objectForKey:@"GNUSTEP_USER_ROOT"];
87 apath = [apath stringByAppendingPathComponent:relPath];
88 if (![fm fileExistsAtPath:apath]) {
89 apath = [env objectForKey:@"GNUSTEP_LOCAL_ROOT"];
90 apath = [apath stringByAppendingPathComponent:relPath];
92 if (![fm fileExistsAtPath:apath]) {
93 apath = [env objectForKey:@"GNUSTEP_SYSTEM_ROOT"];
94 apath = [apath stringByAppendingPathComponent:relPath];
96 if (![fm fileExistsAtPath:apath]) {
99 if (![fm fileExistsAtPath:apath]) {
100 NSLog(@"ERROR: cannot find Languages.plist resource "
101 @"of NGObjWeb library !");
108 static BOOL isInitialized = NO;
109 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
110 NSDictionary *langMap;
113 if (isInitialized) return;
116 NSAssert2([super version] == 4,
117 @"invalid superclass (%@) version %i !",
118 NSStringFromClass([self superclass]), [super version]);
120 debugOn = [WOApplication isDebuggingEnabled];
122 /* apply defaults on some globals ... */
124 apath = [ud stringForKey:@"WORequestValueSessionID"];
125 if ([apath length] > 0)
126 WORequestValueSessionID = [apath copy];
127 apath = [ud stringForKey:@"WORequestValueInstance"];
128 if ([apath length] > 0)
129 WORequestValueInstance = [apath copy];
130 apath = [ud stringForKey:@"WONoSelectionString"];
131 if ([apath length] > 0)
132 WONoSelectionString = [apath copy];
134 /* load language mappings */
136 apath = [self lookupLanguagesPlist];
137 langMap = [NSDictionary dictionaryWithContentsOfFile:apath];
142 defs = [NSDictionary dictionaryWithObject:langMap
143 forKey:@"WOBrowserLanguageMappings"];
144 [ud registerDefaults:defs];
147 NSLog(@"WARNING: did not register browser language mappings: %@", apath);
158 uriLen = [self->_uri cStringLength];
160 uriBuf = uri = malloc(uriLen + 1);
161 [self->_uri getCString:uriBuf]; uriBuf[uriLen] = '\0';
163 /* determine adaptor prefix */
165 if ((serverUrl = [self headerForKey:@"x-webobjects-adaptor-prefix"]))
166 self->adaptorPrefix = [serverUrl copyWithZone:[self zone]];
168 if (self->adaptorPrefix == nil)
169 self->adaptorPrefix = @"";
173 const char *start = NULL;
175 /* skip adaptor prefix */
176 if (self->adaptorPrefix)
177 uri += [self->adaptorPrefix cStringLength];
178 if (*uri == '\0') goto done;
180 /* parse application name */
184 while ((*uri != '\0') && (*uri != '/') && (*uri != '.'))
189 [[NSString alloc] initWithCString:start length:(uri - start)];
192 else if (*uri == '.') {
194 [[NSString alloc] initWithCString:start length:(uri - start)];
196 // skip appname trailer (eg .woa)
197 while ((*uri != '\0') && (*uri != '/'))
199 if (*uri == '\0') goto done;
202 else if (*uri == '/') {
204 [[NSString alloc] initWithCString:start length:(uri - start)];
208 goto done; // invalid state !
210 if (*uri == '\0') goto done;
212 /* parse request handler key */
215 while ((*uri != '\0') && (*uri != '/') && (*uri != '?'))
217 self->requestHandlerKey =
218 [[NSString alloc] initWithCString:start length:(uri - start)];
219 if (*uri == '\0') goto done;
222 /* parse request handler path */
225 while (*uri != '\0' && (*uri != '?'))
227 self->requestHandlerPath =
228 [[NSString alloc] initWithCString:start length:(uri - start)];
231 /* parsing done (found '\0') */
233 ; // required for MacOSX-S
234 if (uriBuf) free(uriBuf);
238 - (id)initWithMethod:(NSString *)_method
239 uri:(NSString *)__uri
240 httpVersion:(NSString *)_version
241 headers:(NSDictionary *)_headers
242 content:(NSData *)_body
243 userInfo:(NSDictionary *)_userInfo
245 if ((self = [super init])) {
246 self->_uri = [__uri copy];
247 self->method = [_method copy];
251 [self setHTTPVersion:_version];
252 [self setContent:_body];
253 [self setUserInfo:_userInfo];
254 [self setHeaders:_headers];
260 [self->method release];
261 [self->_uri release];
263 [self->adaptorPrefix release];
264 [self->requestHandlerKey release];
265 [self->requestHandlerPath release];
266 [self->appName release];
268 [self->formContent release];
269 [self->request release];
275 - (void)_setHttpRequest:(NGHttpRequest *)_request {
276 ASSIGN(self->request, _request);
278 - (NGHttpRequest *)httpRequest {
279 if (self->request == nil) {
280 /* construct request 'on-demand' */
282 [[NSClassFromString(@"NGHttpRequest") alloc] initWithWORequest:self];
284 return self->request;
287 /* request handler */
289 - (void)setRequestHandlerKey:(NSString *)_key {
290 ASSIGNCOPY(self->requestHandlerKey, _key);
292 - (NSString *)requestHandlerKey { // new in WO4
293 if ([self isProxyRequest])
295 return self->requestHandlerKey;
298 - (void)setRequestHandlerPath:(NSString *)_path {
299 ASSIGNCOPY(self->requestHandlerPath, _path);
301 - (NSString *)requestHandlerPath { // new in WO4
302 return self->requestHandlerPath;
305 - (NSArray *)requestHandlerPathArray { // new in WO4
306 NSMutableArray *array = nil;
311 clen = [self->requestHandlerPath cStringLength];
315 cstrBuf = cstr = malloc(clen + 1);
316 [self->requestHandlerPath getCString:cstrBuf]; cstrBuf[clen] = '\0';
319 NSString *component = nil;
320 register char *tmp = cstr;
322 while ((*tmp != '\0') && (*tmp != '?') && (*tmp != '/'))
325 component = ((tmp - cstr) == 0)
327 : [[NSString alloc] initWithCString:cstr length:(tmp - cstr)];
330 if (array == nil) array = [NSMutableArray arrayWithCapacity:64];
331 [array addObject:component];
332 [component release]; component = nil;
336 if (*cstr == '/') cstr++; // skip '/'
338 while ((*cstr != '\0') && (*cstr != '?'));
341 return [[array copy] autorelease];
346 - (BOOL)isFromClientComponent {
350 - (NSString *)sessionID { // deprecated in WO4
351 return [self cookieValueForKey:self->appName];
353 - (NSString *)senderID { // deprecated in WO4
355 return [[[WOApplication application] context] senderID];
358 - (NSString *)contextID {
359 return [[[WOApplication application] context] contextID];
360 //return self->contextID;
363 - (NSString *)applicationName {
364 return self->appName;
366 - (NSString *)applicationHost {
367 return [[NSHost currentHost] name];
370 - (NSString *)adaptorPrefix {
371 return self->adaptorPrefix;
374 - (NSString *)method {
377 - (void)_hackSetURI:(NSString *)_vuri {
378 /* be careful, used by the WebDAV dispatcher for ZideLook range queries */
379 ASSIGNCOPY(self->_uri, _vuri);
384 - (BOOL)isProxyRequest {
385 return [[self uri] isAbsoluteURL];
388 // ******************** Forms ********************
390 - (NSStringEncoding)formValueEncoding {
391 return NSUTF8StringEncoding;
394 - (void)setDefaultFormValueEncoding:(NSStringEncoding)_enc {
395 if (_enc != NSUTF8StringEncoding || _enc != NSASCIIStringEncoding)
396 [self notImplemented:_cmd];
398 - (NSStringEncoding)defaultFormValueEncoding {
399 return NSUTF8StringEncoding;
402 - (void)setFormValueEncodingDetectionEnabled:(BOOL)_flag {
403 if (_flag) [self notImplemented:_cmd];
405 - (BOOL)isFormValueEncodingDetectionEnabled {
409 - (void)_parseQueryParameters:(NSString *)_s intoMap:(NGMutableHashMap *)_map {
413 e = [[_s componentsSeparatedByString:@"&"] objectEnumerator];
414 while ((part = [e nextObject])) {
416 NSString *key, *value;
418 r = [part rangeOfString:@"="];
420 /* missing value of query parameter */
421 key = [part stringByUnescapingURL];
425 key = [[part substringToIndex:r.location] stringByUnescapingURL];
426 value = [[part substringFromIndex:(r.location + r.length)]
427 stringByUnescapingURL];
430 [self->formContent addObject:value forKey:key];
434 - (NGHashMap *)_getFormParameters {
435 if (self->formContent)
436 return self->formContent;
438 if (self->request == nil) {
440 TODO: add parsing of form values
443 a/blah?name=login&pwd=j
446 Content-Type: application/x-www-form-urlencoded
447 browserconfig=%7BisJavaScriptEnabled%3DYES%3B%7D&login=r&button=login
452 BOOL isMultiPartContent = NO, isFormContent = NO;
454 r = [self->_uri rangeOfString:@"?"];
455 query = (r.length > 0)
456 ? [self->_uri substringFromIndex:(r.location + r.length)]
459 if ((ctype = [self headerForKey:@"content-type"]) != nil) {
460 isFormContent = [ctype hasPrefix:@"application/x-www-form-urlencoded"];
462 isMultiPartContent = [ctype hasPrefix:@"multipart/form-data"];
465 if (query != nil || isFormContent || isMultiPartContent) {
466 NSAutoreleasePool *pool;
468 pool = [[NSAutoreleasePool alloc] init];
469 self->formContent = [[NGMutableHashMap alloc] init];
471 /* parse query string */
473 [self _parseQueryParameters:query intoMap:self->formContent];
475 /* parse content (if form content) */
477 [self _parseQueryParameters:[self contentAsString]
478 intoMap:self->formContent];
480 else if (isMultiPartContent) {
482 @"ERROR: missing NGHttpRequest, cannot parse multipart"];
488 self->formContent = [[NGHashMap alloc] init];
491 self->formContent = [[self->request formParameters] retain];
492 return self->formContent;
495 - (NSArray *)formValueKeys {
496 id paras = [self _getFormParameters];
498 if ([paras respondsToSelector:@selector(allKeys)])
499 return [paras allKeys];
504 - (NSString *)formValueForKey:(NSString *)_key {
509 paras = [self _getFormParameters];
510 if ([paras respondsToSelector:@selector(objectForKey:)])
511 value = [(NSDictionary *)paras objectForKey:_key];
515 - (NSArray *)formValuesForKey:(NSString *)_key {
516 id paras = [self _getFormParameters];
517 return [paras respondsToSelector:@selector(objectsForKey:)]
518 ? [paras objectsForKey:_key]
522 - (NSDictionary *)formValues {
525 if ((paras = [self _getFormParameters]) == nil)
528 /* check class, could change with different HTTP adaptor */
530 if ([paras isKindOfClass:[NGHashMap class]])
531 return [paras asDictionaryWithArraysForValues];
532 if ([paras isKindOfClass:[NSDictionary class]])
536 @"ERROR(%s): don't know how to deal with form object: %@", paras];
540 // ******************** Headers ******************
542 - (NSString *)languageForBrowserLanguageCode:(NSString *)_e {
543 static NSDictionary *langMap = nil;
546 if (_e == nil) return nil;
548 if (langMap == nil) {
549 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
551 langMap = [[ud dictionaryForKey:@"WOBrowserLanguageMappings"] copy];
552 if (langMap == nil) {
553 [self debugWithFormat:
554 @"WARNING: did not find browser language mappings!"];
558 _e = [_e lowercaseString];
560 lang = [langMap objectForKey:_e];
561 if (lang == nil && [_e length] > 2) {
562 /* process constructs like 'de-ch' */
563 if ([_e characterAtIndex:2] == '-') {
566 ek = [_e substringToIndex:2];
567 lang = [langMap objectForKey:ek];
570 if (lang == nil && ![_e isEqualToString:@"*"]) {
571 [self debugWithFormat:@"did not find '%@' in map: %@",
572 _e, [[langMap allKeys] componentsJoinedByString:@", "]];
577 - (NSString *)_languageFromUserAgent {
579 user-agent sometimes stores the browser-language,
580 eg: Opera/5.0 (Linux 2.2.18 i686; U) [en]
586 if ((ua = [self headerForKey:@"user-agent"]) == nil)
589 rng = [ua rangeOfString:@"["];
593 tmp = [ua substringFromIndex:(rng.location + rng.length)];
594 rng = [tmp rangeOfString:@"]"];
596 tmp = [tmp substringToIndex:rng.location];
598 return [self languageForBrowserLanguageCode:tmp];
601 - (NSArray *)browserLanguages { /* new in WO4 */
602 static NSArray *defLangs = nil;
605 NSMutableArray *languages;
609 languages = [NSMutableArray arrayWithCapacity:8];
611 e = [[self headersForKey:@"accept-language"] objectEnumerator];
612 while ((hheader = [e nextObject])) {
615 le = [[hheader componentsSeparatedByString:@","] objectEnumerator];
616 while ((language = [le nextObject])) {
620 /* split off the quality (eg 'en;0.96') */
621 r = [language rangeOfString:@";"];
623 language = [language substringToIndex:r.location];
624 language = [language stringByTrimmingSpaces];
627 if ((tmp = [self languageForBrowserLanguageCode:language]))
630 if ([languages containsObject:language])
633 [languages addObject:language];
637 if ((tmp = [self _languageFromUserAgent]))
638 [languages addObject:tmp];
640 if (defLangs == nil) {
641 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
642 defLangs = [[ud arrayForKey:@"WODefaultLanguages"] copy];
644 [languages addObjectsFromArray:defLangs];
646 //[self debugWithFormat:@"languages: %@", languages];
647 return [[languages copy] autorelease];
652 - (NSArray *)cookieValuesForKey:(NSString *)_key {
653 NSEnumerator *ecookies;
654 NSMutableArray *values;
657 values = [NSMutableArray arrayWithCapacity:8];
659 ecookies = [[self cookies] objectEnumerator];
660 while ((cookie = [ecookies nextObject])) {
661 if ([_key isEqualToString:[cookie name]])
662 [values addObject:[cookie value]];
668 - (NSString *)cookieValueForKey:(NSString *)_key {
669 NSEnumerator *ecookies;
672 ecookies = [[self cookies] objectEnumerator];
673 while ((cookie = [ecookies nextObject])) {
674 if ([_key isEqualToString:[cookie name]])
675 return [cookie value];
680 - (NSDictionary *)cookieValues {
681 NSEnumerator *ecookies;
682 NSMutableDictionary *values;
685 values = [NSMutableDictionary dictionaryWithCapacity:8];
687 ecookies = [[self cookies] objectEnumerator];
688 while ((cookie = [ecookies nextObject])) {
690 NSMutableArray *vArray;
692 name = [cookie name];
693 vArray = [values objectForKey:name];
696 vArray = [[NSMutableArray alloc] initWithCapacity:8];
697 [values setObject:vArray forKey:name];
701 [vArray addObject:[cookie value]];
709 - (BOOL)isDebuggingEnabled {
712 - (NSString *)loggingPrefix {
713 return [NSString stringWithFormat:@"|Rq:%@ 0x%08X|",
714 [self method], self];
719 - (NSString *)description {
720 NSMutableString *str;
722 str = [NSMutableString stringWithCapacity:256];
723 [str appendFormat:@"<%@[0x%08X]:", NSStringFromClass([self class]), self];
724 [str appendFormat:@" method=%@", [self method]];
725 [str appendFormat:@" uri=%@", [self uri]];
726 [str appendFormat:@" app=%@", self->appName];
727 [str appendFormat:@" rqKey=%@", [self requestHandlerKey]];
728 [str appendFormat:@" rqPath=%@", [self requestHandlerPath]];
729 [str appendString:@">"];