2 Copyright (C) 2002-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
23 #include "SoObjectXmlRpcDispatcher.h"
25 #include "NSException+HTTP.h"
26 #include <NGXmlRpc/XmlRpcMethodCall+WO.h>
27 #include <NGXmlRpc/XmlRpcMethodResponse+WO.h>
28 #include <NGObjWeb/WOActionResults.h>
29 #include <NGObjWeb/WOContext.h>
30 #include <NGObjWeb/WOResponse.h>
33 @interface NSObject(XmlRpcCall)
35 - (id)callOnObject:(id)_client
36 withPositionalParameters:(NSArray *)_args
41 @implementation SoObjectXmlRpcDispatcher
43 static BOOL debugOn = NO;
46 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
48 debugOn = [ud boolForKey:@"SoObjectXmlRpcDispatcherDebugEnabled"];
49 if (debugOn) NSLog(@"Note: SOPE XML-RPC Dispatcher Debugging turned on.");
54 - (NSException *)missingMethodFault:(NSString *)_method
55 inContext:(WOContext *)_ctx
59 r = [@"Could not locate requested XML-RPC method: "
60 stringByAppendingString:_method];
61 return [NSException exceptionWithHTTPStatus:404 /* not found */
65 /* perform call on object */
67 - (id)performActionNamed:(NSString *)_name parameters:(NSArray *)_params
68 inContext:(WOContext *)_ctx
75 // TODO: check whether _name is set
77 [self debugWithFormat:@"should perform: %@", _name];
80 r = [_name rangeOfString:@"." options:(NSLiteralSearch|NSBackwardsSearch)];
82 /* has a prefix, eg "folder.folder.a()" => traverse */
84 NSException *error = nil;
87 nsParts = [_name componentsSeparatedByString:@"."];
88 count = [nsParts count];
89 methodName = [[[nsParts objectAtIndex:(count - 1)] copy] autorelease];
90 nsParts = [nsParts subarrayWithRange:NSMakeRange(0, count - 1)];
92 [self debugWithFormat:@"XML-RPC traversal: %@",
93 [nsParts componentsJoinedByString:@" => "]];
96 TODO: we might not want to use -traverse.. so that the clientObject
97 stays the same (the one bound to the URL)?
99 clientObject = [self->object
100 traversePathArray:nsParts inContext:_ctx error:&error
103 [self debugWithFormat:@" XML-RPC traversal error: %@", error];
108 clientObject = self->object;
112 method = [clientObject lookupName:methodName inContext:_ctx acquire:YES];
114 // TODO: return proper fault!
115 [self logWithFormat:@"did not find requested XML-RPC method: '%@'",
117 return [self missingMethodFault:methodName inContext:_ctx];
119 if (![method isCallable]) {
120 // TODO: return proper fault!
122 @"located object (%@) is not callable (class=%@):\n %@",
123 methodName, NSStringFromClass([method class]), method];
127 /* TODO: do we need to bind or is this automatic? */
129 if ([method respondsToSelector:
130 @selector(callOnObject:withPositionalParameters:inContext:)]) {
131 [self debugWithFormat:
132 @"calling XML-RPC method with %i positional parameters.",
134 return [method callOnObject:clientObject
135 withPositionalParameters:_params
139 if ([_params count] > 0) {
141 @"WARNING: invoking SOPE method via XML-RPC without "
142 @"positional paramters (%i parameters defined)",
145 return [method callOnObject:clientObject inContext:_ctx];
148 - (id)faultFromException:(NSException *)_exception
149 methodCall:(XmlRpcMethodCall *)_call
151 /* add some more information to generic exceptions ... */
153 NSMutableDictionary *ui;
155 ui = [[_exception userInfo] mutableCopy];
156 if (ui == nil) ui = [[NSMutableDictionary alloc] init];
158 [ui setObject:[_call methodName] forKey:@"methodName"];
159 [ui setObject:[_call parameters] forKey:@"methodParameters"];
161 [_exception setUserInfo:ui];
165 [self logWithFormat:@"%s: turning exception into fault %@\n",
167 [_exception description]];
172 - (id<WOActionResults>)actionResponseForResult:(id)resValue {
173 if ([resValue conformsToProtocol:@protocol(WOActionResults)]) {
174 /* a "HTTP" result ... */
178 /* an XML-RPC result ... */
179 XmlRpcMethodResponse *mResponse;
181 mResponse = [[XmlRpcMethodResponse alloc] initWithResult:resValue];
182 return [mResponse autorelease];
186 - (id)performMethodCall:(XmlRpcMethodCall *)_call inContext:(WOContext *)_ctx{
190 resValue = [self performActionNamed:[_call methodName]
191 parameters:[_call parameters]
193 resValue = [resValue retain];
196 resValue = [self faultFromException:localException
198 resValue = [resValue retain];
202 resValue = [resValue autorelease];
204 return [self actionResponseForResult:resValue];
207 - (id)couldNotDecodeXmlRpcRequestInContext:(WOContext *)_ctx {
208 WOResponse *r = [_ctx response];
210 [r setStatus:400 /* bad request */];
211 [r appendContentString:@"malformed XML-RPC request !"];
215 - (id)handleXmlRpcEncodingException:(NSException *)_exception object:(id)_obj {
216 [self logWithFormat:@"could not encode object: %@ (%@): %@",
217 _obj, NSStringFromClass([_obj class]), _exception];
221 - (id)dispatchInContext:(WOContext *)_ctx {
222 XmlRpcMethodResponse *r;
223 XmlRpcMethodCall *call;
226 call = [XmlRpcMethodCall alloc];
227 call = [call initWithRequest:[_ctx request]];
228 call = [call autorelease];
231 return [self couldNotDecodeXmlRpcRequestInContext:_ctx];
233 if ((result = [self performMethodCall:call inContext:_ctx]) == nil)
234 /* TODO: should we return a fault instead? */
237 if ([result isKindOfClass:[WOResponse class]])
238 /* pass WOResponse objects through ... */
242 if ([result isKindOfClass:[XmlRpcMethodResponse class]])
245 r = [[[XmlRpcMethodResponse alloc] initWithResult:result] autorelease];
247 result = [[[r generateResponse] retain] autorelease];
250 result = [self handleXmlRpcEncodingException:localException
260 - (NSString *)loggingPrefix {
261 return @"[obj-xmlrpc-dispatch]";
263 - (BOOL)isDebuggingEnabled {
267 @end /* SoObjectXmlRpcDispatcher */