]> err.no Git - sope/blob - sope-appserver/NGXmlRpc/WODirectAction+XmlRpc.m
INSTALL_ROOT_DIR patch
[sope] / sope-appserver / NGXmlRpc / WODirectAction+XmlRpc.m
1 /*
2   Copyright (C) 2000-2003 SKYRIX Software AG
3
4   This file is part of OGo
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "WODirectAction+XmlRpc.h"
24 #include <NGXmlRpc/NSObject+Reflection.h>
25 #include <NGXmlRpc/XmlRpcMethodCall+WO.h>
26 #include <NGXmlRpc/XmlRpcMethodResponse+WO.h>
27 #include <NGObjWeb/WORequest.h>
28 #include "common.h"
29
30 @implementation WODirectAction(XmlRpc)
31
32 static int CoreOnException = -1;
33
34 - (NSString *)xmlrpcComponentNamespacePrefix {
35   NSString *np;
36   
37   np = [[NSUserDefaults standardUserDefaults]
38                         stringForKey:@"SxDefaultNamespacePrefix"];
39   if ([np length] > 0)
40     return np;
41
42   [self logWithFormat:
43           @"WARNING: SxDefaultNamespacePrefix default is not set !"];
44   
45   np = [(NSHost *)[NSHost currentHost] name];
46   if ([np length] > 0) {
47     if (!isdigit([np characterAtIndex:0])) {
48       NSArray *parts;
49
50       parts = [np componentsSeparatedByString:@"."];
51       if ([parts count] == 0) {
52       }
53       else if ([parts count] == 1)
54         return [parts objectAtIndex:0];
55       else {
56         NSEnumerator *e;
57         BOOL     isFirst = YES;
58         NSString *s;
59         
60         e = [parts reverseObjectEnumerator];
61         while ((s = [e nextObject])) {
62           if (isFirst) {
63             isFirst = NO;
64             np = s;
65           }
66           else {
67             np = [[np stringByAppendingString:@"."] stringByAppendingString:s];
68           }
69         }
70         return np;
71       }
72     }
73   }
74   
75   return @"com.skyrix";
76 }
77 - (NSString *)xmlrpcComponentName {
78   NSString *s;
79
80   s = NSStringFromClass([self class]);
81   if (![s isEqualToString:@"DirectAction"])
82     return s;
83   
84   return [[NSProcessInfo processInfo] processName];
85 }
86
87 - (NSString *)xmlrpcComponentNamespace {
88   NSString *ns, *n;
89   
90   ns = [self xmlrpcComponentNamespacePrefix];
91   n  = [self xmlrpcComponentName];
92   return [[ns stringByAppendingString:@"."] stringByAppendingString:n];
93 }
94
95 - (NSArray *)_methodActionNames {
96   NSMutableArray *ma;
97   NSEnumerator   *sels;
98   NSString       *sel;
99
100   sels = [[self respondsToSelectors] objectEnumerator];
101
102   ma = [NSMutableArray arrayWithCapacity:16];
103   while ((sel = [sels nextObject])) {
104     unsigned idx, len;
105     NSString *actionName;
106     NSRange rng;
107     
108     rng = [sel rangeOfString:@"Action"];
109     if (rng.length <= 0) continue;
110     
111     actionName = sel;
112     
113     /* ensure that only dots are following the 'Action' */
114     for (idx = (rng.location + rng.length), len = [sel length]; 
115          idx < len; idx++) {
116       unichar c = [sel characterAtIndex:idx];
117       if (c != ':') {
118         actionName = nil;
119         break;
120       }
121     }
122     
123     /* go to next selector if ... */
124     if ([actionName length] == 0) continue;
125     
126     /* add to reflection set */
127     [ma addObject:actionName];
128   }
129   return [[ma copy] autorelease];
130 }
131
132 - (NSString *)selectorForXmlRpcAction:(NSString *)_name {
133   NSString *actionName;
134   NSString *p;
135
136   actionName = @"Action";
137
138   /* check component namespace and strip it ;-) */
139   
140   p = [self xmlrpcComponentNamespace];
141   
142   if ([p length] > 0) {
143     if ([_name hasPrefix:@"system."])
144       ;
145     else if ([_name hasPrefix:p]) {
146       _name = [_name substringFromIndex:[p length]];
147       if ([_name length] > 0) {
148         if ([_name characterAtIndex:0] == '.')
149           _name = [_name substringFromIndex:1];
150       }
151     }
152     else {
153       [self logWithFormat:
154             @"WARNING: tried to invoke XML-RPC method from "
155             @"different component (namespace=%@): %@",
156             p, _name];
157     }
158   }
159   
160   /* replace namespace points by '_' */
161   
162   _name      = [_name stringByReplacingString:@"." withString:@"_"];
163   actionName = [_name stringByAppendingString:actionName];
164   
165   /* finished */
166   return actionName;
167 }
168
169 - (NSString *)selectorForXmlRpcAction:(NSString *)_name
170   parameters:(NSArray *)_params
171 {
172   NSString *actionName;
173   int i, cnt;
174   
175   actionName = [self selectorForXmlRpcAction:_name];
176   
177   /* append ':' for each parameter */
178
179   switch ((cnt = [_params count])) {
180     case 0:
181       break;
182     case 1:
183       actionName = [actionName stringByAppendingString:@":"];
184       break;
185     case 2:
186       actionName = [actionName stringByAppendingString:@"::"];
187       break;
188     case 3:
189       actionName = [actionName stringByAppendingString:@":::"];
190       break;
191     case 4:
192       actionName = [actionName stringByAppendingString:@"::::"];
193       break;
194       
195     default:
196       for (i = 0, cnt = [_params count]; i < cnt; i++)
197         actionName = [actionName stringByAppendingString:@":"];
198       break;
199   }
200   
201   /* finished */
202   return actionName;
203 }
204
205 - (id)performActionNamed:(NSString *)_name parameters:(NSArray *)_params {
206   NSMethodSignature *sign;
207   NSInvocation      *invo;
208   NSString          *actionName;
209   id   result = nil;
210   SEL  sel;
211   int  i, cnt = 0;
212   
213   /* generate selector */
214   actionName = [self selectorForXmlRpcAction:_name parameters:_params];
215   sel = NSSelectorFromString(actionName);
216   
217   if (![self respondsToSelector:sel]) {
218     NSEnumerator *actEnum;
219     NSString     *name    = nil;
220     NSString     *act     = nil;
221     
222     actEnum    = [[self _methodActionNames] objectEnumerator];
223     name       = [actionName stringByReplacingString:@":" withString:@""];
224     actionName = nil;
225     while ((act = [actEnum nextObject])) {
226       NSString *tmp = [act stringByReplacingString:@":" withString:@""];
227       
228       if ([tmp isEqualToString:name]) actionName = act;
229     }
230     sel = NSSelectorFromString(actionName);
231     
232     if (sel == NULL) {
233       /* Note: NULL selectors are not caught by MacOSX -respondsToSel: ! */
234       [self logWithFormat:@"no such XMLRPC action: '%@'", _name];
235       return [NSException exceptionWithName:@"NoSuchAction"
236                           reason:@"action not implemented"
237                           userInfo:nil];
238     }
239     else if (![self respondsToSelector:sel]) {
240       [self logWithFormat:@"no such XMLRPC action: '%@' (selector=%@)",
241               _name, NSStringFromSelector(sel)];
242       
243       return [NSException exceptionWithName:@"NoSuchAction"
244                           reason:@"action not implemented"
245                           userInfo:nil];
246     }
247     else {
248       // count the ':'
249       cnt = [[actionName componentsSeparatedByString:@":"] count] - 1;
250     }
251   }
252   sign = [[self class] instanceMethodSignatureForSelector:sel];
253   invo = [NSInvocation invocationWithMethodSignature:sign];
254   [invo setSelector:sel];
255   if (cnt == 0) cnt = ([sign numberOfArguments] - 2);
256   
257   [invo setTarget:self];
258   
259   cnt = (cnt > (int)[_params count]) ? (int)[_params count] : cnt;
260   
261   for (i = 0; i < cnt; i++) {
262     id param = [_params objectAtIndex:i];
263     [invo setArgument:&param atIndex:(i + 2)];
264   }
265   // TODO(hh): should fill the remaining args when less params available ?
266   
267   [invo invoke];
268   [invo getReturnValue:&result];
269
270   return result;
271 }
272
273 - (id)_faultForException:(NSException *)_exception {
274   if (CoreOnException == -1) {
275     // TODO: add default
276     CoreOnException =   
277       [[NSUserDefaults standardUserDefaults] 
278                        boolForKey:@"WOCoreOnXmlRpcFault"] ? 1 : 0;
279   }
280   
281   if (CoreOnException) {
282     [self logWithFormat:@"core on exception: %@", _exception];
283     abort();
284     return nil;
285   }
286   else {
287     [self logWithFormat:@"turn exception into fault: %@", _exception];
288     return _exception;
289   }
290 }
291
292 - (id)RPC2Action {
293   XmlRpcMethodCall     *call;
294   XmlRpcMethodResponse *mResponse;
295   id                   result;
296   
297   if (![[[self request] method] isEqualToString:@"POST"]) {
298     /* only POST is allowed for direct XML-RPC requests ! */
299     
300     if ([[[self request] method] isEqualToString:@"GET"])
301       return [self RPC2InfoPageAction];
302     
303     return nil;
304   }
305
306   call = [XmlRpcMethodCall alloc];
307   call = [[call initWithRequest:[self request]] autorelease];
308   
309   if (call == nil) {
310     WORequest *rq;
311     NSData    *content;
312     
313     rq      = [self request];
314     content = [rq content];
315     
316     [self logWithFormat:@"couldn't decode XMLRPC content:\n"];
317     [self logWithFormat:@"  content-len: %d", [content length]];
318     [self logWithFormat:@"  encoding:    %d", [rq contentEncoding]];
319     return nil;
320   }
321   
322   [self debugWithFormat:@"decoded XMLRPC call: %@", call];
323   
324   NS_DURING {
325     result = [[self performActionNamed:[call methodName]
326                     parameters:[call parameters]]
327                     retain];
328   }
329   NS_HANDLER
330     result = [[self _faultForException:localException] retain];
331   NS_ENDHANDLER;
332   
333   mResponse =
334     [[[XmlRpcMethodResponse alloc] initWithResult:result] autorelease];
335   
336   [result release]; result = nil;
337   
338   return [mResponse generateResponse];
339 }
340 - (id<WOActionResults>)xmlrpcAction {
341   [self debugWithFormat:@"deprecated, please use /RPC2 as direct action !"];
342   return [self RPC2Action];
343 }
344
345 @end /* WODirectAction(XmlRpc) */