]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WORequest.m
fixed makefiles to search FHS locations at the last resort
[sope] / sope-appserver / NGObjWeb / WORequest.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
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     NSLog(@"ERROR: 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     NSLog(@"WARNING: did not register browser language mappings: %@", apath);
107 }
108
109 /* parse URI */
110
111 - (void)_parseURI {
112   unsigned uriLen;
113   char     *uriBuf;
114   char     *uri;
115   NSString *serverUrl;
116
117   uriLen = [self->_uri cStringLength];
118   
119   uriBuf = uri = malloc(uriLen + 1);
120   [self->_uri getCString:uriBuf]; uriBuf[uriLen] = '\0';
121   
122   /* determine adaptor prefix */
123
124   if ((serverUrl = [self headerForKey:@"x-webobjects-adaptor-prefix"]))
125     self->adaptorPrefix = [serverUrl copyWithZone:[self zone]];
126   
127   if (self->adaptorPrefix == nil)
128     self->adaptorPrefix = @"";
129
130     /* new parse */
131   if (uri) {
132     const char *start = NULL;
133       
134     /* skip adaptor prefix */
135     if (self->adaptorPrefix)
136       uri += [self->adaptorPrefix cStringLength];
137     if (*uri == '\0') goto done;
138
139     /* parse application name */
140       
141     uri++; // skip '/'
142     start = uri;
143     while ((*uri != '\0') && (*uri != '/') && (*uri != '.'))
144       uri++;
145
146     if (*uri == '\0') {
147       self->appName =
148         [[NSString alloc] initWithCString:start length:(uri - start)];
149       goto done;
150     }
151     else if (*uri == '.') {
152       self->appName =
153         [[NSString alloc] initWithCString:start length:(uri - start)];
154
155       // skip appname trailer (eg .woa)
156       while ((*uri != '\0') && (*uri != '/'))
157         uri++;
158       if (*uri == '\0') goto done;
159       uri++; // skip '/'
160     }
161     else if (*uri == '/') {
162       self->appName =
163         [[NSString alloc] initWithCString:start length:(uri - start)];
164       uri++; // skip '/'
165     }
166     else
167       goto done; // invalid state !
168
169     if (*uri == '\0') goto done;
170     
171     /* parse request handler key */
172     
173     start = uri;
174     while ((*uri != '\0') && (*uri != '/') && (*uri != '?'))
175       uri++;
176     self->requestHandlerKey =
177       [[NSString alloc] initWithCString:start length:(uri - start)];
178     if (*uri == '\0') goto done;
179     if(*uri == '/'){
180       uri++; // skip '/'
181       /* parse request handler path */
182       
183       start = uri;
184       while (*uri != '\0' && (*uri != '?'))
185         uri++;
186       self->requestHandlerPath =
187         [[NSString alloc] initWithCString:start length:(uri - start)];
188     }
189     
190     /* parsing done (found '\0') */
191   done:
192     ; // required for MacOSX-S
193     if (uriBuf) free(uriBuf);
194   }
195 }
196
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
203 {
204   if ((self = [super init])) {
205     self->_uri   = [__uri   copy];
206     self->method = [_method copy];
207     [self _parseURI];
208     
209     /* WOMessage */
210     [self setHTTPVersion:_version];
211     [self setContent:_body];
212     [self setUserInfo:_userInfo];
213     [self setHeaders:_headers];
214   }
215   return self;
216 }
217
218 - (void)dealloc {
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];
229   [super dealloc];
230 }
231
232 /* privates */
233
234 - (void)_setHttpRequest:(NGHttpRequest *)_request {
235   ASSIGN(self->request, _request);
236 }
237 - (NGHttpRequest *)httpRequest {
238   if (self->request == nil) {
239     /* construct request 'on-demand' */
240     self->request =
241       [[NSClassFromString(@"NGHttpRequest") alloc] initWithWORequest:self];
242   }
243   return self->request;
244 }
245
246 /* request handler */
247
248 - (void)setRequestHandlerKey:(NSString *)_key {
249   ASSIGNCOPY(self->requestHandlerKey, _key);
250 }
251 - (NSString *)requestHandlerKey { // new in WO4
252   if ([self isProxyRequest])
253     return @"proxy";
254   return self->requestHandlerKey;
255 }
256
257 - (void)setRequestHandlerPath:(NSString *)_path {
258   ASSIGNCOPY(self->requestHandlerPath, _path);
259 }
260 - (NSString *)requestHandlerPath { // new in WO4
261   return self->requestHandlerPath;
262 }
263
264 - (NSArray *)requestHandlerPathArray { // new in WO4
265   NSMutableArray *array = nil;
266   unsigned       clen;
267   char           *cstrBuf;
268   register char  *cstr;
269   
270   clen   = [self->requestHandlerPath cStringLength];
271   if (clen == 0)
272     return nil;
273   
274   cstrBuf = cstr = malloc(clen + 1);
275   [self->requestHandlerPath getCString:cstrBuf]; cstrBuf[clen] = '\0';
276   
277   do {
278     NSString *component = nil;
279     register char *tmp = cstr;
280
281     while ((*tmp != '\0') && (*tmp != '?') && (*tmp != '/'))
282       tmp++;
283     
284     component = ((tmp - cstr) == 0)
285       ? @""
286       : [[NSString alloc] initWithCString:cstr length:(tmp - cstr)];
287
288     if (component) {
289       if (array == nil) array = [NSMutableArray arrayWithCapacity:64];
290       [array addObject:component];
291       [component release]; component = nil;
292     }
293
294     cstr = tmp;
295     if (*cstr == '/') cstr++; // skip '/'
296   }
297   while ((*cstr != '\0') && (*cstr != '?'));
298
299   free(cstrBuf);
300   return [[array copy] autorelease];
301 }
302
303 /* WO methods */
304
305 - (BOOL)isFromClientComponent {
306   return NO;
307 }
308
309 - (NSString *)sessionID { // deprecated in WO4
310   return [self cookieValueForKey:self->appName];
311 }
312 - (NSString *)senderID { // deprecated in WO4
313   IS_DEPRECATED;
314   return [[[WOApplication application] context] senderID];
315 }
316
317 - (NSString *)contextID {
318   return [[[WOApplication application] context] contextID];
319   //return self->contextID;
320 }
321
322 - (NSString *)applicationName {
323   return self->appName;
324 }
325 - (NSString *)applicationHost {
326   return [[NSHost currentHost] name];
327 }
328
329 - (NSString *)adaptorPrefix {
330   return self->adaptorPrefix;
331 }
332
333 - (NSString *)method {
334   return self->method;
335 }
336 - (void)_hackSetURI:(NSString *)_vuri {
337   /* be careful, used by the WebDAV dispatcher for ZideLook range queries */
338   ASSIGNCOPY(self->_uri, _vuri);
339 }
340 - (NSString *)uri {
341   return self->_uri;
342 }
343 - (BOOL)isProxyRequest {
344   return [[self uri] isAbsoluteURL];
345 }
346
347 - (void)setStartDate:(NSCalendarDate *)_startDate {
348   ASSIGNCOPY(self->startDate, _startDate);
349 }
350 - (NSCalendarDate *)startDate {
351   return self->startDate;
352 }
353 - (id)startStatistics {
354   return self->startStatistics;
355 }
356
357 /* forms */
358
359 - (NSStringEncoding)formValueEncoding {
360   return NSUTF8StringEncoding;
361 }
362
363 - (void)setDefaultFormValueEncoding:(NSStringEncoding)_enc {
364   if (_enc != NSUTF8StringEncoding || _enc != NSASCIIStringEncoding)
365     [self notImplemented:_cmd];
366 }
367 - (NSStringEncoding)defaultFormValueEncoding {
368   return NSUTF8StringEncoding;
369 }
370
371 - (void)setFormValueEncodingDetectionEnabled:(BOOL)_flag {
372   if (_flag) [self notImplemented:_cmd];
373 }
374 - (BOOL)isFormValueEncodingDetectionEnabled {
375   return NO;
376 }
377
378 - (void)_parseQueryParameters:(NSString *)_s intoMap:(NGMutableHashMap *)_map {
379   NSEnumerator *e;
380   NSString *part;
381   
382   e = [[_s componentsSeparatedByString:@"&"] objectEnumerator];
383   while ((part = [e nextObject])) {
384     NSRange  r;
385     NSString *key, *value;
386           
387     r = [part rangeOfString:@"="];
388     if (r.length == 0) {
389       /* missing value of query parameter */
390       key   = [part stringByUnescapingURL];
391       value = @"1";
392     }
393     else {
394       key   = [[part substringToIndex:r.location] stringByUnescapingURL];
395       value = [[part substringFromIndex:(r.location + r.length)] 
396                      stringByUnescapingURL];
397     }
398     
399     [self->formContent addObject:value forKey:key];
400   }
401 }
402
403 - (NGHashMap *)_getFormParameters {
404   if (self->formContent) 
405     return self->formContent;
406   
407   if (self->request == nil) {
408     /*
409       TODO: add parsing of form values
410       
411       contained in URL:
412         a/blah?name=login&pwd=j
413       
414       contained in body:
415         Content-Type: application/x-www-form-urlencoded
416         browserconfig=%7BisJavaScriptEnabled%3DYES%3B%7D&login=r&button=login
417     */
418     NSRange  r;
419     NSString *query;
420     NSString *ctype;
421     BOOL     isMultiPartContent = NO, isFormContent = NO;
422     
423     r = [self->_uri rangeOfString:@"?"];
424     query = (r.length > 0)
425       ? [self->_uri substringFromIndex:(r.location + r.length)]
426       : nil;
427     
428     if ((ctype = [self headerForKey:@"content-type"]) != nil) {
429       isFormContent = [ctype hasPrefix:@"application/x-www-form-urlencoded"];
430       if (!isFormContent)
431         isMultiPartContent = [ctype hasPrefix:@"multipart/form-data"];
432     }
433     
434     if (query != nil || isFormContent || isMultiPartContent) {
435       NSAutoreleasePool *pool;
436       
437       pool = [[NSAutoreleasePool alloc] init];
438       self->formContent = [[NGMutableHashMap alloc] init];
439       
440       /* parse query string */
441       if (query)
442         [self _parseQueryParameters:query intoMap:self->formContent];
443       
444       /* parse content (if form content) */
445       if (isFormContent) {
446         [self _parseQueryParameters:[self contentAsString]
447               intoMap:self->formContent];
448       }
449       else if (isMultiPartContent) {
450         [self logWithFormat:
451                 @"ERROR: 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 logWithFormat:
505           @"ERROR(%s): don't know how to deal with form object: %@", 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 debugWithFormat:
523               @"WARNING: did not find browser language mappings!"];
524     }
525   }
526   
527   _e = [_e lowercaseString];
528   
529   lang = [langMap objectForKey:_e];
530   if (lang == nil && [_e length] > 2) {
531     /* process constructs like 'de-ch' */
532     if ([_e characterAtIndex:2] == '-') {
533       NSString *ek;
534       
535       ek = [_e substringToIndex:2];
536       lang = [langMap objectForKey:ek];
537     }
538   }
539   if (lang == nil && ![_e isEqualToString:@"*"]) {
540     [self debugWithFormat:@"did not find '%@' in map: %@", 
541             _e, [[langMap allKeys] componentsJoinedByString:@", "]];
542   }
543   return lang;
544 }
545
546 - (NSString *)_languageFromUserAgent {
547   /*
548     user-agent sometimes stores the browser-language,
549     eg: Opera/5.0 (Linux 2.2.18 i686; U)  [en]
550   */
551   NSString *ua;
552   NSRange  rng;
553   NSString *tmp;
554   
555   if ((ua = [self headerForKey:@"user-agent"]) == nil)
556     return nil;
557
558   rng = [ua rangeOfString:@"["];
559   if (rng.length == 0)
560     return nil;
561       
562   tmp = [ua substringFromIndex:(rng.location + rng.length)];
563   rng = [tmp rangeOfString:@"]"];
564   if (rng.length > 0)
565     tmp = [tmp substringToIndex:rng.location];
566
567   return [self languageForBrowserLanguageCode:tmp];
568 }
569
570 - (NSArray *)browserLanguages { /* new in WO4 */
571   static NSArray *defLangs = nil;
572   NSString       *hheader;
573   NSEnumerator   *e;
574   NSMutableArray *languages;
575   NSString       *language;
576   NSString       *tmp;
577   
578   languages = [NSMutableArray arrayWithCapacity:8];
579   
580   e = [[self headersForKey:@"accept-language"] objectEnumerator];
581   while ((hheader = [e nextObject])) {
582     NSEnumerator *le;
583     
584     le = [[hheader componentsSeparatedByString:@","] objectEnumerator];
585     while ((language = [le nextObject])) {
586       NSString *tmp;
587       NSRange  r;
588       
589       /* split off the quality (eg 'en;0.96') */
590       r = [language rangeOfString:@";"];
591       if (r.length > 0)
592         language = [language substringToIndex:r.location];
593       language = [language stringByTrimmingSpaces];
594       
595       /* check in map */
596       if ((tmp = [self languageForBrowserLanguageCode:language]))
597         language = tmp;
598       
599       if ([languages containsObject:language])
600         continue;
601       
602       [languages addObject:language];
603     }
604   }
605   
606   if ((tmp = [self _languageFromUserAgent]))
607     [languages addObject:tmp];
608   
609   if (defLangs == nil) {
610     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
611     defLangs = [[ud arrayForKey:@"WODefaultLanguages"] copy];
612   }
613   [languages addObjectsFromArray:defLangs];
614   
615   //[self debugWithFormat:@"languages: %@", languages];
616   return [[languages copy] autorelease];
617 }
618
619 /* cookies */
620
621 - (NSArray *)cookieValuesForKey:(NSString *)_key {
622   NSEnumerator   *ecookies;
623   NSMutableArray *values;
624   WOCookie       *cookie;
625   
626   values  = [NSMutableArray arrayWithCapacity:8];
627   
628   ecookies = [[self cookies] objectEnumerator];
629   while ((cookie = [ecookies nextObject])) {
630     if ([_key isEqualToString:[cookie name]])
631       [values addObject:[cookie value]];
632   }
633   
634   return values;
635 }
636
637 - (NSString *)cookieValueForKey:(NSString *)_key {
638   NSEnumerator *ecookies;
639   WOCookie     *cookie;
640   
641   ecookies = [[self cookies] objectEnumerator];
642   while ((cookie = [ecookies nextObject])) {
643     if ([_key isEqualToString:[cookie name]])
644       return [cookie value];
645   }
646   return nil;
647 }
648
649 - (NSDictionary *)cookieValues {
650   NSEnumerator        *ecookies;
651   NSMutableDictionary *values;
652   WOCookie            *cookie;
653   
654   values  = [NSMutableDictionary dictionaryWithCapacity:8];
655   
656   ecookies = [[self cookies] objectEnumerator];
657   while ((cookie = [ecookies nextObject])) {
658     NSString       *name;
659     NSMutableArray *vArray;
660     
661     name   = [cookie name];
662     vArray = [values objectForKey:name];
663     
664     if (vArray == nil) {
665       vArray = [[NSMutableArray alloc] initWithCapacity:8];
666       [values setObject:vArray forKey:name];
667       [vArray release];
668     }
669     
670     [vArray addObject:[cookie value]];
671   }
672   
673   return values;
674 }
675
676 /* logging */
677
678 - (BOOL)isDebuggingEnabled {
679   return debugOn;
680 }
681 - (NSString *)loggingPrefix {
682   return [NSString stringWithFormat:@"|Rq:%@ 0x%08X|", 
683                      [self method], self];
684 }
685
686 /* description */
687
688 - (NSString *)description {
689   NSMutableString *str;
690
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:@">"];
699   return str;
700 }
701
702 @end /* WORequest */