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