]> err.no Git - sope/commitdiff
ignore unbound KVC keys on WODirectAction, added support for positional
authorhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Sat, 23 Oct 2004 21:24:02 +0000 (21:24 +0000)
committerhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Sat, 23 Oct 2004 21:24:02 +0000 (21:24 +0000)
arguments in action and page invocations, allow XML-RPC methods with
dots on the object itself (without path traversal)

git-svn-id: http://svn.opengroupware.org/SOPE/trunk@312 e4a50df8-12e2-0310-a44c-efbce7f8a7e3

sope-appserver/NGObjWeb/ChangeLog
sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.h
sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.m
sope-appserver/NGObjWeb/SoObjects/SoObjectXmlRpcDispatcher.m
sope-appserver/NGObjWeb/Version
sope-appserver/NGObjWeb/WODirectAction.m

index a56a7aec42dbab93593fbbefc9e603c5ed320266..a5b060122c3d6c9d0125eff2cfb4010d384787b3 100644 (file)
@@ -1,3 +1,24 @@
+2004-10-23  Helge Hess  <helge.hess@opengroupware.org>
+       
+       * v4.3.74
+       
+       * WODirectAction.m: ignore requests on unbound KVC keys on non-lF
+         libraries
+
+       * SoObjects/SoObjectXmlRpcDispatcher.m: do not call -setUserInfo: to
+         annotate NSException's on Cocoa Foundation
+
+       * SoObjects/SoActionInvocation.m: added support for calling actions
+         and pages with positional parameters (eg from XML-RPC)
+
+       * SoObjects/SoObjectXmlRpcDispatcher.m: when looking up a SoMethod for
+         an XML-RPC method name containing a dot (like system.listmethods),
+         first check the fully qualified name prior traversing the package
+         namespaces
+       
+       * SoObjects/SoObjectXmlRpcDispatcher.m: print a warning if server was
+         not linked against libNGXmlRpc
+
 2004-10-22  Marcus Mueller  <znek@mulle-kybernetik.com>
 
        * WOElement.m: fixed unwanted behaviour introduced in v4.3.72 (v4.3.73)
index b039f9c8dd1a11be92cde0cac08f5ac6d913a5bb..e3f39998da3c934748d24c73565bfb68e297a5c9 100644 (file)
 #import <Foundation/NSObject.h>
 
 /*
+  SoActionInvocation
+
   An invocation object for WODirectAction based SoClass methods.
   
   If the invocation is bound, the action is instantiated and initialized,
   if it is called, the "actionName" is called and the result is returned or
   if no "actionName" is set, the default action is called.
+  
+  Usage:
+    methods = {
+      view = {
+        protectedBy = "View";
+        actionClass = "DirectAction"; // class to be instantiated
+        actionName  = "view";         // 'viewAction' will get called
+      };
+      ...
+    }
+  
+  Positional Arguments
+
+    When being queried using some method which supplies positional arguments
+    (for example XML-RPC), you can assign keys to each position using the
+    'positionalKeys' argument info.
+    
+    If more arguments are passed in than the number of keys specified in the
+    argument info, the additional arguments will be ignore (a warning will get
+    printed).
+    
+    If less arguments are passed in, the additional keys will be resetted to
+    'nil' (-takeValue:nil forKey:key will get called) to ensure that the call
+    signature is deterministic.
+    
+    methods = {
+      "blogger.getUsersBlogs" = {
+        protectedBy = "View";
+        actionClass = BloggerGetUserBlogs;
+        arguments = {
+          positionalKeys = ( appId, login, password );
+        };
+      };
+      ...
+    }
+
+  SOAP Arguments
+
+    When being queried using SOAP, you can extract keys from the SOAP message
+    using EDOM query pathes. The values are assigned to the specified keys of
+    the action/page object.
+    
+    methods = {
+      "cms.login" = {
+         actionClass = CMSLoginAction;
+         arguments   = {
+           SOAP = { // key is from context.soRequestType
+             login    = "/envelope/loginRequest/login";
+             password = "/envelope/loginRequest/password";
+           };
+         }
+      };
+    }
 */
 
