]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WORequest.m
Revisited all copied resources in all Xcode projects. Ensured that all README, COPYIN...
[sope] / sope-appserver / NGObjWeb / WORequest.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
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>
31 #include <time.h>
32 #include "common.h"
33
34 #if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY
35 @interface NSObject(Miss)
36 - (id)notImplemented:(SEL)cmd;
37 @end
38 #endif
39
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";
47
48 @interface WOCoreApplication(Resources)
49 + (NSString *)findNGObjWebResource:(NSString *)_name ofType:(NSString *)_ext;
50 @end
51
52 @implementation WORequest
53
54 static BOOL debugOn = NO;
55
56 + (int)version {
57   return [super version] + 2 /* v7 */;
58 }
59
60 + (void)initialize {
61   static BOOL isInitialized = NO;
62   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
63   NSDictionary   *langMap;
64   NSString       *apath;
65
66   if (isInitialized) return;
67   isInitialized = YES;
68   
69   NSAssert2([super version] == 5,
70             @"invalid superclass (%@) version %i !",
71             NSStringFromClass([self superclass]), [super version]);
72   
73   debugOn = [WOApplication isDebuggingEnabled];
74     
75   /* apply defaults on some globals ... */
76     
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];
86   
87   /* load language mappings */
88     
89   apath = [WOApplication findNGObjWebResource:@"Languages" ofType:@"plist"];
90   if (apath == nil) {
91     [self errorWithFormat:@"cannot find Languages.plist resource "
92             @"of NGObjWeb library !"];
93     langMap = nil;
94   }
95   else
96     langMap = [NSDictionary dictionaryWithContentsOfFile:apath];
97   
98   if (langMap != nil) {
99     NSDictionary *defs;
100     
101     defs = [NSDictionary dictionaryWithObject:langMap
102                          forKey:@"WOBrowserLanguageMappings"];
103     [ud registerDefaults:defs];
104   }
105   else
106     [self warnWithFormat:
107             @"did not register browser language mappings: %@", apath];
108 }
109
110 /* parse URI */
111
112 - (void)_parseURI {
113   unsigned uriLen;
114   char     *uriBuf;
115   char     *uri;
116   NSString *serverUrl;
117
118   uriLen = [self->_uri cStringLength];
119   
120   uriBuf = uri = malloc(uriLen + 1);
121   [self->_uri getCString:uriBuf]; uriBuf[uriLen] = '\0';
122   
123   /* determine adaptor prefix */
124
125   if ((serverUrl = [self headerForKey:@"x-webobjects-adaptor-prefix"]))
126     self->adaptorPrefix = [serverUrl copyWithZone:[self zone]];
127   
128   if (self->adaptorPrefix == nil)
129     self->adaptorPrefix = @"";
130
131     /* new parse */
132   if (uri) {
133     const char *start = NULL;
134       
135     /* skip adaptor prefix */
136     if (self->adaptorPrefix)
137       uri += [self->adaptorPrefix cStringLength];
138     if (*uri == '\0') goto done;
139
140     /* parse application name */
141       
142     uri++; // skip '/'
143     start = uri;
144     while ((*uri != '\0') && (*uri != '/') && (*uri != '.'))
145       uri++;
146
147     if (*uri == '\0') {
148       self->appName =
149         [[NSString alloc] initWithCString:start length:(uri - start)];
150       goto done;
151     }
152     else if (*uri == '.') {
153       self->appName =
154         [[NSString alloc] initWithCString:start length:(uri - start)];
155
156       // skip appname trailer (eg .woa)
157       while ((*uri != '\0') && (*uri != '/'))
158         uri++;
159       if (*uri == '\0') goto done;
160       uri++; // skip '/'
161     }
162     else if (*uri == '/') {
163       self->appName =
164         [[NSString alloc] initWithCString:start length:(uri - start)];
165       uri++; // skip '/'
166     }
167     else
168       goto done; // invalid state !
169
170     if (*uri == '\0') goto done;
171     
172     /* parse request handler key */
173     
174     start = uri;
175     while ((*uri != '\0') && (*uri != '/') && (*uri != '?'))
176       uri++;
177     self->requestHandlerKey =
178       [[NSString alloc] initWithCString:start length:(uri - start)];
179     if (*uri == '\0') goto done;
180     if(*uri == '/'){
181       uri++; // skip '/'
182       /* parse request handler path */
183       
184       start = uri;
185       while (*uri != '\0' && (*uri != '?'))
186         uri++;
187       self->requestHandlerPath =
188         [[NSString alloc] initWithCString:start length:(uri - start)];
189     }
190     
191     /* parsing done (found '\0') */
192   done:
193     ; // required for MacOSX-S
194     if (uriBuf) free(uriBuf);
195   }
196 }
197
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
204 {
205   if ((self = [super init])) {
206     self->_uri   = [__uri   copy];
207     self->method = [_method copy];
208     [self _parseURI];
209     
210     /* WOMessage */
211     [self setHTTPVersion:_version];
212     [self setContent:_body];
213     [self setUserInfo:_userInfo];
214     [self setHeaders:_headers];
215   }
216   return self;
217 }
218
219 - (void)dealloc {
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];
230   [super dealloc];
231 }
232
233 /* privates */
234
235 - (void)_setHttpRequest:(NGHttpRequest *)_request {
236   ASSIGN(self->request, _request);
237 }
238 - (NGHttpRequest *)httpRequest {
239   if (self->request == nil) {
240     /* construct request 'on-demand' */
241     self->request =
242       [[NSClassFromString(@"NGHttpRequest") alloc] initWithWORequest:self];
243   }
244   return self->request;
245 }
246
247 /* request handler */
248
249 - (void)setRequestHandlerKey:(NSString *)_key {
250   ASSIGNCOPY(self->requestHandlerKey, _key);
251 }
252 - (NSString *)requestHandlerKey { // new in WO4
253   if ([self isProxyRequest])
254     return @"proxy";
255   return self->requestHandlerKey;
256 }
257
258 - (void)setRequestHandlerPath:(NSString *)_path {
259   ASSIGNCOPY(self->requestHandlerPath, _path);
260 }
261 - (NSString *)requestHandlerPath { // new in WO4
262   return self->requestHandlerPath;
263 }
264
265 - (NSArray *)requestHandlerPathArray { // new in WO4
266   NSMutableArray *array = nil;
267   unsigned       clen;
268   char           *cstrBuf;
269   register char  *cstr;
270   
271   clen   = [self->requestHandlerPath cStringLength];
272   if (clen == 0)
273     return nil;
274   
275   cstrBuf = cstr = malloc(clen + 1);
276   [self->requestHandlerPath getCString:cstrBuf]; cstrBuf[clen] = '\0';
277   
278   do {
279     NSString *component = nil;
280     register char *tmp = cstr;
281
282     while ((*tmp != '\0') && (*tmp != '?') && (*tmp != '/'))
283       tmp++;
284     
285     component = ((tmp - cstr) == 0)
286       ? @""
287       : [[NSString alloc] initWithCString:cstr length:(tmp - cstr)];
288
289     if (component) {
290       if (array == nil) array = [NSMutableArray arrayWithCapacity:64];
291       [array addObject:component];
292       [component release]; component = nil;
293     }
294
295     cstr = tmp;
296     if (*cstr == '/') cstr++; // skip '/'
297   }
298   while ((*cstr != '\0') && (*cstr != '?'));
299
300   free(cstrBuf);
301   return [[array copy] autorelease];
302 }
303
304 /* WO methods */
305
306 - (BOOL)isFromClientComponent {
307   return NO;
308 }
309
310 - (NSString *)sessionID { // deprecated in WO4
311   return [self cookieValueForKey:self->appName];
312 }
313 - (NSString *)senderID { // deprecated in WO4
314   IS_DEPRECATED;
315   return [[[WOApplication application] context] senderID];
316 }
317
318 - (NSString *)contextID {
319   return [[[WOApplication application] context] contextID];
320   //return self->contextID;
321 }
322
323 - (NSString *)applicationName {
324   return self->appName;
325 }
326 - (NSString *)applicationHost {
327   return [[NSHost currentHost] name];
328 }
329
330 - (NSString *)adaptorPrefix {
331   return self->adaptorPrefix;
332 }
333
334 - (NSString *)method {
335   return self->method;
336 }
337 - (void)_hackSetURI:(NSString *)_vuri {
338   /* be careful, used by the WebDAV dispatcher for ZideLook range queries */
339   ASSIGNCOPY(self->_uri, _vuri);
340 }
341 - (NSString *)uri {
342   return self->_uri;
343 }
344 - (BOOL)isProxyRequest {
345   return [[self uri] isAbsoluteURL];
346 }
347
348 - (void)setStartDate:(NSCalendarDate *)_startDate {
349   ASSIGNCOPY(self->startDate, _startDate);
350 }
351 - (NSCalendarDate *)startDate {
352   return self->startDate;
353 }
354 - (id)startStatistics {
355   return self->startStatistics;
356 }
357
358 /* forms */
359
360 - (NSStringEncoding)formValueEncoding {
361   return NSUTF8StringEncoding;
362 }
363
364 - (void)setDefaultFormValueEncoding:(NSStringEncoding)_enc {
365   if (_enc != NSUTF8StringEncoding || _enc != NSASCIIStringEncoding)
366     [self notImplemented:_cmd];
367 }
368 - (NSStringEncoding)defaultFormValueEncoding {
369   return NSUTF8StringEncoding;
370 }
371
372 - (void)setFormValueEncodingDetectionEnabled:(BOOL)_flag {
373   if (_flag) [self notImplemented:_cmd];
374 }
375 - (BOOL)isFormValueEncodingDetectionEnabled {
376   return NO;
377 }
378
379 - (void)_parseQueryParameters:(NSString *)_s intoMap:(NGMutableHashMap *)_map {
380   NSEnumerator *e;
381   NSString *part;
382   
383   e = [[_s componentsSeparatedByString:@"&"] objectEnumerator];
384   while ((part = [e nextObject])) {
385     NSRange  r;
386     NSString *key, *value;
387           
388     r = [part rangeOfString:@"="];
389     if (r.length == 0) {
390       /* missing value of query parameter */
391       key   = [part stringByUnescapingURL];
392       value = @"1";
393     }
394     else {
395       key   = [[part substringToIndex:r.location] stringByUnescapingURL];
396       value = [[part substringFromIndex:(r.location + r.length)] 
397                      stringByUnescapingURL];
398     }
399     
400     [self->formContent addObject:value forKey:key];
401   }
402 }
403
404 - (NGHashMap *)_getFormParameters {
405   if (self->formContent) 
406     return self->formContent;
407   
408   if (self->request == nil) {
409     /*
410       TODO: add parsing of form values
411       
412       contained in URL:
413         a/blah?name=login&pwd=j
414       
415       contained in body:
416         Content-Type: application/x-www-form-urlencoded
417         browserconfig=%7BisJavaScriptEnabled%3DYES%3B%7D&login=r&button=login
418     */
419     NSRange  r;
420     NSString *query;
421     NSString *ctype;
422     BOOL     isMultiPartContent = NO, isFormContent = NO;
423     
424     r = [self->_uri rangeOfString:@"?"];
425     query = (r.length > 0)
426       ? [self->_uri substringFromIndex:(r.location + r.length)]
427       : nil;
428     
429     if ((ctype = [self headerForKey:@"content-type"]) != nil) {
430       isFormContent = [ctype hasPrefix:@"application/x-www-form-urlencoded"];
431       if (!isFormContent)
432         isMultiPartContent = [ctype hasPrefix:@"multipart/form-data"];
433     }
434     
435     if (query != nil || isFormContent || isMultiPartContent) {
436       NSAutoreleasePool *pool;
437       
438       pool = [[NSAutoreleasePool alloc] init];
439       self->formContent = [[NGMutableHashMap alloc] init];
440       
441       /* parse query string */
442       if (query)
443         [self _parseQueryParameters:query intoMap:self->formContent];
444       
445       /* parse content (if form content) */
446       if (isFormContent) {
447         [self _parseQueryParameters:[self contentAsString]
448               intoMap:self->formContent];
449       }
450       else if (isMultiPartContent) {
451         [self errorWithFormat:@"missing NGHttpRequest, cannot parse multipart"];
452       }
453       
454       [pool release];
455     }
456     else
457       self->formContent = [[NGHashMap alloc] init];
458   }
459   else
460     self->formContent = [[self->request formParameters] retain];
461   return self->formContent;
462 }
463
464 - (NSArray *)formValueKeys {
465   id paras = [self _getFormParameters];
466   
467   if ([paras respondsToSelector:@selector(allKeys)])
468     return [paras allKeys];
469   
470   return nil;
471 }
472
473 - (NSString *)formValueForKey:(NSString *)_key {
474   NSString *value;
475   id paras;
476   
477   value = nil;
478   paras = [self _getFormParameters];
479   if ([paras respondsToSelector:@selector(objectForKey:)])
480     value = [(NSDictionary *)paras objectForKey:_key];
481   
482   return value;
483 }
484 - (NSArray *)formValuesForKey:(NSString *)_key {
485   id paras = [self _getFormParameters];
486   return [paras respondsToSelector:@selector(objectsForKey:)]
487     ? [paras objectsForKey:_key]
488     : nil;
489 }
490
491 - (NSDictionary *)formValues {
492   id paras;
493   
494   if ((paras = [self _getFormParameters]) == nil)
495     return nil;
496   
497   /* check class, could change with different HTTP adaptor */
498   
499   if ([paras isKindOfClass:[NGHashMap class]])
500     return [paras asDictionaryWithArraysForValues];
501   if ([paras isKindOfClass:[NSDictionary class]])
502     return paras;
503   
504   [self errorWithFormat:@"(%s): don't know how to deal with form object: %@",
505           paras];
506   return nil;
507 }
508
509 // ******************** Headers ******************
510
511 - (NSString *)languageForBrowserLanguageCode:(NSString *)_e {
512   static NSDictionary *langMap = nil;
513   NSString *lang;
514   
515   if (_e == nil) return nil;
516   
517   if (langMap == nil) {
518     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
519     
520     langMap = [[ud dictionaryForKey:@"WOBrowserLanguageMappings"] copy];
521     if (langMap == nil) {
522       [self warnWithFormat:@"did not find browser language mappings!"];
523     }
524   }
525   
526   _e = [_e lowercaseString];
527   
528   lang = [langMap objectForKey:_e];
529   if (lang == nil && [_e length] > 2) {
530     /* process constructs like 'de-ch' */
531     if ([_e characterAtIndex:2] == '-') {
532       NSString *ek;
533       
534       ek = [_e substringToIndex:2];
535       lang = [langMap objectForKey:ek];
536     }
537   }
538   if (lang == nil && ![_e isEqualToString:@"*"]) {
539     [self debugWithFormat:@"did not find '%@' in map: %@", 
540             _e, [[langMap allKeys] componentsJoinedByString:@", "]];
541   }
542   return lang;
543 }
544
545 - (NSString *)_languageFromUserAgent {
546   /*
547     user-agent sometimes stores the browser-language,
548     eg: Opera/5.0 (Linux 2.2.18 i686; U)  [en]
549   */
550   NSString *ua;
551   NSRange  rng;
552   NSString *tmp;
553   
554   if ((ua = [self headerForKey:@"user-agent"]) == nil)
555     return nil;
556
557   rng = [ua rangeOfString:@"["];
558   if (rng.length == 0)
559     return nil;
560       
561   tmp = [ua substringFromIndex:(rng.location + rng.length)];
562   rng = [tmp rangeOfString:@"]"];
563   if (rng.length > 0)
564     tmp = [tmp substringToIndex:rng.location];
565
566   return [self languageForBrowserLanguageCode:tmp];
567 }
568
569 - (NSArray *)browserLanguages { /* new in WO4 */
570   static NSArray *defLangs = nil;
571   NSString       *hheader;
572   NSEnumerator   *e;
573   NSMutableArray *languages;
574   NSString       *language;
575   NSString       *tmp;
576   
577   languages = [NSMutableArray arrayWithCapacity:8];
578   
579   e = [[self headersForKey:@"accept-language"] objectEnumerator];
580   while ((hheader = [e nextObject])) {
581     NSEnumerator *le;
582     
583     le = [[hheader componentsSeparatedByString:@","] objectEnumerator];
584     while ((language = [le nextObject])) {
585       NSString *tmp;
586       NSRange  r;
587       
588       /* split off the quality (eg 'en;0.96') */
589       r = [language rangeOfString:@";"];
590       if (r.length > 0)
591         language = [language substringToIndex:r.location];
592       language = [language stringByTrimmingSpaces];
593       
594       /* check in map */
595       if ((tmp = [self languageForBrowserLanguageCode:language]))
596         language = tmp;
597       
598       if ([languages containsObject:language])
599         continue;
600       
601       [languages addObject:language];
602     }
603   }
604   
605   if ((tmp = [self _languageFromUserAgent]))
606     [languages addObject:tmp];
607   
608   if (defLangs == nil) {
609     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
610     defLangs = [[ud arrayForKey:@"WODefaultLanguages"] copy];
611   }
612   [languages addObjectsFromArray:defLangs];
613   
614   //[self debugWithFormat:@"languages: %@", languages];
615   return [[languages copy] autorelease];
616 }
617
618 /* cookies */
619
620 - (NSArray *)cookieValuesForKey:(NSString *)_key {
621   NSEnumerator   *ecookies;
622   NSMutableArray *values;
623   WOCookie       *cookie;
624   
625   values  = [NSMutableArray arrayWithCapacity:8];
626   
627   ecookies = [[self cookies] objectEnumerator];
628   while ((cookie = [ecookies nextObject])) {
629     if ([_key isEqualToString:[cookie name]])
630       [values addObject:[cookie value]];
631   }
632   
633   return values;
634 }
635
636 - (NSString *)cookieValueForKey:(NSString *)_key {
637   NSEnumerator *ecookies;
638   WOCookie     *cookie;
639   
640   ecookies = [[self cookies] objectEnumerator];
641   while ((cookie = [ecookies nextObject])) {
642     if ([_key isEqualToString:[cookie name]])
643       return [cookie value];
644   }
645   return nil;
646 }
647
648 - (NSDictionary *)cookieValues {
649   NSEnumerator        *ecookies;
650   NSMutableDictionary *values;
651   WOCookie            *cookie;
652   
653   values  = [NSMutableDictionary dictionaryWithCapacity:8];
654   
655   ecookies = [[self cookies] objectEnumerator];
656   while ((cookie = [ecookies nextObject])) {
657     NSString       *name;
658     NSMutableArray *vArray;
659     
660     name   = [cookie name];
661     vArray = [values objectForKey:name];
662     
663     if (vArray == nil) {
664       vArray = [[NSMutableArray alloc] initWithCapacity:8];
665       [values setObject:vArray forKey:name];
666       [vArray release];
667     }
668     
669     [vArray addObject:[cookie value]];
670   }
671   
672   return values;
673 }
674
675 /* logging */
676
677 - (BOOL)isDebuggingEnabled {
678   return debugOn;
679 }
680 - (NSString *)loggingPrefix {
681   return [NSString stringWithFormat:@"|Rq:%@ 0x%08X|", 
682                      [self method], self];
683 }
684
685 /* description */
686
687 - (NSString *)description {
688   NSMutableString *str;
689
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:@">"];
698   return str;
699 }
700
701 @end /* WORequest */