]> err.no Git - sope/blob - sope-appserver/NGXmlRpc/NGXmlRpcAction.m
fixed gcc 4.0 warnings
[sope] / sope-appserver / NGXmlRpc / NGXmlRpcAction.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
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>
32 #include "common.h"
33
34
35 @interface NSException(UserInfoExt)
36 - (void)setUserInfo:(NSDictionary *)_ui;
37 @end
38
39 @implementation WOCoreApplication(XmlRpcActionClass)
40
41 - (Class)defaultActionClassForRequest:(WORequest *)_request {
42   return NSClassFromString(@"DirectAction");
43 }
44
45 @end
46
47 @implementation NGXmlRpcAction
48
49 + (int)version {
50   return 1;
51 }
52
53 + (BOOL)coreOnFault {
54 #if DEBUG
55   return [[NSUserDefaults standardUserDefaults] 
56                           boolForKey:@"WOCoreOnXmlRpcFault"];
57 #else
58   return NO;
59 #endif
60 }
61
62 /* initialization */
63
64 - (id)initWithContext:(WOContext *)_ctx {
65   if ((self = [super init])) {
66     self->context = RETAIN(_ctx);
67   }
68   return self;
69 }
70 - (id)init {
71   return [self initWithContext:nil];
72 }
73
74 - (void)dealloc {
75   RELEASE(self->context);
76   [super dealloc];
77 }
78
79 /* sandstorm components */
80
81 - (NSString *)xmlrpcComponentNamespacePrefix {
82   NSString *np;
83   
84   np = [[NSUserDefaults standardUserDefaults]
85                         stringForKey:@"SxDefaultNamespacePrefix"];
86   if ([np length] > 0)
87     return np;
88
89   [self logWithFormat:
90           @"WARNING: SxDefaultNamespacePrefix default is not set !"];
91   
92   np = [(NSHost *)[NSHost currentHost] name];
93   if ([np length] > 0) {
94     if (!isdigit([np characterAtIndex:0])) {
95       NSArray *parts;
96
97       parts = [np componentsSeparatedByString:@"."];
98       if ([parts count] == 0) {
99       }
100       else if ([parts count] == 1)
101         return [parts objectAtIndex:0];
102       else {
103         NSEnumerator *e;
104         BOOL     isFirst = YES;
105         NSString *s;
106         
107         e = [parts reverseObjectEnumerator];
108         while ((s = [e nextObject])) {
109           if (isFirst) {
110             isFirst = NO;
111             np = s;
112           }
113           else {
114             np = [[np stringByAppendingString:@"."] stringByAppendingString:s];
115           }
116         }
117         return np;
118       }
119     }
120   }
121   
122   return @"com.skyrix";
123 }
124 - (NSString *)xmlrpcComponentName {
125   NSString *s;
126
127   s = NSStringFromClass([self class]);
128   if (![s isEqualToString:@"DirectAction"])
129     return s;
130   
131   return [[NSProcessInfo processInfo] processName];
132 }
133
134 - (NSString *)xmlrpcComponentNamespace {
135   NSString *ns, *n;
136   
137   ns = [self xmlrpcComponentNamespacePrefix];
138   n  = [self xmlrpcComponentName];
139   return [[ns stringByAppendingString:@"."] stringByAppendingString:n];
140 }
141
142 /* notifications */
143
144 - (void)awake {
145 }
146 - (void)sleep {
147 }
148
149 - (id)application {
150   return [WOCoreApplication application];
151 }
152
153 - (NSNotificationCenter *)notificationCenter {
154   return [NSNotificationCenter defaultCenter];
155 }
156
157 - (WOContext *)context {
158   if (self->context == nil)
159     self->context = RETAIN([[WOApplication application] context]);
160   return self->context;
161 }
162
163 - (WORequest *)request {
164   return [[self context] request];
165 }
166
167 - (id)session {
168   return [[self context] session];
169 }
170
171 - (id)existingSession {
172   WOContext *ctx = [self context];
173   
174   /* check whether the context has a session */
175   
176   return [ctx hasSession] ? [ctx session] : nil;
177 }
178
179 /* XML-RPC direct action dispatcher ... */
180
181 - (NSString *)authRealm {
182   WOApplication *app = [self application];
183   return [app name];
184 }
185
186 - (id<WOActionResults>)missingAuthAction {
187   WOResponse *resp;
188   NSString *auth;
189
190   auth = [NSString stringWithFormat:@"basic realm=\"%@\"",[self authRealm]];
191   
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];
197 }
198 - (id<WOActionResults>)accessDeniedAction {
199   WOResponse *resp;
200   NSString *auth;
201   
202   auth = [NSString stringWithFormat:@"basic realm=\"%@\"",[self authRealm]];
203   
204   [self logWithFormat:@"access was denied"];
205   
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];
211 }
212
213 - (id<WOActionResults>)actionResponseForResult:(id)resValue {
214   if ([resValue isKindOfClass:[NGAsyncResultProxy class]]) {
215     /* async result ... */
216     return [self responseForAsyncResult:resValue];
217   }
218   else if ([resValue conformsToProtocol:@protocol(WOActionResults)]) {
219     /* a "HTTP" result ... */
220     return resValue;
221   }
222   else {
223     /* an XML-RPC result ... */
224     XmlRpcMethodResponse *mResponse;
225     
226     mResponse = [[[XmlRpcMethodResponse alloc]
227                                         initWithResult:resValue]
228                                         autorelease];
229     return mResponse;
230   }
231 }
232
233 - (void)proxyReady:(NGAsyncResultProxy *)_sender {
234   id<WOActionResults> ares;
235   WOResponse *r;
236   
237   AUTORELEASE(RETAIN(self)); /* keep me around ;-) */
238   
239   //[self debugWithFormat:@"ready: %@", _sender];
240   
241   [_sender setTarget:nil];
242   [_sender setAction:NULL];
243   
244   ares = [self actionResponseForResult:[_sender result]];
245   //[self debugWithFormat:@"  result: %@", ares];
246
247   r = [ares generateResponse];
248   //[self debugWithFormat:@"  response: %@", r];
249
250   [[self notificationCenter]
251          postNotificationName:@"WOAsyncResponseReadyNotification"
252          object:[_sender token]
253          userInfo:[NSDictionary dictionaryWithObject:r
254                                 forKey:@"WOAsyncResponse"]];
255 }
256
257 - (WOResponse *)responseForAsyncResult:(NGAsyncResultProxy *)_proxy {
258   static int cnt = 0;
259   NSString   *token;
260   WOResponse *r;
261   NSDictionary *ui;
262
263   //[self debugWithFormat:@"shall create async result for proxy:\n  %@", _proxy];
264   
265   token = [NSString stringWithFormat:@"0x%08X-%i", _proxy, cnt++];
266   //[self debugWithFormat:@"token: %@", token];
267   ui = [NSDictionary dictionaryWithObject:token
268                      forKey:@"WOAsyncResponseToken"];
269   
270   r = [WOResponse responseWithRequest:[self request]];
271   [r setStatus:20001 /* async response */];
272   [r setUserInfo:ui];
273   
274   /* map token to result proxy ... */
275   [_proxy setTarget:self];
276   [_proxy setAction:@selector(proxyReady:)];
277   [_proxy setToken:token];
278   
279   return r;
280 }
281
282 - (id)faultFromException:(NSException *)_exception
283   methodCall:(XmlRpcMethodCall *)_call
284 {
285   /* add some more information to generic exceptions ... */
286   if (_call) {
287     NSMutableDictionary *ui;
288     
289     ui = [[_exception userInfo] mutableCopy];
290     if (ui == nil) ui = [[NSMutableDictionary alloc] init];
291     
292     [ui setObject:[_call methodName] forKey:@"methodName"];
293     [ui setObject:[_call parameters] forKey:@"methodParameters"];
294     
295     [_exception setUserInfo:ui];
296     RELEASE(ui);
297   }
298
299   [self logWithFormat:@"%s: turning exception into fault %@\n",
300           __PRETTY_FUNCTION__,
301           [_exception description]];
302
303   if ([[self class] coreOnFault])
304     abort();
305   
306   return _exception;
307 }
308 - (id)faultFromException:(NSException *)_exception {
309   return [self faultFromException:_exception methodCall:nil];
310 }
311
312 - (NSArray *)signatureForParameters:(NSArray *)_params {
313   NSMutableArray *ma;
314   unsigned count, i;
315   
316   if ((count = [_params count]) == 0)
317     return [NSArray arrayWithObject:@"*"];
318   
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]];
323   return ma;
324 }
325 - (SEL)selectorForXmlRpcAction:(NSString *)_name
326   parameters:(NSArray *)_params
327 {
328   NSArray *sig = nil;
329
330   if ((sig = [self signatureForParameters:_params]) == nil)
331     [self logWithFormat:@"found not signature for params ..."];
332   
333   return [[self class] selectorForActionNamed:_name
334                        signature:sig];
335 }
336
337 - (NSString *)_methodNameWithoutPrefix:(NSString *)_name {
338   NSString *n;
339   int len;
340
341   if ((n = [self xmlrpcComponentNamespacePrefix]) == nil)
342     return _name;
343   if ((len = [n length]) == 0)
344     return _name;
345   if (![_name hasPrefix:n])
346     return _name;
347   
348   n = _name;
349   _name = [_name substringFromIndex:len];
350   if ([_name hasPrefix:@"."])
351     _name = [_name substringFromIndex:1];
352   return _name;
353 }
354
355 - (id)performActionNamed:(NSString *)_name parameters:(NSArray *)_params {
356   NSMethodSignature *sign;
357   NSInvocation      *invo;
358   id       result = nil;
359   SEL      sel;
360   int      i, cnt;
361   NSString *n;
362   
363   n = _name;
364   _name = [self _methodNameWithoutPrefix:_name];
365   
366   /* generate selector */
367   if ((sel = [self selectorForXmlRpcAction:_name parameters:_params]) ==NULL) {
368     /* return a fault .. */
369     NSString     *r;
370     NSDictionary *ui;
371     
372     [self debugWithFormat:@"found no selector for XML-RPC action %@", _name];
373     
374     ui = nil;
375     r = [NSString stringWithFormat:
376                     @"found no XML-RPC method named '%@' "
377                     @"(%i parameters, component=%@)",
378                     n, [_params count], [self xmlrpcComponentNamespace]];
379     
380     return [NSException exceptionWithName:@"NoSuchXmlRpcMethod"
381                         reason:r
382                         userInfo:ui];
383   }
384   
385   sign = [[self class] instanceMethodSignatureForSelector:sel];
386   invo = [NSInvocation invocationWithMethodSignature:sign];
387   [invo setSelector:sel];
388   [invo setTarget:self];
389   
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];
395     /* 
396        TODO: bjoern
397        is this correct ? shouldnt that break, because the address of
398        param is always the same (who says, that NSInvocation copies the
399        values ???)
400     */
401     [invo setArgument:&param atIndex:(i + 2)];
402   }
403   
404   /* fill additional selector values with nil ... */
405   if (cnt < ((int)[sign numberOfArguments] - 2)) {
406     static id nilValue = nil;
407     unsigned int oldCnt = cnt;
408     
409     for (i = oldCnt, cnt = ([sign numberOfArguments] - 2); i < cnt; i++)
410       [invo setArgument:&nilValue atIndex:(i + 2)];
411   }
412   
413   [invo invoke];
414   [invo getReturnValue:&result];
415   
416   return result;
417 }
418 - (id<WOActionResults>)performMethodCall:(XmlRpcMethodCall *)_call {
419   id resValue;
420   
421   NS_DURING {
422     resValue = [self performActionNamed:[_call methodName]
423                      parameters:[_call parameters]];
424     resValue = [resValue retain];
425   }
426   NS_HANDLER {
427     resValue = [self faultFromException:localException
428                      methodCall:_call];
429     if ([[self class] coreOnFault])
430       abort();
431     resValue = [resValue retain];
432   }
433   NS_ENDHANDLER;
434   
435   resValue = [resValue autorelease];
436   
437   if ([[self class] coreOnFault]) {
438     if ([resValue isKindOfClass:[NSException class]]) {
439       abort();
440     }
441   }
442   return [self actionResponseForResult:resValue];
443 }
444
445 /* command context */
446
447 - (BOOL)hasAuthorizationHeader {
448   WORequest *rq;
449   NSString  *cred;
450   
451   if ((rq = [self request]) == nil)
452     return NO;
453
454   if ((cred = [rq headerForKey:@"authorization"]) == nil)
455     return NO;
456   
457   return YES;
458 }
459
460 - (NSString *)credentials {
461   WORequest *rq;
462   NSString  *cred;
463   NSRange   r;
464   
465   if ((rq = [self request]) == nil)
466     return nil;
467   if ((cred = [rq headerForKey:@"authorization"]) == nil)
468     return nil;
469   
470   r = [cred rangeOfString:@" " options:NSBackwardsSearch];
471   if (r.length == 0) {
472     [self logWithFormat:@"invalid 'authorization' header: '%@'", cred];
473     return nil;
474   }
475   return [cred substringFromIndex:(r.location + r.length)];
476 }
477
478 /* logging */
479
480 - (NSString *)loggingPrefix {
481   return [NSString stringWithFormat:@"RPC>%@>",
482                      NSStringFromClass([self class])];
483 }
484
485 /* reflection (do not define as a category, as other may do this .. */
486
487 - (NSArray *)system_listMethodsAction {
488   NSArray *names;
489   
490   names = [[self class] registeredMethodNames];
491   names = [names sortedArrayUsingSelector:@selector(compare:)];
492   
493   return names;
494 }
495 - (NSArray *)system_methodSignatureAction:(NSString *)_method {
496   return [[self class] signaturesForMethodNamed:_method];
497 }
498
499 @end /* NGXmlRpcAction */