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