]> err.no Git - sope/blob - xmlrpc_call/xmlrpc_call.m
moved xmlrpc_call to own top-level directory
[sope] / xmlrpc_call / xmlrpc_call.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
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
22 /*
23   xmlrpc_call
24   
25   A neat tool to call XML-RPC servers from the shell.
26
27   Defaults:
28     login     - string
29     password  - string
30     forceauth - bool   - send the credentials in the first request!
31 */
32
33 #include <NGXmlRpc/NGXmlRpcClient.h>
34
35 #include <NGExtensions/NSString+Ext.h>
36 #include <NGStreams/NGStreams.h>
37 #include <NGStreams/NGNet.h>
38
39 #if !LIB_FOUNDATION_LIBRARY
40 #  include <NGObjWeb/UnixSignalHandler.h>
41 #endif
42
43 #include "common.h"
44
45 @class WOResponse;
46
47 @interface NGXmlRpcClient(CallFailed)
48 - (id)callFailed:(WOResponse *)_response;
49 @end /* NGXmlRpcClient */
50
51 @class HandleCredentialsClient;
52
53 @interface XmlRpcClient : NSObject
54 {
55   HandleCredentialsClient  *client;
56   NSString                 *methodName;
57   NSArray                  *parameters;
58 }
59
60 /* initialization */
61
62 - (id)initWithArguments:(NSArray *)_arguments;
63 - (void)initMethodCall:(NSArray *)_arguments;
64 - (BOOL)initXmlRpcClientWithStringURL:(NSString *)_url;
65
66 - (int)run;
67 - (void)help:(NSString *)pn;
68
69 - (void)printElement:(id)_element;
70 - (void)printDictionary:(NSDictionary *)_dict;
71 - (void)printArray:(NSArray *)_array;
72
73 @end /* XmlRpcClient */
74
75 @implementation NSObject(Printing)
76
77 - (void)printWithTool:(XmlRpcClient *)_tool {
78   printf("%s\n", [[self description] cString]);
79 }
80
81 @end /* NSObject(Printing) */
82
83 @implementation NSData(Printing)
84
85 - (void)printWithTool:(XmlRpcClient *)_tool {
86   fwrite([self bytes], [self length], 1, stdout);
87 }
88
89 @end /* NSData(Printing) */
90
91 @implementation NSDictionary(Printing)
92
93 - (void)printWithTool:(XmlRpcClient *)_tool {
94   [_tool printDictionary:self];
95 }
96
97 @end /* NSDictionary(Printing) */
98
99 @implementation NSArray(Printing)
100
101 - (void)printWithTool:(XmlRpcClient *)_tool {
102   [_tool printArray:self];
103 }
104
105 @end /* NSArray(Printing) */
106
107 @implementation NSException(Printing)
108
109 - (void)printWithTool:(XmlRpcClient *)_tool {
110   printf("Exception caught\nName  : %s\nReason: %s\n",
111          [[self name] cString], [[self reason] cString]);
112 }
113
114 @end /* NSException(Printing) */
115
116 @interface HandleCredentialsClient : NGXmlRpcClient
117 {
118   NSString *defLogin;
119   NSString *defPassword;
120 }
121
122 /* accessors */
123
124 - (void)setDefLogin:(NSString *)_login;
125 - (void)setDefPassword:(NSString *)_pwd;
126
127 @end /* HandleCredentialsClient */
128
129 #include <unistd.h>
130 #include <NGObjWeb/WOResponse.h>
131
132 @implementation HandleCredentialsClient
133
134 - (void)dealloc {
135   [self->defLogin    release];
136   [self->defPassword release];
137   [super dealloc];
138 }
139
140 /* accessors */
141
142 - (void)setDefLogin:(NSString *)_login {
143   ASSIGNCOPY(self->defLogin, _login);
144 }
145 - (void)setDefPassword:(NSString *)_pwd {
146   ASSIGNCOPY(self->defPassword, _pwd);
147 }
148
149 /* prompting */
150
151 - (NSString *)prompt:(NSString *)_prompt {
152   NSString *login;
153   char clogin[256];
154   
155   fprintf(stderr, "%s", [_prompt cString]);
156   fflush(stderr);
157   fgets(clogin, 200, stdin);
158   clogin[strlen(clogin) - 1] = '\0';
159   login = [NSString stringWithCString:clogin];
160   return login;
161 }
162
163 - (NSString *)promptPassword:(NSString *)_prompt {
164   NSString *pwd;
165   char     *cpwd;
166
167   cpwd = getpass("password: ");
168   pwd = [NSString stringWithCString:cpwd];
169   return pwd;
170 }
171
172 - (id)callFailed:(WOResponse *)_response {
173   if ([_response status] == 401) {
174     NSString *wwwauth;
175     NSString *user;
176     NSString *pass;
177     
178     wwwauth = [_response headerForKey:@"www-authenticate"];
179     if ([[wwwauth lowercaseString] hasPrefix:@"digest"])
180       [self logWithFormat:@"Digest authentication:\n'%@'", wwwauth];
181
182     // TODO: test credentials of URL
183     
184     if (self->defLogin) {
185       user = [self->defLogin autorelease];
186       self->defLogin = nil;
187     }
188     else
189       user = [self prompt:@"login:    "];
190     
191     if (self->defPassword) {
192       pass = [self->defPassword autorelease];
193       self->defPassword = nil;
194     }
195     else
196       pass = [self promptPassword:@"password: "];
197     
198     [self setUserName:user];
199     [self setPassword:pass];
200     
201     /* this "should" return some kind of "need-pwd" object ... */
202     return nil;
203   }
204   else {
205     return [super callFailed:_response];
206   }
207 }
208
209 @end /* HandleCredentialsClient */
210
211 #define EXIT_FAIL -1
212
213 @implementation XmlRpcClient
214
215 /* initialization */
216
217 - (id)init {
218   return [self initWithArguments:nil];
219 }
220
221 - (id)initWithArguments:(NSArray *)_arguments {
222   if ((self = [super init])) {
223     NSUserDefaults *ud;
224     NSString *s;
225     int argc;
226
227     argc = [_arguments count];
228     if(argc == 1) {
229       [self help:[_arguments objectAtIndex:0]];
230       return nil;
231     }
232     
233     s = [_arguments objectAtIndex:1];
234     if (![self initXmlRpcClientWithStringURL:s]) {
235       printf("Error initializing the XML-RPC client\n");
236       [self release];
237       return nil;
238     }
239     
240     if (argc > 2) {
241       [self initMethodCall:_arguments];
242     }
243     else {
244       self->methodName = @"system.listMethods";
245       self->parameters = nil;
246     }
247     
248     ud = [NSUserDefaults standardUserDefaults];
249     if ([ud boolForKey:@"forceauth"]) {
250       [self->client setUserName:[ud stringForKey:@"login"]];
251       [self->client setPassword:[ud stringForKey:@"password"]];
252     }
253     else {
254       [self->client setDefLogin:[ud stringForKey:@"login"]];
255       [self->client setDefPassword:[ud stringForKey:@"password"]];
256     }
257   }
258   return self;
259 }
260
261 - (BOOL)initXmlRpcClientWithStringURL:(NSString *)_url {
262   NSURL *url = nil;
263   
264   if (![_url isAbsoluteURL]) {
265     /* make a raw, Unix domain socket connection */
266     NGLocalSocketAddress *addr;
267     
268     addr = [NGLocalSocketAddress addressWithPath:_url];
269     self->client = [[HandleCredentialsClient alloc] initWithRawAddress:addr];
270     return YES;
271   }
272   
273   if ((url = [NSURL URLWithString:_url]) != nil) {
274 #if 0
275     if ((uri = [url path]) == nil)
276       uri = @"/RPC2";
277 #endif
278     
279     self->client = [[HandleCredentialsClient alloc] initWithURL:url];
280     return YES;
281
282   }
283   else {
284     printf("Invalid URL\n");
285     return NO;
286   }
287 }
288
289 - (void)initMethodCall:(NSArray *)_arguments {
290   self->methodName = [_arguments objectAtIndex:2];
291
292   if ([_arguments count] > 2) {
293     NSRange range = NSMakeRange(3, [_arguments count] - 3);
294     self->parameters = [[_arguments subarrayWithRange:range] retain];
295   }
296 }
297
298 - (void)dealloc {
299   [self->client      release];
300   [self->methodName  release];
301   [self->parameters  release];
302   [super dealloc];
303 }
304
305 /* printing */
306
307 - (void)printElement:(id)element {
308   [element printWithTool:self];
309 }
310
311 /* printing objects */
312
313 - (void)printDictionary:(NSDictionary *)_dict {
314   NSEnumerator *dictEnum;
315   id dictKey;
316
317   dictEnum = [_dict keyEnumerator];
318
319   while((dictKey = [dictEnum nextObject])) {
320     printf("%s=", [dictKey cString]);    
321     [self printElement:[_dict objectForKey:dictKey]];
322   }
323 }
324
325 - (void)printArray:(NSArray *)_array {
326   NSEnumerator *arrayEnum;
327   id arrayElem;
328   
329   arrayEnum = [_array objectEnumerator];
330   while((arrayElem = [arrayEnum nextObject])) {
331     [self printElement:arrayElem];
332   }
333 }
334
335 - (void)help:(NSString *)pn {
336   fprintf(stderr,
337           "usage:    %s <url> [<method-name>] [<arg1>,...]\n"
338           "  sample: %s http://localhost:20000/RPC2 bc 1 2\n",
339           [pn cString], [pn cString]);
340 }
341
342 - (int)run {
343   int  exitCode  = 0;
344   int  loopCount = 0;
345   id   result;
346
347   if (self->client == nil) {
348     NSLog(@"missing XML-RPC client object ...");
349     return EXIT_FAIL;
350   }
351   
352   do {
353     result = [self->client
354                   invokeMethodNamed:self->methodName
355                   parameters:self->parameters];
356     loopCount++;
357   }
358   while ((result == nil) && loopCount < 20);
359
360   if (result == nil) {
361     NSLog(@"call failed, no result (looped %i times) ?!", loopCount);
362     return EXIT_FAIL;
363   }
364   
365   [self printElement:result];
366   
367   if ([result isKindOfClass:[NSException class]]) {
368     exitCode = 255;
369   }
370
371   return exitCode;
372 }
373
374 @end /* XmlRpcClient */
375
376 int main(int argc, char **argv, char **env) {
377   NSAutoreleasePool *pool;
378   XmlRpcClient      *client;
379   NSArray           *arguments;
380   int               exitCode;
381   
382   pool = [[NSAutoreleasePool alloc] init];
383 #if LIB_FOUNDATION_LIBRARY || defined(GS_PASS_ARGUMENTS)
384   [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
385 #endif
386   
387   /* our sockets need to know, if the PIPE is broken */
388   signal(SIGPIPE, SIG_IGN);
389
390   arguments = [[NSProcessInfo processInfo] argumentsWithoutDefaults];
391   if ((client = [[XmlRpcClient alloc] initWithArguments:arguments]) == nil)
392     exitCode = 2;
393   else
394     exitCode =  [client run];
395   
396   [client release];
397   [pool   release];
398   
399   exit(exitCode);
400   return exitCode;
401 }