2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include <NGXmlRpc/NGXmlRpcAction.h>
23 #include <NGXmlRpc/NGAsyncResultProxy.h>
24 #include <NGXmlRpc/NGXmlRpc.h>
25 #include <NGXmlRpc/XmlRpcMethodCall+WO.h>
26 #include <NGXmlRpc/XmlRpcMethodResponse+WO.h>
27 #include <NGObjWeb/WOApplication.h>
28 #include <NGObjWeb/WORequest.h>
29 #include <NGObjWeb/WOContext.h>
30 #include <NGObjWeb/WOSession.h>
31 #include <NGObjWeb/WOResponse.h>
35 @interface NSException(UserInfoExt)
36 - (void)setUserInfo:(NSDictionary *)_ui;
39 @implementation WOCoreApplication(XmlRpcActionClass)
41 - (Class)defaultActionClassForRequest:(WORequest *)_request {
42 return NSClassFromString(@"DirectAction");
47 @implementation NGXmlRpcAction
55 return [[NSUserDefaults standardUserDefaults]
56 boolForKey:@"WOCoreOnXmlRpcFault"];
64 - (id)initWithContext:(WOContext *)_ctx {
65 if ((self = [super init])) {
66 self->context = RETAIN(_ctx);
71 return [self initWithContext:nil];
75 RELEASE(self->context);
79 /* sandstorm components */
81 - (NSString *)xmlrpcComponentNamespacePrefix {
84 np = [[NSUserDefaults standardUserDefaults]
85 stringForKey:@"SxDefaultNamespacePrefix"];
90 @"WARNING: SxDefaultNamespacePrefix default is not set !"];
92 np = [(NSHost *)[NSHost currentHost] name];
93 if ([np length] > 0) {
94 if (!isdigit([np characterAtIndex:0])) {
97 parts = [np componentsSeparatedByString:@"."];
98 if ([parts count] == 0) {
100 else if ([parts count] == 1)
101 return [parts objectAtIndex:0];
107 e = [parts reverseObjectEnumerator];
108 while ((s = [e nextObject])) {
114 np = [[np stringByAppendingString:@"."] stringByAppendingString:s];
122 return @"com.skyrix";
124 - (NSString *)xmlrpcComponentName {
127 s = NSStringFromClass([self class]);
128 if (![s isEqualToString:@"DirectAction"])
131 return [[NSProcessInfo processInfo] processName];
134 - (NSString *)xmlrpcComponentNamespace {
137 ns = [self xmlrpcComponentNamespacePrefix];
138 n = [self xmlrpcComponentName];
139 return [[ns stringByAppendingString:@"."] stringByAppendingString:n];
150 return [WOCoreApplication application];
153 - (NSNotificationCenter *)notificationCenter {
154 return [NSNotificationCenter defaultCenter];
157 - (WOContext *)context {
158 if (self->context == nil)
159 self->context = RETAIN([[WOApplication application] context]);
160 return self->context;
163 - (WORequest *)request {
164 return [[self context] request];
168 return [[self context] session];
171 - (id)existingSession {
172 WOContext *ctx = [self context];
174 /* check whether the context has a session */
176 return [ctx hasSession] ? [ctx session] : nil;
179 /* XML-RPC direct action dispatcher ... */
181 - (NSString *)authRealm {
182 WOApplication *app = [self application];
186 - (id<WOActionResults>)missingAuthAction {
190 auth = [NSString stringWithFormat:@"basic realm=\"%@\"",[self authRealm]];
192 resp = [(WOResponse *)[WOResponse alloc] initWithRequest:[self request]];
193 [resp setStatus:401 /* unauthorized */];
194 [resp setHeader:auth forKey:@"www-authenticate"];
195 // TODO: should embed an XML-RPC fault representing the auth-problem
196 return [resp autorelease];
198 - (id<WOActionResults>)accessDeniedAction {
202 auth = [NSString stringWithFormat:@"basic realm=\"%@\"",[self authRealm]];
204 [self logWithFormat:@"access was denied"];
206 resp = [(WOResponse *)[WOResponse alloc] initWithRequest:[self request]];
207 [resp setStatus:401 /* unauthorized */];
208 [resp setHeader:auth forKey:@"www-authenticate"];
209 // TODO: should embed an XML-RPC fault representing the auth-problem
210 return [resp autorelease];
213 - (id<WOActionResults>)actionResponseForResult:(id)resValue {
214 if ([resValue isKindOfClass:[NGAsyncResultProxy class]]) {
215 /* async result ... */
216 return [self responseForAsyncResult:resValue];
218 else if ([resValue conformsToProtocol:@protocol(WOActionResults)]) {
219 /* a "HTTP" result ... */
223 /* an XML-RPC result ... */
224 XmlRpcMethodResponse *mResponse;
226 mResponse = [[[XmlRpcMethodResponse alloc]
227 initWithResult:resValue]
233 - (void)proxyReady:(NGAsyncResultProxy *)_sender {
234 id<WOActionResults> ares;
237 AUTORELEASE(RETAIN(self)); /* keep me around ;-) */
239 //[self debugWithFormat:@"ready: %@", _sender];
241 [_sender setTarget:nil];
242 [_sender setAction:NULL];
244 ares = [self actionResponseForResult:[_sender result]];
245 //[self debugWithFormat:@" result: %@", ares];
247 r = [ares generateResponse];
248 //[self debugWithFormat:@" response: %@", r];
250 [[self notificationCenter]
251 postNotificationName:@"WOAsyncResponseReadyNotification"
252 object:[_sender token]
253 userInfo:[NSDictionary dictionaryWithObject:r
254 forKey:@"WOAsyncResponse"]];
257 - (WOResponse *)responseForAsyncResult:(NGAsyncResultProxy *)_proxy {
263 //[self debugWithFormat:@"shall create async result for proxy:\n %@", _proxy];
265 token = [NSString stringWithFormat:@"0x%08X-%i", _proxy, cnt++];
266 //[self debugWithFormat:@"token: %@", token];
267 ui = [NSDictionary dictionaryWithObject:token
268 forKey:@"WOAsyncResponseToken"];
270 r = [WOResponse responseWithRequest:[self request]];
271 [r setStatus:20001 /* async response */];
274 /* map token to result proxy ... */
275 [_proxy setTarget:self];
276 [_proxy setAction:@selector(proxyReady:)];
277 [_proxy setToken:token];
282 - (id)faultFromException:(NSException *)_exception
283 methodCall:(XmlRpcMethodCall *)_call
285 /* add some more information to generic exceptions ... */
287 NSMutableDictionary *ui;
289 ui = [[_exception userInfo] mutableCopy];
290 if (ui == nil) ui = [[NSMutableDictionary alloc] init];
292 [ui setObject:[_call methodName] forKey:@"methodName"];
293 [ui setObject:[_call parameters] forKey:@"methodParameters"];
295 [_exception setUserInfo:ui];
299 [self logWithFormat:@"%s: turning exception into fault %@\n",
301 [_exception description]];
303 if ([[self class] coreOnFault])
308 - (id)faultFromException:(NSException *)_exception {
309 return [self faultFromException:_exception methodCall:nil];
312 - (NSArray *)signatureForParameters:(NSArray *)_params {
316 if ((count = [_params count]) == 0)
317 return [NSArray arrayWithObject:@"*"];
319 ma = [NSMutableArray arrayWithCapacity:(count + 1)];
320 [ma addObject:@"*"]; // return type, unknown from request ...
321 for (i = 0; i < count; i++)
322 [ma addObject:[[_params objectAtIndex:i] xmlRpcType]];
325 - (SEL)selectorForXmlRpcAction:(NSString *)_name
326 parameters:(NSArray *)_params
330 if ((sig = [self signatureForParameters:_params]) == nil)
331 [self logWithFormat:@"found not signature for params ..."];
333 return [[self class] selectorForActionNamed:_name
337 - (NSString *)_methodNameWithoutPrefix:(NSString *)_name {
341 if ((n = [self xmlrpcComponentNamespacePrefix]) == nil)
343 if ((len = [n length]) == 0)
345 if (![_name hasPrefix:n])
349 _name = [_name substringFromIndex:len];
350 if ([_name hasPrefix:@"."])
351 _name = [_name substringFromIndex:1];
355 - (id)performActionNamed:(NSString *)_name parameters:(NSArray *)_params {
356 NSMethodSignature *sign;
364 _name = [self _methodNameWithoutPrefix:_name];
366 /* generate selector */
367 if ((sel = [self selectorForXmlRpcAction:_name parameters:_params]) ==NULL) {
368 /* return a fault .. */
372 [self debugWithFormat:@"found no selector for XML-RPC action %@", _name];
375 r = [NSString stringWithFormat:
376 @"found no XML-RPC method named '%@' "
377 @"(%i parameters, component=%@)",
378 n, [_params count], [self xmlrpcComponentNamespace]];
380 return [NSException exceptionWithName:@"NoSuchXmlRpcMethod"
385 sign = [[self class] instanceMethodSignatureForSelector:sel];
386 invo = [NSInvocation invocationWithMethodSignature:sign];
387 [invo setSelector:sel];
388 [invo setTarget:self];
390 /* more arguments may be passed than supported by the method .. */
391 cnt = [sign numberOfArguments] - 2;
392 cnt = (cnt > (int)[_params count]) ? (int)[_params count] : cnt;
393 for (i = 0; i < cnt; i++) {
394 id param = [_params objectAtIndex:i];
397 is this correct ? shouldnt that break, because the address of
398 param is always the same (who says, that NSInvocation copies the
401 [invo setArgument:¶m atIndex:(i + 2)];
404 /* fill additional selector values with nil ... */
405 if (cnt < ((int)[sign numberOfArguments] - 2)) {
406 static id nilValue = nil;
407 unsigned int oldCnt = cnt;
409 for (i = oldCnt, cnt = ([sign numberOfArguments] - 2); i < cnt; i++)
410 [invo setArgument:&nilValue atIndex:(i + 2)];
414 [invo getReturnValue:&result];
418 - (id<WOActionResults>)performMethodCall:(XmlRpcMethodCall *)_call {
422 resValue = [self performActionNamed:[_call methodName]
423 parameters:[_call parameters]];
424 resValue = [resValue retain];
427 resValue = [self faultFromException:localException
429 if ([[self class] coreOnFault])
431 resValue = [resValue retain];
435 resValue = [resValue autorelease];
437 if ([[self class] coreOnFault]) {
438 if ([resValue isKindOfClass:[NSException class]]) {
442 return [self actionResponseForResult:resValue];
445 /* command context */
447 - (BOOL)hasAuthorizationHeader {
451 if ((rq = [self request]) == nil)
454 if ((cred = [rq headerForKey:@"authorization"]) == nil)
460 - (NSString *)credentials {
465 if ((rq = [self request]) == nil)
467 if ((cred = [rq headerForKey:@"authorization"]) == nil)
470 r = [cred rangeOfString:@" " options:NSBackwardsSearch];
472 [self logWithFormat:@"invalid 'authorization' header: '%@'", cred];
475 return [cred substringFromIndex:(r.location + r.length)];
480 - (NSString *)loggingPrefix {
481 return [NSString stringWithFormat:@"RPC>%@>",
482 NSStringFromClass([self class])];
485 /* reflection (do not define as a category, as other may do this .. */
487 - (NSArray *)system_listMethodsAction {
490 names = [[self class] registeredMethodNames];
491 names = [names sortedArrayUsingSelector:@selector(compare:)];
495 - (NSArray *)system_methodSignatureAction:(NSString *)_method {
496 return [[self class] signaturesForMethodNamed:_method];
499 @end /* NGXmlRpcAction */