-@class NSString, NSDictionary, NSMutableString;
+@class NSString, NSArray, NSDictionary, NSMutableString;
 
 @interface SoActionInvocation : NSObject
 {
 - (BOOL)isBound;
 - (id)bindToObject:(id)_object inContext:(id)_ctx;
 
+/* calling the method */
+
+- (id)callOnObject:(id)_client inContext:(id)_ctx;
+- (id)callOnObject:(id)_client 
+  withPositionalParameters:(NSArray *)_args
+  inContext:(id)_ctx;
+
 /* description */
 
 - (void)appendAttributesToDescription:(NSMutableString *)_ms;
index 6a4a231da856940234adc894ab0aefbed33de55a..3a1f5d60bda48f2eea7124ff169a61f5e5606c88 100644 (file)
@@ -108,7 +108,7 @@ static int debugOn = 0;
     return nil;
   }
   
-  /* for each positional selector argument we have a query path */
+  /* for each key argument we have a query path */
   
   args = [NSMutableDictionary dictionaryWithCapacity:8];
   keys = [_spec keyEnumerator];
@@ -199,7 +199,7 @@ static int debugOn = 0;
   argspec = [self->argumentSpecifications objectForKey:[_ctx soRequestType]];
   if (argspec == nil)
     return;
-    
+  
   args = [self extractArgumentsFromContext:_ctx
               forRequestType:[_ctx soRequestType]
               specification:argspec];
@@ -208,7 +208,46 @@ static int debugOn = 0;
   if (args != nil) [_method takeValuesFromDictionary:args];
 }
 
