2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
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 [self errorWithFormat:@"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 [self warnWithFormat:
107 @"did not register browser language mappings: %@", apath];
118 uriLen = [self->_uri cStringLength];
120 uriBuf = uri = malloc(uriLen + 1);
121 [self->_uri getCString:uriBuf]; uriBuf[uriLen] = '\0';
123 /* determine adaptor prefix */
125 if ((serverUrl = [self headerForKey:@"x-webobjects-adaptor-prefix"]))
126 self->adaptorPrefix = [serverUrl copyWithZone:[self zone]];
128 if (self->adaptorPrefix == nil)
129 self->adaptorPrefix = @"";
133 const char *start = NULL;
135 /* skip adaptor prefix */
136 if (self->adaptorPrefix)
137 uri += [self->adaptorPrefix cStringLength];
138 if (*uri == '\0') goto done;
140 /* parse application name */
144 while ((*uri != '\0') && (*uri != '/') && (*uri != '.'))
149 [[NSString alloc] initWithCString:start length:(uri - start)];
152 else if (*uri == '.') {
154 [[NSString alloc] initWithCString:start length:(uri - start)];
156 // skip appname trailer (eg .woa)
157 while ((*uri != '\0') && (*uri != '/'))
159 if (*uri == '\0') goto done;
162 else if (*uri == '/') {
164 [[NSString alloc] initWithCString:start length:(uri - start)];
168 goto done; // invalid state !
170 if (*uri == '\0') goto done;
172 /* parse request handler key */
175 while ((*uri != '\0') && (*uri != '/') && (*uri != '?'))
177 self->requestHandlerKey =
178 [[NSString alloc] initWithCString:start length:(uri - start)];
179 if (*uri == '\0') goto done;
182 /* parse request handler path */
185 while (*uri != '\0' && (*uri != '?'))
187 self->requestHandlerPath =
188 [[NSString alloc] initWithCString:start length:(uri - start)];
191 /* parsing done (found '\0') */
193 ; // required for MacOSX-S
194 if (uriBuf) free(uriBuf);
198 - (id)initWithMethod:(NSString *)_method
199 uri:(NSString *)__uri
200 httpVersion:(NSString *)_version
201 headers:(NSDictionary *)_headers
202 content:(NSData *)_body
203 userInfo:(NSDictionary *)_userInfo
205 if ((self = [super init])) {
206 self->_uri = [__uri copy];
207 self->method = [_method copy];
211 [self setHTTPVersion:_version];
212 [self setContent:_body];
213 [self setUserInfo:_userInfo];
214 [self setHeaders:_headers];
220 [self->startDate release];
221 [self->startStatistics release];
222 [self->method release];
223 [self->_uri release];
224 [self->adaptorPrefix release];
225 [self->requestHandlerKey release];
226 [self->requestHandlerPath release];
227 [self->appName release];
228 [self->formContent release];
229 [self->request release];
235 - (void)_setHttpRequest:(NGHttpRequest *)_request {
236 ASSIGN(self->request, _request);
238 - (NGHttpRequest *)httpRequest {
239 if (self->request == nil) {
240 /* construct request 'on-demand' */
242 [[NSClassFromString(@"NGHttpRequest") alloc] initWithWORequest:self];
244 return self->request;
247 /* request handler */
249 - (void)setRequestHandlerKey:(NSString *)_key {
250 ASSIGNCOPY(self->requestHandlerKey, _key);
252 - (NSString *)requestHandlerKey { // new in WO4
253 if ([self isProxyRequest])
255 return self->requestHandlerKey;
258 - (void)setRequestHandlerPath:(NSString *)_path {
259 ASSIGNCOPY(self->requestHandlerPath, _path);
261 - (NSString *)requestHandlerPath { // new in WO4
262 return self->requestHandlerPath;
265 - (NSArray *)requestHandlerPathArray { // new in WO4
266 NSMutableArray *array = nil;
271 clen = [self->requestHandlerPath cStringLength];
275 cstrBuf = cstr = malloc(clen + 1);
276 [self->requestHandlerPath getCString:cstrBuf]; cstrBuf[clen] = '\0';
279 NSString *component = nil;
280 register char *tmp = cstr;
282 while ((*tmp != '\0') && (*tmp != '?') && (*tmp != '/'))
285 component = ((tmp - cstr) == 0)
287 : [[NSString alloc] initWithCString:cstr length:(tmp - cstr)];
290 if (array == nil) array = [NSMutableArray arrayWithCapacity:64];
291 [array addObject:component];
292 [component release]; component = nil;
296 if (*cstr == '/') cstr++; // skip '/'
298 while ((*cstr != '\0') && (*cstr != '?'));
301 return [[array copy] autorelease];
306 - (BOOL)isFromClientComponent {
310 - (NSString *)sessionID { // deprecated in WO4
311 return [self cookieValueForKey:self->appName];
313 - (NSString *)senderID { // deprecated in WO4
315 return [[[WOApplication application] context] senderID];
318 - (NSString *)contextID {
319 return [[[WOApplication application] context] contextID];
320 //return self->contextID;
323 - (NSString *)applicationName {
324 return self->appName;
326 - (NSString *)applicationHost {
327 return [[NSHost currentHost] name];
330 - (NSString *)adaptorPrefix {
331 return self->adaptorPrefix;
334 - (NSString *)method {
337 - (void)_hackSetURI:(NSString *)_vuri {
338 /* be careful, used by the WebDAV dispatcher for ZideLook range queries */
339 ASSIGNCOPY(self->_uri, _vuri);
344 - (BOOL)isProxyRequest {
345 return [[self uri] isAbsoluteURL];
348 - (void)setStartDate:(NSCalendarDate *)_startDate {
349 ASSIGNCOPY(self->startDate, _startDate);
351 - (NSCalendarDate *)startDate {
352 return self->startDate;
354 - (id)startStatistics {
355 return self->startStatistics;
360 - (NSStringEncoding)formValueEncoding {
361 return NSUTF8StringEncoding;
364 - (void)setDefaultFormValueEncoding:(NSStringEncoding)_enc {
365 if (_enc != NSUTF8StringEncoding || _enc != NSASCIIStringEncoding)
366 [self notImplemented:_cmd];
368 - (NSStringEncoding)defaultFormValueEncoding {
369 return NSUTF8StringEncoding;
372 - (void)setFormValueEncodingDetectionEnabled:(BOOL)_flag {
373 if (_flag) [self notImplemented:_cmd];
375 - (BOOL)isFormValueEncodingDetectionEnabled {
379 - (void)_parseQueryParameters:(NSString *)_s intoMap:(NGMutableHashMap *)_map {
383 e = [[_s componentsSeparatedByString:@"&"] objectEnumerator];
384 while ((part = [e nextObject])) {
386 NSString *key, *value;
388 r = [part rangeOfString:@"="];
390 /* missing value of query parameter */
391 key = [part stringByUnescapingURL];
395 key = [[part substringToIndex:r.location] stringByUnescapingURL];
396 value = [[part substringFromIndex:(r.location + r.length)]
397 stringByUnescapingURL];
400 [self->formContent addObject:value forKey:key];
404 - (NGHashMap *)_getFormParameters {
405 if (self->formContent)
406 return self->formContent;
408 if (self->request == nil) {
410 TODO: add parsing of form values
413 a/blah?name=login&pwd=j
416 Content-Type: application/x-www-form-urlencoded
417 browserconfig=%7BisJavaScriptEnabled%3DYES%3B%7D&login=r&button=login
422 BOOL isMultiPartContent = NO, isFormContent = NO;
424 r = [self->_uri rangeOfString:@"?"];
425 query = (r.length > 0)
426 ? [self->_uri substringFromIndex:(r.location + r.length)]
429 if ((ctype = [self headerForKey:@"content-type"]) != nil) {
430 isFormContent = [ctype hasPrefix:@"application/x-www-form-urlencoded"];
432 isMultiPartContent = [ctype hasPrefix:@"multipart/form-data"];
435 if (query != nil || isFormContent || isMultiPartContent) {
436 NSAutoreleasePool *pool;
438 pool = [[NSAutoreleasePool alloc] init];
439 self->formContent = [[NGMutableHashMap alloc] init];
441 /* parse query string */
443 [self _parseQueryParameters:query intoMap:self->formContent];
445 /* parse content (if form content) */
447 [self _parseQueryParameters:[self contentAsString]
448 intoMap:self->formContent];
450 else if (isMultiPartContent) {
451 [self errorWithFormat:@"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]])
504 [self errorWithFormat:@"(%s): don't know how to deal with form object: %@",
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 warnWithFormat:@"did not find browser language mappings!"];
526 _e = [_e lowercaseString];
528 lang = [langMap objectForKey:_e];
529 if (lang == nil && [_e length] > 2) {
530 /* process constructs like 'de-ch' */
531 if ([_e characterAtIndex:2] == '-') {
534 ek = [_e substringToIndex:2];
535 lang = [langMap objectForKey:ek];
538 if (lang == nil && ![_e isEqualToString:@"*"]) {
539 [self debugWithFormat:@"did not find '%@' in map: %@",
540 _e, [[langMap allKeys] componentsJoinedByString:@", "]];
545 - (NSString *)_languageFromUserAgent {
547 user-agent sometimes stores the browser-language,
548 eg: Opera/5.0 (Linux 2.2.18 i686; U) [en]
554 if ((ua = [self headerForKey:@"user-agent"]) == nil)
557 rng = [ua rangeOfString:@"["];
561 tmp = [ua substringFromIndex:(rng.location + rng.length)];
562 rng = [tmp rangeOfString:@"]"];
564 tmp = [tmp substringToIndex:rng.location];
566 return [self languageForBrowserLanguageCode:tmp];
569 - (NSArray *)browserLanguages { /* new in WO4 */
570 static NSArray *defLangs = nil;
573 NSMutableArray *languages;
577 languages = [NSMutableArray arrayWithCapacity:8];
579 e = [[self headersForKey:@"accept-language"] objectEnumerator];
580 while ((hheader = [e nextObject])) {
583 le = [[hheader componentsSeparatedByString:@","] objectEnumerator];
584 while ((language = [le nextObject])) {
588 /* split off the quality (eg 'en;0.96') */
589 r = [language rangeOfString:@";"];
591 language = [language substringToIndex:r.location];
592 language = [language stringByTrimmingSpaces];
595 if ((tmp = [self languageForBrowserLanguageCode:language]))
598 if ([languages containsObject:language])
601 [languages addObject:language];
605 if ((tmp = [self _languageFromUserAgent]))
606 [languages addObject:tmp];
608 if (defLangs == nil) {
609 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
610 defLangs = [[ud arrayForKey:@"WODefaultLanguages"] copy];
612 [languages addObjectsFromArray:defLangs];
614 //[self debugWithFormat:@"languages: %@", languages];
615 return [[languages copy] autorelease];
620 - (NSArray *)cookieValuesForKey:(NSString *)_key {
621 NSEnumerator *ecookies;
622 NSMutableArray *values;
625 values = [NSMutableArray arrayWithCapacity:8];
627 ecookies = [[self cookies] objectEnumerator];
628 while ((cookie = [ecookies nextObject])) {
629 if ([_key isEqualToString:[cookie name]])
630 [values addObject:[cookie value]];
636 - (NSString *)cookieValueForKey:(NSString *)_key {
637 NSEnumerator *ecookies;
640 ecookies = [[self cookies] objectEnumerator];
641 while ((cookie = [ecookies nextObject])) {
642 if ([_key isEqualToString:[cookie name]])
643 return [cookie value];
648 - (NSDictionary *)cookieValues {
649 NSEnumerator *ecookies;
650 NSMutableDictionary *values;
653 values = [NSMutableDictionary dictionaryWithCapacity:8];
655 ecookies = [[self cookies] objectEnumerator];
656 while ((cookie = [ecookies nextObject])) {
658 NSMutableArray *vArray;
660 name = [cookie name];
661 vArray = [values objectForKey:name];
664 vArray = [[NSMutableArray alloc] initWithCapacity:8];
665 [values setObject:vArray forKey:name];
669 [vArray addObject:[cookie value]];
677 - (BOOL)isDebuggingEnabled {
680 - (NSString *)loggingPrefix {
681 return [NSString stringWithFormat:@"|Rq:%@ 0x%08X|",
682 [self method], self];
687 - (NSString *)description {
688 NSMutableString *str;
690 str = [NSMutableString stringWithCapacity:256];
691 [str appendFormat:@"<%@[0x%08X]:", NSStringFromClass([self class]), self];
692 [str appendFormat:@" method=%@", [self method]];
693 [str appendFormat:@" uri=%@", [self uri]];
694 [str appendFormat:@" app=%@", self->appName];
695 [str appendFormat:@" rqKey=%@", [self requestHandlerKey]];
696 [str appendFormat:@" rqPath=%@", [self requestHandlerPath]];
697 [str appendString:@">"];