From: helge Date: Sat, 23 Oct 2004 21:24:02 +0000 (+0000) Subject: ignore unbound KVC keys on WODirectAction, added support for positional X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=014f620e56075cf462c94649a7e898423fc310a6;p=sope ignore unbound KVC keys on WODirectAction, added support for positional 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 --- diff --git a/sope-appserver/NGObjWeb/ChangeLog b/sope-appserver/NGObjWeb/ChangeLog index a56a7aec..a5b06012 100644 --- a/sope-appserver/NGObjWeb/ChangeLog +++ b/sope-appserver/NGObjWeb/ChangeLog @@ -1,3 +1,24 @@ +2004-10-23 Helge Hess + + * 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 * WOElement.m: fixed unwanted behaviour introduced in v4.3.72 (v4.3.73) diff --git a/sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.h b/sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.h index b039f9c8..e3f39998 100644 --- a/sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.h +++ b/sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.h @@ -25,14 +25,69 @@ #import /* + 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 { @@ -62,6 +117,13 @@ - (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; diff --git a/sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.m b/sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.m index 6a4a231d..3a1f5d60 100644 --- a/sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.m +++ b/sope-appserver/NGObjWeb/SoObjects/SoActionInvocation.m @@ -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 { diff --git a/sope-appserver/NGObjWeb/SoObjects/SoObjectXmlRpcDispatcher.m b/sope-appserver/NGObjWeb/SoObjects/SoObjectXmlRpcDispatcher.m index ad10721b..064cb0f7 100644 --- a/sope-appserver/NGObjWeb/SoObjects/SoObjectXmlRpcDispatcher.m +++ b/sope-appserver/NGObjWeb/SoObjects/SoObjectXmlRpcDispatcher.m @@ -18,10 +18,10 @@ 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 #include @@ -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; diff --git a/sope-appserver/NGObjWeb/Version b/sope-appserver/NGObjWeb/Version index 694d1b5c..241d5142 100644 --- a/sope-appserver/NGObjWeb/Version +++ b/sope-appserver/NGObjWeb/Version @@ -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 diff --git a/sope-appserver/NGObjWeb/WODirectAction.m b/sope-appserver/NGObjWeb/WODirectAction.m index 5e5fc158..ced47602 100644 --- a/sope-appserver/NGObjWeb/WODirectAction.m +++ b/sope-appserver/NGObjWeb/WODirectAction.m @@ -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 #include "NSObject+WO.h" @@ -30,6 +30,10 @@ #include #include "common.h" +#if !LIB_FOUNDATION_LIBRARY +# define NG_USE_KVC_FALLBACK 1 +#endif + @implementation WODirectAction + (int)version { @@ -205,4 +209,18 @@ 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 */