]> err.no Git - sope/blobdiff - sope-appserver/NGObjWeb/SoObjects/SoObjectMethodDispatcher.m
use %p for pointer formats, fixed gcc 4.1 warnings, minor code improvs
[sope] / sope-appserver / NGObjWeb / SoObjects / SoObjectMethodDispatcher.m
index ddc70e3272dcc2a00c533bc35180e34a982ecbc8..d9b38400f1682395cad3f763083f68445cec65ab 100644 (file)
@@ -1,24 +1,23 @@
 /*
-  Copyright (C) 2002-2004 SKYRIX Software AG
+  Copyright (C) 2002-2005 SKYRIX Software AG
 
-  This file is part of OpenGroupware.org.
+  This file is part of SOPE.
 
-  OGo is free software; you can redistribute it and/or modify it under
+  SOPE is free software; you can redistribute it and/or modify it under
   the terms of the GNU Lesser General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.
 
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
   License for more details.
 
   You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
+  License along with SOPE; see the file COPYING.  If not, write to the
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #include "SoObjectMethodDispatcher.h"
 #include "SoObject.h"
 @implementation SoObjectMethodDispatcher
 
 static BOOL debugOn = NO;
+static BOOL useRedirectsForDefaultMethods = NO;
 
 + (void)initialize {
   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
   static BOOL didInit = NO;
   if (didInit) return;
   didInit = YES;
-
+  
   debugOn = [ud boolForKey:@"SoObjectMethodDispatcherDebugEnabled"];
+  useRedirectsForDefaultMethods = 
+    [ud boolForKey:@"SoRedirectToDefaultMethods"];
 }
 
 - (id)initWithObject:(id)_object {
@@ -60,6 +62,7 @@ static BOOL debugOn = NO;
 - (id)dispatchInContext:(WOContext *)_ctx {
   NSAutoreleasePool *pool;
   WORequest *rq;
+  NSString  *httpMethod;
   id clientObject;
   id methodObject;
   id resultObject;
@@ -69,18 +72,35 @@ static BOOL debugOn = NO;
   
   /* find client object */
   
-  if ((clientObject = [_ctx clientObject])) {
+  if ((clientObject = [_ctx clientObject]) != nil) {
     if (debugOn)
       [self debugWithFormat:@"client object set in ctx: %@", clientObject];
   }
-  else if ((clientObject = [self->object clientObject])) {
+  else if ((clientObject = [self->object clientObject]) != nil) {
     if (debugOn)
       [self debugWithFormat:@"setting client object: %@", clientObject];
     [_ctx setClientObject:clientObject];
   }
+
+  /* check whether client object is a response ... */
+  
+  if ([clientObject isKindOfClass:[WOResponse class]]) {
+    [self debugWithFormat:@"clientObject is a WOResponse, returning that: %@",
+           clientObject];
+    resultObject = [clientObject retain];
+    [pool release];
+    return [resultObject autorelease];
+  }
   
   // TODO: should check XML-RPC !!! 
-  //       (hm, why ? XML-RPC is handled by other dispatcher ?)
+  //       (hm, why? XML-RPC is handled by other dispatcher?)
+  
+  /* 
+     This X- field is used by Google which uses POST to trigger REST methods,
+     don't ask me why ... ;-/
+  */
+  if (![(httpMethod = [rq headerForKey:@"x-http-method-override"]) isNotEmpty])
+    httpMethod = [rq method];
   
   /* find callable (method) object */
   
@@ -89,16 +109,43 @@ static BOOL debugOn = NO;
       [self debugWithFormat:@"traversed object is callable: %@", self->object];
     methodObject = self->object;
   }
-  else if ([[self->object soClass] hasKey:[rq method] inContext:_ctx]) {
+  else if ([[self->object soClass] hasKey:httpMethod inContext:_ctx]) {
     // TODO: I'm not sure whether this step is correct
-    /* the class has a GET method */
+    /* the class has a GET/PUT/xxx method */
     methodObject = [self->object lookupName:[rq method] 
                                 inContext:_ctx
                                 acquire:NO];
   }
+  else if (useRedirectsForDefaultMethods) {
+    /*
+      Redirect to a default method if available.
+    */
+    NSString *defaultName;
+    
+    defaultName = [self->object defaultMethodNameInContext:_ctx];
+    if ([defaultName isNotEmpty]) {
+      WOResponse *r;
+      NSString   *url;
+       
+      url = [self->object baseURLInContext:_ctx];
+      if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
+      url = [url stringByAppendingString:defaultName];
+       
+      [self debugWithFormat:@"redirect to default method %@ of %@: %@", 
+             defaultName, self->object, url];
+       
+      r = [[_ctx response] retain];
+      [r setStatus:302 /* moved */];
+      [r setHeader:url forKey:@"location"];
+      [pool release];
+      return [r autorelease];
+    }
+  }
   else {
-    // TODO: should we replace the methodObject with a redirect to the
-    //       default method name? This would ensure proper URLs
+    /* 
+       Note: this can lead to incorrect URLs if the base URL of the method is
+             not set (the method will run in the client URL).
+    */
     methodObject = [self->object lookupDefaultMethod];
     if (debugOn)
       [self debugWithFormat:@"using default method: %@", methodObject];
@@ -114,16 +161,29 @@ static BOOL debugOn = NO;
   }
 
   /* perform call */
-    
+  
   if (methodObject == nil || ![methodObject isCallable]) {
     /*
       The object is neither callable nor does it have a default method,
       so we just pass it through.
+      
+      Note: you can run into situations where there is a methodObject, but
+            it isn't callable. Eg this situation can occur if the default
+           method name leads to an object which isn't a method (occured in
+           the OFS context if 'index' maps to an OFSFile which itself isn't
+           callable).
     */
     resultObject = self->object;
     if (debugOn) {
-      [self debugWithFormat:@"got no method, using object as result: %@", 
-             resultObject];
+      if (methodObject == nil) {
+       [self debugWithFormat:@"got no method, using object as result: %@", 
+               resultObject];
+      }
+      else {
+       [self debugWithFormat:
+               @"method is not callable %@, using object as result: %@", 
+               methodObject, resultObject];
+      }
     }
   }
   else {
@@ -131,7 +191,7 @@ static BOOL debugOn = NO;
                                 inContext:_ctx];
     if (debugOn) {
       if ([resultObject isKindOfClass:[WOResponse class]]) {
-       [self debugWithFormat:@"call produced response: 0x%08X (code=%i)", 
+       [self debugWithFormat:@"call produced response: 0x%p (code=%i)", 
                resultObject, [(WOResponse *)resultObject status]];
       }
       else
@@ -161,7 +221,7 @@ static BOOL debugOn = NO;
   NSMutableString *ms;
   
   ms = [NSMutableString stringWithCapacity:64];
-  [ms appendFormat:@"<0x%08X[%@]:", self,
+  [ms appendFormat:@"<0x%p[%@]:", self,
         NSStringFromClass((Class)*(void**)self)];
   
   if (self->object)