]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SoObjects/SoObject.m
fixed various warnings
[sope] / sope-appserver / NGObjWeb / SoObjects / SoObject.m
1 /*
2   Copyright (C) 2002-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 "SoObject.h"
23 #include "SoClassRegistry.h"
24 #include "SoClass.h"
25 #include "SoSecurityManager.h"
26 #include "WOContext+SoObjects.h"
27 #include <EOControl/EOClassDescription.h>
28 #include <NGObjWeb/WOApplication.h>
29 #include <NGObjWeb/WORequest.h>
30 #include "common.h"
31
32 @interface NSObject(Folders)
33 - (BOOL)isFolderish;
34 @end
35
36 @implementation NSObject(SoObject)
37
38 static int debugLookup  = -1;
39 static int debugBaseURL = -1;
40 static void _initialize(void) {
41   if (debugLookup == -1) {
42     debugLookup = [[NSUserDefaults standardUserDefaults]
43                                    boolForKey:@"SoDebugKeyLookup"] ? 1 : 0;
44   }
45   if (debugBaseURL == -1) {
46     debugBaseURL = [[NSUserDefaults standardUserDefaults]
47                                      boolForKey:@"SoDebugBaseURL"] ? 1 : 0;
48   }
49 }
50
51 /* classes */
52
53 + (SoClass *)soClass {
54   static SoClassRegistry *registry = nil; // THREAD
55   if (registry == nil)
56     registry = [[SoClassRegistry sharedClassRegistry] retain];
57   return [registry soClassForClass:self];
58 }
59 - (SoClass *)soClass {
60   return [[self class] soClass];
61 }
62 - (NSString *)soClassName {
63   return [[self soClass] className];
64 }
65
66 + (SoClassSecurityInfo *)soClassSecurityInfo {
67   return [[self soClass] soClassSecurityInfo];
68 }
69
70 - (NSClassDescription *)soClassDescription {
71   return [[self soClass] soClassDescription];
72 }
73
74 /* invocation */
75
76 - (BOOL)isCallable {
77   return NO;
78 }
79 - (id)clientObject {
80   return self;
81 }
82
83 - (id)callOnObject:(id)_client inContext:(id)_ctx {
84   return nil;
85 }
86
87 - (NSString *)defaultMethodNameInContext:(id)_ctx {
88   return @"index";
89 }
90 - (id)lookupDefaultMethod {
91   id ctx = nil;
92   
93   // TODO: lookupDefaultMethod should be rewritten to take a context!
94   ctx = [[WOApplication application] context];
95
96   // TODO: we might want to return a redirect?!
97   
98   return [self lookupName:[self defaultMethodNameInContext:ctx]
99                inContext:ctx
100                acquire:YES];
101 }
102
103 /* keys */
104
105 - (BOOL)hasName:(NSString *)_key inContext:(id)_ctx {
106   /* this corresponds to Zope's/Pythons __hasattr__() */
107   if ([[self soClass] hasKey:_key inContext:_ctx])
108     return YES;
109   if ([[self toOneRelationshipKeys] containsObject:_key])
110     return YES;
111   return NO;
112 }
113
114 - (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag {
115   /* this corresponds to Zope's/Pythons __getattr__() */
116   id value;
117   _initialize();
118   
119   if (debugLookup)
120     [self debugWithFormat:@"lookup key '%@'", _key];
121   
122   /* we might want to cache class methods ? */
123   if ((value = [[self soClass] lookupKey:_key inContext:_ctx]) == nil) {
124     if (debugLookup) {
125       [self logWithFormat:@"  did not find key '%@' in SoClass: %@", 
126               _key, [self soClass]];
127     }
128     
129     if ([[self toOneRelationshipKeys] containsObject:_key]) {
130       if (debugLookup) {
131         [self logWithFormat:
132                 @"  %@ is a toOneRelationshipKey (use -valueForKey:)", _key];
133       }
134       value = [self valueForKey:_key];
135     }
136   }
137   
138   if (value) {
139     if ((value = [value bindToObject:self inContext:_ctx]) == nil) {
140       if (debugLookup)
141         [self logWithFormat:@"  value from class did not bind: %@", 
142               [self soClass]];
143       return nil;
144     }
145   }
146   else if (_flag) { /* try to acquire from container */
147     if (debugLookup)
148       [self logWithFormat:@"  try to acquire %@ from container ...", _key];
149     value = [[self container] lookupName:_key inContext:_ctx acquire:YES];
150   }
151   
152   if (debugLookup) [self logWithFormat:@"  looked up value: %@", value];
153   return value;
154 }
155
156 - (NSException *)validateName:(NSString *)_key inContext:(id)_ctx {
157   static SoSecurityManager *sm = nil;
158   if (sm == nil) sm = [[SoSecurityManager sharedSecurityManager] retain];
159   return [sm validateName:_key ofObject:self inContext:_ctx];
160 }
161
162 /* binding */
163
164 - (id)bindToObject:(id)_object inContext:(id)_ctx {
165   return self;
166 }
167
168 /* security */
169
170 - (NSString *)ownerInContext:(id)_ctx {
171   /* objects are not owned by default, suggest to inherit owner */
172   return [[self container] ownerInContext:_ctx];
173 }
174 - (id)authenticatorInContext:(id)_ctx {
175   return [[_ctx application] authenticatorInContext:_ctx];
176 }
177
178 /* containment */
179
180 - (id)container {
181   return nil;
182 }
183 - (void)detachFromContainer {
184 }
185 - (NSString *)nameInContainer {
186   return nil;
187 }
188
189 - (NSArray *)objectContainmentStack {
190   NSMutableArray *ma;
191   id object;
192   
193   if ((object = [self container]) == nil)
194     /* this is root */
195     return [NSArray arrayWithObject:self];
196   
197   ma = [[NSMutableArray alloc] initWithCapacity:16];
198   for (object = self; object; object = [object container])
199     [ma insertObject:(object ? object : (id)[NSNull null]) atIndex:0];
200
201   object = [ma shallowCopy];
202   [ma release];
203   return [object autorelease];
204 }
205
206 - (NSArray *)reversedPathArrayToSoObject {
207   NSMutableArray *ma;
208   id object, nextObject;
209   
210   if ((object = [self container]) == nil)
211     /* this is root */
212     return [NSArray array];
213   
214   ma = [NSMutableArray arrayWithCapacity:16];
215   for (object = self; (nextObject = [object container]); object = nextObject) {
216     NSString *oname;
217     
218     oname = [object nameInContainer];
219     [ma addObject:(oname ? oname : (id)[NSNull null])];
220   }
221   return ma;
222 }
223 - (NSArray *)pathArrayToSoObject {
224   NSArray      *pathArray;
225   NSEnumerator *e;
226   
227   if ((pathArray = [self reversedPathArrayToSoObject]) == nil)
228     return nil;
229   
230   e = [pathArray reverseObjectEnumerator];
231   pathArray = [[[NSArray alloc] initWithObjectsFromEnumerator:e] autorelease];
232   return pathArray;
233 }
234
235 - (NSString *)baseURLInContext:(id)_ctx {
236   NSString *baseURL;
237   id parent;
238   _initialize();
239   
240   // TODO: should we check the traversal path?
241   
242   if ((parent = [self container]) != nil) {
243     /* Note: cannot use -stringByAppendingPathComponent: on OSX! */
244     NSString *name;
245     
246     if (parent == self) {
247       [self warnWithFormat:
248               @"container==object in baseURL calculation (loop?): %@",
249               self];
250     }
251     
252     baseURL = [parent baseURLInContext:_ctx];
253     if (![baseURL hasSuffix:@"/"])
254       baseURL = [baseURL stringByAppendingString:@"/"];
255     
256     name    = [[self nameInContainer] stringByEscapingURL];
257     baseURL = [baseURL stringByAppendingString:name];
258     
259     if (debugBaseURL) {
260       [self logWithFormat:@"baseURL(%@,%@): %@", 
261               [self nameInContainer], [[self container] baseURL], baseURL];
262     }
263   }
264   else {
265     baseURL = [self rootURLInContext:_ctx];
266     if (debugBaseURL) {
267       [self logWithFormat:@"ROOT baseURL(no container, name=%@): %@", 
268               [self nameInContainer], baseURL];
269     }
270   }
271   
272   /* add a trailing slash for folders */
273   
274   if (![baseURL hasSuffix:@"/"]) {
275     if ([self respondsToSelector:@selector(isFolderish)]) {
276       if ([self isFolderish])
277         baseURL = [baseURL stringByAppendingString:@"/"];
278     }
279   }
280   
281   return baseURL;
282 }
283 - (NSString *)rootURLInContext:(id)_ctx {
284   NSMutableString *ms;
285   BOOL      isHTTPS = NO; // TODO: what about https??
286   NSString  *rootURL;
287   WORequest *rq;
288   NSString  *rh, *tmp;
289   int       port;
290   _initialize();
291   
292   if ((rootURL = [_ctx rootURL]) != nil) {
293     if (debugBaseURL) {
294       [self logWithFormat:@"  using root-url from context: %@",
295               rootURL];
296     }
297     return rootURL;
298   }
299
300   // TODO: this is somewhat weird, why don't we use WOContext for URL gen.?
301   
302   rq   = [(WOContext *)_ctx request];
303   port = [[rq headerForKey:@"x-webobjects-server-port"] intValue];
304   
305   /* TODO: how to handle Evolution bug which sends invalid port ? */
306   if (port == 0) {
307     static BOOL didWarn = NO;
308     if (!didWarn) {
309       [self warnWithFormat:@"(%s:%i): got an empty port, probably buggy "
310               @"SOAP host header!",
311               __PRETTY_FUNCTION__, __LINE__];
312       didWarn = YES;
313     }
314     port = 23000;
315   }
316
317   ms = [[NSMutableString alloc] initWithCapacity:128];
318   
319   if ((tmp = [rq headerForKey:@"host"])) { 
320     /* check whether we have a host header with port */
321     if ([tmp rangeOfString:@":"].length == 0)
322       tmp = nil;
323   }
324
325   if (tmp) {
326     isHTTPS = 
327       [[rq headerForKey:@"x-webobjects-server-url"] hasPrefix:@"https"];
328     [ms appendString:isHTTPS ? @"https://" : @"http://"]; 
329     [ms appendString:tmp];
330   }
331   else if ((tmp = [rq headerForKey:@"x-webobjects-server-url"])) {
332     /* sometimes the URL is just wrong! (suggests port 80) */
333     if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: bad bad bad
334       tmp = [tmp substringToIndex:([tmp length] - 2)];
335     [ms appendString:tmp];
336   }
337   else {
338     [ms appendString:isHTTPS ? @"https://" : @"http://"]; 
339   
340     [ms appendString:[rq headerForKey:@"x-webobjects-server-name"]];
341     if ((isHTTPS ? (port != 443) : (port != 80)) && port != 0)
342       [ms appendFormat:@":%i", port];
343   }
344   if (![ms hasSuffix:@"/"]) [ms appendString:@"/"];
345   
346   /* appname, two cases: */
347   /*   a) direct access,  eg /MyFolder */
348   /*   b) access via app, eg /MyApp/so/MyFolder */
349   [ms appendString:[rq applicationName]];
350   if (![ms hasSuffix:@"/"]) [ms appendString:@"/"];
351   
352   /* done */
353   rootURL = [[ms copy] autorelease];
354   [ms release];
355   if (debugBaseURL)
356     [self logWithFormat:@"  constructed root-url: %@", rootURL];
357   
358   /* some hack for the request handler? */
359   rh = [rq requestHandlerKey];
360   if ([[[_ctx application] registeredRequestHandlerKeys] containsObject:rh])
361     rootURL = [rootURL stringByAppendingFormat:@"%@/", rh];
362   
363   if (debugBaseURL) {
364     [self logWithFormat:@"  setting root-url in context: %@",
365             rootURL];
366   }
367   [(WOContext *)_ctx setRootURL:rootURL];
368   return rootURL;
369 }
370
371 - (NSString *)baseURL {
372   /* you should use the context method ! */
373   return [self baseURLInContext:[[WOApplication application] context]];
374 }
375
376 @end /* NSObject(SoObject) */
377
378 @implementation WOApplication(Authenticator)
379
380 - (NSString *)ownerInContext:(id)_ctx {
381   /* objects are not owned by default */
382   return nil;
383 }
384 - (id)authenticatorInContext:(id)_ctx {
385   return nil;
386 }
387
388 @end /* WOApplication(Authenticator) */