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 @implementation WORequest
50 static BOOL debugOn = NO;
53 return [super version] + 2 /* v7 */;
56 + (NSString *)lookupLanguagesPlist {
58 #if !COMPILE_AS_FRAMEWORK
59 NSFileManager *fm = [NSFileManager defaultManager];
66 #if COMPILE_AS_FRAMEWORK
67 bundle = [NSBundle bundleForClass:self];
68 apath = [bundle pathForResource:@"Languages" ofType:@"plist"];
70 env = [[NSProcessInfo processInfo] environment];
73 TODO: the following is a dirty hack because GNUstep people decided
74 to change the directory structure. SIGH!
76 #if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY || GNUSTEP_BASE_LIBRARY
77 relPath = @"Library/Libraries";
79 relPath = @"Libraries";
81 relPath = [relPath stringByAppendingPathComponent:@"Resources"];
82 relPath = [relPath stringByAppendingPathComponent:@"NGObjWeb"];
83 relPath = [relPath stringByAppendingPathComponent:@"Languages.plist"];
85 apath = [env objectForKey:@"GNUSTEP_USER_ROOT"];
86 apath = [apath stringByAppendingPathComponent:relPath];
87 if (![fm fileExistsAtPath:apath]) {
88 apath = [env objectForKey:@"GNUSTEP_LOCAL_ROOT"];
89 apath = [apath stringByAppendingPathComponent:relPath];
91 if (![fm fileExistsAtPath:apath]) {
92 apath = [env objectForKey:@"GNUSTEP_SYSTEM_ROOT"];
93 apath = [apath stringByAppendingPathComponent:relPath];
95 if (![fm fileExistsAtPath:apath]) {
98 if (![fm fileExistsAtPath:apath]) {
99 NSLog(@"ERROR: cannot find Languages.plist resource "
100 @"of NGObjWeb library !");
107 static BOOL isInitialized = NO;
108 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
109 NSDictionary *langMap;
112 if (isInitialized) return;
115 NSAssert2([super version] == 5,
116 @"invalid superclass (%@) version %i !",
117 NSStringFromClass([self superclass]), [super version]);
119 debugOn = [WOApplication isDebuggingEnabled];
121 /* apply defaults on some globals ... */
123 apath = [ud stringForKey:@"WORequestValueSessionID"];
124 if ([apath length] > 0)
125 WORequestValueSessionID = [apath copy];
126 apath = [ud stringForKey:@"WORequestValueInstance"];
127 if ([apath length] > 0)
128 WORequestValueInstance = [apath copy];
129 apath = [ud stringForKey:@"WONoSelectionString"];
130 if ([apath length] > 0)
131 WONoSelectionString = [apath copy];
133 /* load language mappings */
135 apath = [self lookupLanguagesPlist];
136 langMap = [NSDictionary dictionaryWithContentsOfFile:apath];
141 defs = [NSDictionary dictionaryWithObject:langMap
142 forKey:@"WOBrowserLanguageMappings"];
143 [ud registerDefaults:defs];
146 NSLog(@"WARNING: did not register browser language mappings: %@", apath);
157 uriLen = [self->_uri cStringLength];
159 uriBuf = uri = malloc(uriLen + 1);
160 [self->_uri getCString:uriBuf]; uriBuf[uriLen] = '\0';
162 /* determine adaptor prefix */
164 if ((serverUrl = [self headerForKey:@"x-webobjects-adaptor-prefix"]))
165 self->adaptorPrefix = [serverUrl copyWithZone:[self zone]];
167 if (self->adaptorPrefix == nil)
168 self->adaptorPrefix = @"";
172 const char *start = NULL;
174 /* skip adaptor prefix */
175 if (self->adaptorPrefix)
176 uri += [self->adaptorPrefix cStringLength];
177 if (*uri == '\0') goto done;
179 /* parse application name */
183 while ((*uri != '\0') && (*uri != '/') && (*uri != '.'))
188 [[NSString alloc] initWithCString:start length:(uri - start)];
191 else if (*uri == '.') {
193 [[NSString alloc] initWithCString:start length:(uri - start)];
195 // skip appname trailer (eg .woa)
196 while ((*uri != '\0') && (*uri != '/'))
198 if (*uri == '\0') goto done;
201 else if (*uri == '/') {
203 [[NSString alloc] initWithCString:start length:(uri - start)];
207 goto done; // invalid state !
209 if (*uri == '\0') goto done;
211 /* parse request handler key */
214 while ((*uri != '\0') && (*uri != '/') && (*uri != '?'))
216 self->requestHandlerKey =
217 [[NSString alloc] initWithCString:start length:(uri - start)];
218 if (*uri == '\0') goto done;
221 /* parse request handler path */
224 while (*uri != '\0' && (*uri != '?'))
226 self->requestHandlerPath =
227 [[NSString alloc] initWithCString:start length:(uri - start)];
230 /* parsing done (found '\0') */
232 ; // required for MacOSX-S
233 if (uriBuf) free(uriBuf);
237 - (id)initWithMethod:(NSString *)_method
238 uri:(NSString *)__uri
239 httpVersion:(NSString *)_version
240 headers:(NSDictionary *)_headers
241 content:(NSData *)_body
242 userInfo:(NSDictionary *)_userInfo
244 if ((self = [super init])) {
245 self->_uri = [__uri copy];
246 self->method = [_method copy];
250 [self setHTTPVersion:_version];
251 [self setContent:_body];
252 [self setUserInfo:_userInfo];
253 [self setHeaders:_headers];
259 [self->startDate release];
260 [self->startStatistics release];
261 [self->method release];
262 [self->_uri release];
263 [self->adaptorPrefix release];
264 [self->requestHandlerKey release];
265 [self->requestHandlerPath release];
266 [self->appName release];
267 [self->formContent release];
268 [self->request release];
274 - (void)_setHttpRequest:(NGHttpRequest *)_request {
275 ASSIGN(self->request, _request);
277 - (NGHttpRequest *)httpRequest {
278 if (self->request == nil) {
279 /* construct request 'on-demand' */
281 [[NSClassFromString(@"NGHttpRequest") alloc] initWithWORequest:self];
283 return self->request;
286 /* request handler */
288 - (void)setRequestHandlerKey:(NSString *)_key {
289 ASSIGNCOPY(self->requestHandlerKey, _key);
291 - (NSString *)requestHandlerKey { // new in WO4
292 if ([self isProxyRequest])
294 return self->requestHandlerKey;
297 - (void)setRequestHandlerPath:(NSString *)_path {
298 ASSIGNCOPY(self->requestHandlerPath, _path);
300 - (NSString *)requestHandlerPath { // new in WO4
301 return self->requestHandlerPath;
304 - (NSArray *)requestHandlerPathArray { // new in WO4
305 NSMutableArray *array = nil;
310 clen = [self->requestHandlerPath cStringLength];
314 cstrBuf = cstr = malloc(clen + 1);
315 [self->requestHandlerPath getCString:cstrBuf]; cstrBuf[clen] = '\0';
318 NSString *component = nil;
319 register char *tmp = cstr;
321 while ((*tmp != '\0') && (*tmp != '?') && (*tmp != '/'))
324 component = ((tmp - cstr) == 0)
326 : [[NSString alloc] initWithCString:cstr length:(tmp - cstr)];
329 if (array == nil) array = [NSMutableArray arrayWithCapacity:64];
330 [array addObject:component];
331 [component release]; component = nil;
335 if (*cstr == '/') cstr++; // skip '/'
337 while ((*cstr != '\0') && (*cstr != '?'));
340 return [[array copy] autorelease];
345 - (BOOL)isFromClientComponent {
349 - (NSString *)sessionID { // deprecated in WO4
350 return [self cookieValueForKey:self->appName];
352 - (NSString *)senderID { // deprecated in WO4
354 return [[[WOApplication application] context] senderID];
357 - (NSString *)contextID {
358 return [[[WOApplication application] context] contextID];
359 //return self->contextID;
362 - (NSString *)applicationName {
363 return self->appName;
365 - (NSString *)applicationHost {
366 return [[NSHost currentHost] name];
369 - (NSString *)adaptorPrefix {
370 return self->adaptorPrefix;
373 - (NSString *)method {
376 - (void)_hackSetURI:(NSString *)_vuri {
377 /* be careful, used by the WebDAV dispatcher for ZideLook range queries */
378 ASSIGNCOPY(self->_uri, _vuri);
383 - (BOOL)isProxyRequest {
384 return [[self uri] isAbsoluteURL];
387 - (void)setStartDate:(NSCalendarDate *)_startDate {
388 ASSIGNCOPY(self->startDate, _startDate);
390 - (NSCalendarDate *)startDate {
391 return self->startDate;
393 - (id)startStatistics {
394 return self->startStatistics;
399 - (NSStringEncoding)formValueEncoding {
400 return NSUTF8StringEncoding;
403 - (void)setDefaultFormValueEncoding:(NSStringEncoding)_enc {
404 if (_enc != NSUTF8StringEncoding || _enc != NSASCIIStringEncoding)
405 [self notImplemented:_cmd];
407 - (NSStringEncoding)defaultFormValueEncoding {
408 return NSUTF8StringEncoding;
411 - (void)setFormValueEncodingDetectionEnabled:(BOOL)_flag {
412 if (_flag) [self notImplemented:_cmd];
414 - (BOOL)isFormValueEncodingDetectionEnabled {
418 - (void)_parseQueryParameters:(NSString *)_s intoMap:(NGMutableHashMap *)_map {
422 e = [[_s componentsSeparatedByString:@"&"] objectEnumerator];
423 while ((part = [e nextObject])) {
425 NSString *key, *value;
427 r = [part rangeOfString:@"="];
429 /* missing value of query parameter */
430 key = [part stringByUnescapingURL];
434 key = [[part substringToIndex:r.location] stringByUnescapingURL];
435 value = [[part substringFromIndex:(r.location + r.length)]
436 stringByUnescapingURL];
439 [self->formContent addObject:value forKey:key];
443 - (NGHashMap *)_getFormParameters {
444 if (self->formContent)
445 return self->formContent;
447 if (self->request == nil) {
449 TODO: add parsing of form values
452 a/blah?name=login&pwd=j
455 Content-Type: application/x-www-form-urlencoded
456 browserconfig=%7BisJavaScriptEnabled%3DYES%3B%7D&login=r&button=login
461 BOOL isMultiPartContent = NO, isFormContent = NO;
463 r = [self->_uri rangeOfString:@"?"];
464 query = (r.length > 0)
465 ? [self->_uri substringFromIndex:(r.location + r.length)]
468 if ((ctype = [self headerForKey:@"content-type"]) != nil) {
469 isFormContent = [ctype hasPrefix:@"application/x-www-form-urlencoded"];
471 isMultiPartContent = [ctype hasPrefix:@"multipart/form-data"];
474 if (query != nil || isFormContent || isMultiPartContent) {
475 NSAutoreleasePool *pool;
477 pool = [[NSAutoreleasePool alloc] init];
478 self->formContent = [[NGMutableHashMap alloc] init];
480 /* parse query string */
482 [self _parseQueryParameters:query intoMap:self->formContent];
484 /* parse content (if form content) */
486 [self _parseQueryParameters:[self contentAsString]
487 intoMap:self->formContent];
489 else if (isMultiPartContent) {
491 @"ERROR: missing NGHttpRequest, cannot parse multipart"];
497 self->formContent = [[NGHashMap alloc] init];
500 self->formContent = [[self->request formParameters] retain];
501 return self->formContent;
504 - (NSArray *)formValueKeys {
505 id paras = [self _getFormParameters];
507 if ([paras respondsToSelector:@selector(allKeys)])
508 return [paras allKeys];
513 - (NSString *)formValueForKey:(NSString *)_key {
518 paras = [self _getFormParameters];
519 if ([paras respondsToSelector:@selector(objectForKey:)])
520 value = [(NSDictionary *)paras objectForKey:_key];
524 - (NSArray *)formValuesForKey:(NSString *)_key {
525 id paras = [self _getFormParameters];
526 return [paras respondsToSelector:@selector(objectsForKey:)]
527 ? [paras objectsForKey:_key]
531 - (NSDictionary *)formValues {
534 if ((paras = [self _getFormParameters]) == nil)
537 /* check class, could change with different HTTP adaptor */
539 if ([paras isKindOfClass:[NGHashMap class]])
540 return [paras asDictionaryWithArraysForValues];
541 if ([paras isKindOfClass:[NSDictionary class]])
545 @"ERROR(%s): don't know how to deal with form object: %@", paras];
549 // ******************** Headers ******************
551 - (NSString *)languageForBrowserLanguageCode:(NSString *)_e {
552 static NSDictionary *langMap = nil;
555 if (_e == nil) return nil;
557 if (langMap == nil) {
558 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
560 langMap = [[ud dictionaryForKey:@"WOBrowserLanguageMappings"] copy];
561 if (langMap == nil) {
562 [self debugWithFormat:
563 @"WARNING: did not find browser language mappings!"];
567 _e = [_e lowercaseString];
569 lang = [langMap objectForKey:_e];
570 if (lang == nil && [_e length] > 2) {
571 /* process constructs like 'de-ch' */
572 if ([_e characterAtIndex:2] == '-') {
575 ek = [_e substringToIndex:2];
576 lang = [langMap objectForKey:ek];
579 if (lang == nil && ![_e isEqualToString:@"*"]) {
580 [self debugWithFormat:@"did not find '%@' in map: %@",
581 _e, [[langMap allKeys] componentsJoinedByString:@", "]];
586 - (NSString *)_languageFromUserAgent {
588 user-agent sometimes stores the browser-language,
589 eg: Opera/5.0 (Linux 2.2.18 i686; U) [en]
595 if ((ua = [self headerForKey:@"user-agent"]) == nil)
598 rng = [ua rangeOfString:@"["];
602 tmp = [ua substringFromIndex:(rng.location + rng.length)];
603 rng = [tmp rangeOfString:@"]"];
605 tmp = [tmp substringToIndex:rng.location];
607 return [self languageForBrowserLanguageCode:tmp];
610 - (NSArray *)browserLanguages { /* new in WO4 */
611 static NSArray *defLangs = nil;
614 NSMutableArray *languages;
618 languages = [NSMutableArray arrayWithCapacity:8];
620 e = [[self headersForKey:@"accept-language"] objectEnumerator];
621 while ((hheader = [e nextObject])) {
624 le = [[hheader componentsSeparatedByString:@","] objectEnumerator];
625 while ((language = [le nextObject])) {
629 /* split off the quality (eg 'en;0.96') */
630 r = [language rangeOfString:@";"];
632 language = [language substringToIndex:r.location];
633 language = [language stringByTrimmingSpaces];
636 if ((tmp = [self languageForBrowserLanguageCode:language]))
639 if ([languages containsObject:language])
642 [languages addObject:language];
646 if ((tmp = [self _languageFromUserAgent]))
647 [languages addObject:tmp];
649 if (defLangs == nil) {
650 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
651 defLangs = [[ud arrayForKey:@"WODefaultLanguages"] copy];
653 [languages addObjectsFromArray:defLangs];
655 //[self debugWithFormat:@"languages: %@", languages];
656 return [[languages copy] autorelease];
661 - (NSArray *)cookieValuesForKey:(NSString *)_key {
662 NSEnumerator *ecookies;
663 NSMutableArray *values;
666 values = [NSMutableArray arrayWithCapacity:8];
668 ecookies = [[self cookies] objectEnumerator];
669 while ((cookie = [ecookies nextObject])) {
670 if ([_key isEqualToString:[cookie name]])
671 [values addObject:[cookie value]];
677 - (NSString *)cookieValueForKey:(NSString *)_key {
678 NSEnumerator *ecookies;
681 ecookies = [[self cookies] objectEnumerator];
682 while ((cookie = [ecookies nextObject])) {
683 if ([_key isEqualToString:[cookie name]])
684 return [cookie value];
689 - (NSDictionary *)cookieValues {
690 NSEnumerator *ecookies;
691 NSMutableDictionary *values;
694 values = [NSMutableDictionary dictionaryWithCapacity:8];
696 ecookies = [[self cookies] objectEnumerator];
697 while ((cookie = [ecookies nextObject])) {
699 NSMutableArray *vArray;
701 name = [cookie name];
702 vArray = [values objectForKey:name];
705 vArray = [[NSMutableArray alloc] initWithCapacity:8];
706 [values setObject:vArray forKey:name];
710 [vArray addObject:[cookie value]];
718 - (BOOL)isDebuggingEnabled {
721 - (NSString *)loggingPrefix {
722 return [NSString stringWithFormat:@"|Rq:%@ 0x%08X|",
723 [self method], self];
728 - (NSString *)description {
729 NSMutableString *str;
731 str = [NSMutableString stringWithCapacity:256];
732 [str appendFormat:@"<%@[0x%08X]:", NSStringFromClass([self class]), self];
733 [str appendFormat:@" method=%@", [self method]];
734 [str appendFormat:@" uri=%@", [self uri]];
735 [str appendFormat:@" app=%@", self->appName];
736 [str appendFormat:@" rqKey=%@", [self requestHandlerKey]];
737 [str appendFormat:@" rqPath=%@", [self requestHandlerPath]];
738 [str appendString:@">"];