-- (id)callOnObject:(id)_client inContext:(id)_ctx {
+- (void)_applyPositionalArguments:(NSArray *)_args onMethod:(id)_method
+  inContext:(id)_ctx
+{
+  NSArray *info;
+  unsigned i, argCount, infoCount;
+  
+  info      = [self->argumentSpecifications objectForKey:@"positionalKeys"];
+  infoCount = [info  count];
+  argCount  = [_args count];
+  if ((info == nil) && (argCount > 0)) {
+    [self logWithFormat:
+           @"WARNING: found no argument specification for positional keys!"];
+    return;
+  }
+  
+  /* copy available arguments to key */
+  
+  for (i = 0; i < argCount; i++) {
+    if (i >= infoCount) {
+      [self logWithFormat:
+             @"WARNING: could not apply argument %d (no key info)", (i + 1)];
+      continue;
+    }
+    
+    [_method takeValue:[_args objectAtIndex:i] forKey:[info objectAtIndex:i]];
+  }
+  
+  /* fill up missing arguments */
+  
+  for (i = argCount; i < infoCount; i++)
+    [_method takeValue:nil forKey:[info objectAtIndex:i]];
+}
+
+/* calling the method */
+
+- (id)callOnObject:(id)_client 
+  withPositionalParametersWhenNotNil:(NSArray *)_positionalArgs
+  inContext:(id)_ctx
+{
+  /* method used for both, positional and key arguments */
   id method;
   
   if (self->object != _client) {
@@ -235,9 +274,15 @@ static int debugOn = 0;
   if ([method respondsToSelector:@selector(setClientObject:)])
     [method setClientObject:_client];
   
-  /* TODO: what should be done first?, take values or args? */
-  [self _prepareMethod:method          inContext:_ctx];
-  [self _applyArgumentsOnMethod:method inContext:_ctx];
+  if ([_positionalArgs isNotNull]) {
+    [self _applyPositionalArguments:_positionalArgs onMethod:method
+         inContext:_ctx];
+  }
+  else {
+    /* TODO: what should be done first?, take values or args? */
+    [self _prepareMethod:method          inContext:_ctx];
+    [self _applyArgumentsOnMethod:method inContext:_ctx];
+  }
   
   /* call action */
   
@@ -257,6 +302,21 @@ static int debugOn = 0;
   }
 }
 
+- (id)callOnObject:(id)_client inContext:(id)_ctx {
+  return [self callOnObject:_client
+              withPositionalParametersWhenNotNil:nil /* not positional */
+              inContext:_ctx];
+}
+- (id)callOnObject:(id)_client 
+  withPositionalParameters:(NSArray *)_args
+  inContext:(id)_ctx
+{
+  if (_args == nil) _args = [NSArray array];
+  return [self callOnObject:_client
+              withPositionalParametersWhenNotNil:_args
+              inContext:_ctx];
+}
+
 /* bindings */
 
 - (BOOL)isBound {
index ad10721bf67782c4e20bc74bdc4e49e881eaf7f6..064cb0f787edb8a196602cf6130e9888b7583a2a 100644 (file)
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #include "SoObjectXmlRpcDispatcher.h"
 #include "SoObject.h"
+#include "SoClass.h"
 #include "NSException+HTTP.h"
 #include <NGXmlRpc/XmlRpcMethodCall+WO.h>
 #include <NGXmlRpc/XmlRpcMethodResponse+WO.h>
@@ -64,11 +64,66 @@ static BOOL debugOn = NO;
 
 /* perform call on object */
 
+- (BOOL)isPackagePathMethodName:(NSString *)_name inContext:(WOContext *)_ctx {
+  /*
+    If the XML-RPC method name contains a dot (eg system.listmethods), we need
+    to check whether the namespace is to be treated as a path or whether we
+    should lookup the fully qualified name.
+    
+    So we first check whether it contains a dot, then we check whether the
+    object implements the fully qualified name. Otherwise we treat the
+    namespace as a path.
+  */
+  NSRange r;
+
+  /* check whether the name contains a dot ... */
+  
+  r = [_name rangeOfString:@"." options:(NSLiteralSearch|NSBackwardsSearch)];
+  if (r.length == 0)
+    return NO; /* no dot ... */
+  
+  /* check whether the object implements the fully qualified name */
+  
+  if ([self->object hasName:_name inContext:_ctx])
+    return NO;
+  
+  [self debugWithFormat:
+         @"Note: did not find pkg name '%@' in %@", _name, self->object];
+  
+  /* otherwise, perform a path lookup ... */
+  return YES;
+}
+
+- (NSException *)getClientObject:(id *)_object andMethodName:(NSString **)_mn
+  forPackageMethodPath:(NSString *)_path inContext:(WOContext *)_ctx
+{
+  /* has a prefix, eg "folder.folder.a()" => traverse */
+  NSArray     *nsParts;
+  NSException *error = nil;
+  unsigned    count;
+    
+  nsParts = [_path componentsSeparatedByString:@"."];
+  count   = [nsParts count];
+  *_mn    = [[[nsParts objectAtIndex:(count - 1)] copy] autorelease];
+  nsParts = [nsParts subarrayWithRange:NSMakeRange(0, count - 1)];
+  
+  [self debugWithFormat:@"XML-RPC traversal: %@",
+           [nsParts componentsJoinedByString:@" => "]];
+  
+  /*
+    TODO: we might not want to use -traverse.. so that the clientObject
+          stays the same (the one bound to the URL)?
+  */
+  *_object = [self->object 
+                 traversePathArray:nsParts inContext:_ctx error:&error
+                 acquire:YES];
+  return error;
+}
+
 - (id)performActionNamed:(NSString *)_name parameters:(NSArray *)_params 
   inContext:(WOContext *)_ctx
 {
   NSString *methodName;
-  NSRange  r;
   id       clientObject;
   id       method;
 
@@ -77,28 +132,15 @@ static BOOL debugOn = NO;
   [self debugWithFormat:@"should perform: %@", _name];
   methodName = nil;
   
-  r = [_name rangeOfString:@"." options:(NSLiteralSearch|NSBackwardsSearch)];
-  if (r.length > 0) {
+  if ([self isPackagePathMethodName:_name inContext:_ctx]) {
     /* has a prefix, eg "folder.folder.a()" => traverse */
-    NSArray     *nsParts;
     NSException *error = nil;
-    unsigned    count;
-    
-    nsParts    = [_name componentsSeparatedByString:@"."];
-    count      = [nsParts count];
-    methodName = [[[nsParts objectAtIndex:(count - 1)] copy] autorelease];
-    nsParts    = [nsParts subarrayWithRange:NSMakeRange(0, count - 1)];
-    
-    [self debugWithFormat:@"XML-RPC traversal: %@",
-           [nsParts componentsJoinedByString:@" => "]];
+
+    [self debugWithFormat:@"Note: traversing path to XML-RPC method: %@",
+           _name];
     
-    /*
-       TODO: we might not want to use -traverse.. so that the clientObject
-             stays the same (the one bound to the URL)?
-    */
-    clientObject = [self->object 
-                       traversePathArray:nsParts inContext:_ctx error:&error
-                       acquire:YES];
+    error = [self getClientObject:&clientObject andMethodName:&methodName
+                 forPackageMethodPath:_name inContext:_ctx];
     if (error) {
       [self debugWithFormat:@"  XML-RPC traversal error: %@", error];
       return error;
@@ -139,8 +181,8 @@ static BOOL debugOn = NO;
   if ([_params count] > 0) {
     [self logWithFormat:
            @"WARNING: invoking SOPE method via XML-RPC without "
-           @"positional paramters (%i parameters defined)",
-           [_params count]];
+           @"positional paramters (%i parameters defined): %@",
+           [_params count], method];
   }
   return [method callOnObject:clientObject inContext:_ctx];
 }
@@ -148,8 +190,9 @@ static BOOL debugOn = NO;
 - (id)faultFromException:(NSException *)_exception
   methodCall:(XmlRpcMethodCall *)_call
 {
+#if !APPLE_FOUNDATION_LIBRARY && !NeXT_Foundation_LIBRARY
   /* add some more information to generic exceptions ... */
-  if (_call) {
+  if (_call != nil) {
     NSMutableDictionary *ui;
     
     ui = [[_exception userInfo] mutableCopy];
@@ -161,6 +204,7 @@ static BOOL debugOn = NO;
     [_exception setUserInfo:ui];
     [ui release];
   }
+#endif
   
   [self logWithFormat:@"%s: turning exception into fault %@\n",
           __PRETTY_FUNCTION__,
@@ -223,12 +267,26 @@ static BOOL debugOn = NO;
   XmlRpcMethodCall *call;
   id result;
   
+  if (![XmlRpcMethodCall
+        instancesRespondToSelector:@selector(initWithRequest:)]) {
+    [self logWithFormat:
+           @"ERROR: XmlRpcMethodCall does not respond to -initWithRequest:, "
+           @"this method is part of libNGXmlRpc which you might want to link "
+           @"against to get XML-RPC support."];
+    return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
+                       reason:@"server does not support XML-RPC"];
+  }
+  
+  /* decode XML-RPC call */
+  
   call = [XmlRpcMethodCall alloc];
   call = [call initWithRequest:[_ctx request]];
   call = [call autorelease];
   
   if (call == nil)
     return [self couldNotDecodeXmlRpcRequestInContext:_ctx];
+
+  /* perform call */
   
   if ((result = [self performMethodCall:call inContext:_ctx]) == nil)
     /* TODO: should we return a fault instead? */
@@ -238,6 +296,8 @@ static BOOL debugOn = NO;
     /* pass WOResponse objects through ... */
     return result;
   
+  /* encode result as XML-RPC */
+  
   NS_DURING {
     if ([result isKindOfClass:[XmlRpcMethodResponse class]])
       r = result;
index 694d1b5cc2247709264a193913b7730944097add..241d51423be067ff0f0a43b29a07a1b76b3534f0 100644 (file)
@@ -1,6 +1,6 @@
 # version file
 
-SUBMINOR_VERSION:=73
+SUBMINOR_VERSION:=74
 
 # v4.3.42  requires libNGExtensions v4.3.116
 # v4.3.40  requires libNGExtensions v4.3.115
index 5e5fc1589173878850557e25a35567c693bcb318..ced47602c4afac483c02196483c0d8b06ed03aa2 100644 (file)
@@ -18,7 +18,7 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
+// $Id: WODirectAction.m 1 2004-08-20 10:08:27Z znek $
 
 #include <NGObjWeb/WODirectAction.h>
 #include "NSObject+WO.h"
 #include <NGObjWeb/WOSessionStore.h>
 #include "common.h"
 
+#if !LIB_FOUNDATION_LIBRARY
+#  define NG_USE_KVC_FALLBACK 1
+#endif
+
 @implementation WODirectAction
 
 + (int)version {
   return WOGetKVCValueUsingMethod(self, _key);
 }
 
+#if !LIB_FOUNDATION_LIBRARY
+
+- (void)setValue:(id)_value forUndefinedKey:(NSString *)_key {
+  [self logWithFormat:
+         @"WARNING: tried to set value for undefined KVC key: '%@'", _key];
+}
+- (id)valueForUndefinedKey:(NSString *)_key {
+  [self logWithFormat:
+         @"WARNING: tried to access undefined KVC key: '%@'", _key];
+  return nil;
+}
+
+#endif
+
 @end /* WODirectAction */