initial load of upstream version 1.06.32
[xmlrpc-c] / tools / xmlrpc / xmlrpc.c
1 /* Make an XML-RPC call.
2
3    User specifies details of the call on the command line.
4
5    We print the result on Standard Output.
6
7    Example:
8
9      $ xmlrpc http://localhost:8080/RPC2 sample.add i/3 i/5
10      Result:
11        Integer: 8
12
13      $ xmlrpc localhost:8080 sample.add i/3 i/5
14      Result:
15        Integer: 8
16
17    This is just the beginnings of this program.  It should be extended
18    to deal with all types of parameters and results.
19
20    An example of a good syntax for parameters would be:
21
22      $ xmlrpc http://www.oreillynet.com/meerkat/xml-rpc/server.php \
23          meerkat.getItems \
24          struct/{search:linux,descriptions:i/76,time_period:12hour}
25      Result:
26        Array:
27          Struct:
28            title: String: DatabaseJournal: OpenEdge-Based Finance ...
29            link: String: http://linuxtoday.com/news_story.php3?ltsn=...
30            description: String: "Finance application with embedded ...
31          Struct:
32            title: ...
33            link: ...
34            description: ...
35
36 */
37
38 #define _GNU_SOURCE
39
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43
44 #include "config.h"  /* information about this build environment */
45 #include "casprintf.h"
46 #include "mallocvar.h"
47 #include "cmdline_parser.h"
48 #include "dumpvalue.h"
49
50 #include "xmlrpc-c/base.h"
51 #include "xmlrpc-c/client.h"
52
53 #define NAME "xmlrpc command line program"
54 #define VERSION "1.0"
55
56 struct cmdlineInfo {
57     const char *  url;
58     const char *  username;
59     const char *  password;
60     const char *  methodName;
61     unsigned int  paramCount;
62     const char ** params;
63         /* Array of parameters, in order.  Has 'paramCount' entries. */
64     const char *  transport;
65         /* Name of XML transport he wants to use.  NULL if he has no 
66            preference.
67         */
68     const char *  curlinterface;
69         /* "network interface" parameter for the Curl transport.  (Not
70            valid if 'transport' names a non-Curl transport).
71         */
72     xmlrpc_bool   curlnoverifypeer;
73     xmlrpc_bool   curlnoverifyhost;
74     const char *  curluseragent;
75 };
76
77
78
79 static void 
80 die_if_fault_occurred (xmlrpc_env * const envP) {
81     if (envP->fault_occurred) {
82         fprintf(stderr, "Error: %s (%d)\n",
83                 envP->fault_string, envP->fault_code);
84         exit(1);
85     }
86 }
87
88
89
90 static void GNU_PRINTF_ATTR(2,3)
91 setError(xmlrpc_env * const envP, const char format[], ...) {
92     va_list args;
93     const char * faultString;
94
95     va_start(args, format);
96
97     cvasprintf(&faultString, format, args);
98     va_end(args);
99
100     xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR, faultString);
101
102     strfree(faultString);
103 }
104       
105
106
107 static void
108 processArguments(xmlrpc_env *         const envP,
109                  cmdlineParser        const cp,
110                  struct cmdlineInfo * const cmdlineP) {
111
112     if (cmd_argumentCount(cp) < 2)
113         setError(envP, "Not enough arguments.  Need at least a URL and "
114                  "method name.");
115     else {
116         unsigned int i;
117         
118         cmdlineP->url        = cmd_getArgument(cp, 0);
119         cmdlineP->methodName = cmd_getArgument(cp, 1);
120         cmdlineP->paramCount = cmd_argumentCount(cp) - 2;
121         MALLOCARRAY(cmdlineP->params, cmdlineP->paramCount);
122         for (i = 0; i < cmdlineP->paramCount; ++i)
123             cmdlineP->params[i] = cmd_getArgument(cp, i+2);
124     }
125 }
126
127
128
129 static void
130 chooseTransport(xmlrpc_env *  const envP ATTR_UNUSED,
131                 cmdlineParser const cp,
132                 const char ** const transportPP) {
133     
134     const char * transportOpt = cmd_getOptionValueString(cp, "transport");
135
136     if (transportOpt) {
137         *transportPP = transportOpt;
138     } else {
139         if (cmd_optionIsPresent(cp, "curlinterface") || 
140             cmd_optionIsPresent(cp, "curlnoverifypeer") ||
141             cmd_optionIsPresent(cp, "curlnoverifyhost") ||
142             cmd_optionIsPresent(cp, "curluseragent"))
143
144             *transportPP = strdup("curl");
145         else
146             *transportPP = NULL;
147     }
148 }
149
150
151
152 static void
153 parseCommandLine(xmlrpc_env *         const envP,
154                  int                  const argc,
155                  const char **        const argv,
156                  struct cmdlineInfo * const cmdlineP) {
157
158     cmdlineParser const cp = cmd_createOptionParser();
159
160     const char * error;
161
162     cmd_defineOption(cp, "transport",        OPTTYPE_STRING);
163     cmd_defineOption(cp, "username",         OPTTYPE_STRING);
164     cmd_defineOption(cp, "password",         OPTTYPE_STRING);
165     cmd_defineOption(cp, "curlinterface",    OPTTYPE_STRING);
166     cmd_defineOption(cp, "curlnoverifypeer", OPTTYPE_STRING);
167     cmd_defineOption(cp, "curlnoverifyhost", OPTTYPE_STRING);
168     cmd_defineOption(cp, "curluseragent",    OPTTYPE_STRING);
169
170     cmd_processOptions(cp, argc, argv, &error);
171
172     if (error) {
173         setError(envP, "Command syntax error.  %s", error);
174         strfree(error);
175     } else {
176         cmdlineP->username  = cmd_getOptionValueString(cp, "username");
177         cmdlineP->password  = cmd_getOptionValueString(cp, "password");
178
179         if (cmdlineP->username && !cmdlineP->password)
180             setError(envP, "When you specify -username, you must also "
181                      "specify -password.");
182         else {
183             chooseTransport(envP, cp, &cmdlineP->transport);
184
185             cmdlineP->curlinterface = 
186                 cmd_getOptionValueString(cp, "curlinterface");
187             cmdlineP->curlnoverifypeer =
188                 cmd_optionIsPresent(cp, "curlnoverifypeer");
189             cmdlineP->curlnoverifyhost =
190                 cmd_optionIsPresent(cp, "curlnoverifyhost");
191             cmdlineP->curluseragent =
192                 cmd_getOptionValueString(cp, "curluseragent");
193
194             if ((!cmdlineP->transport || 
195                  strcmp(cmdlineP->transport, "curl") != 0)
196                 &&
197                 (cmdlineP->curlinterface ||
198                  cmdlineP->curlnoverifypeer ||
199                  cmdlineP->curlnoverifyhost ||
200                  cmdlineP->curluseragent))
201                 setError(envP, "You may not specify a Curl transport "
202                          "option unless you also specify -transport=curl");
203
204             processArguments(envP, cp, cmdlineP);
205         }
206     }
207     cmd_destroyOptionParser(cp);
208 }
209
210
211
212 static void
213 freeCmdline(struct cmdlineInfo const cmdline) {
214
215     unsigned int i;
216     
217     strfree(cmdline.url);
218     strfree(cmdline.methodName);
219     if (cmdline.transport)
220         strfree(cmdline.transport);
221     if (cmdline.curlinterface)
222         strfree(cmdline.curlinterface);
223     if (cmdline.curluseragent)
224         strfree(cmdline.curluseragent);
225     if (cmdline.username)
226         strfree(cmdline.username);
227     if (cmdline.password)
228         strfree(cmdline.password);
229     for (i = 0; i < cmdline.paramCount; ++i)
230         strfree(cmdline.params[i]);
231 }
232
233
234
235 static void
236 computeUrl(const char *  const urlArg,
237            const char ** const urlP) {
238
239     if (strstr(urlArg, "://") != 0)
240         casprintf(urlP, "%s", urlArg);
241     else
242         casprintf(urlP, "http://%s/RPC2", urlArg);
243 }
244
245
246
247 static void
248 buildString(xmlrpc_env *    const envP,
249             const char *    const valueString,
250             xmlrpc_value ** const paramPP) {
251
252     *paramPP = xmlrpc_build_value(envP, "s", valueString);
253 }
254
255
256
257 static void
258 buildInt(xmlrpc_env *    const envP,
259          const char *    const valueString,
260          xmlrpc_value ** const paramPP) {
261
262     if (strlen(valueString) < 1)
263         setError(envP, "Integer argument has nothing after the 'i/'");
264     else {
265         long value;
266         char * tailptr;
267         
268         value = strtol(valueString, &tailptr, 10);
269
270         if (*tailptr != '\0')
271             setError(envP, 
272                      "Integer argument has non-digit crap in it: '%s'",
273                      tailptr);
274         else
275             *paramPP = xmlrpc_build_value(envP, "i", value);
276     }
277 }
278
279
280
281 static void
282 buildDouble(xmlrpc_env *    const envP,
283             const char *    const valueString,
284             xmlrpc_value ** const paramPP) {
285
286     if (strlen(valueString) < 1)
287         setError(envP, "\"Double\" argument has nothing after the 'd/'");
288     else {
289         double value;
290         char * tailptr;
291
292         value = strtod(valueString, &tailptr);
293
294         if (*tailptr != '\0')
295             setError(envP, 
296                      "\"Double\" argument has non-decimal crap in it: '%s'",
297                      tailptr);
298         else
299             *paramPP = xmlrpc_build_value(envP, "d", value);
300     }
301 }
302
303
304
305 static void
306 buildBool(xmlrpc_env *    const envP,
307           const char *    const valueString,
308           xmlrpc_value ** const paramPP) {
309
310     if (strcmp(valueString, "t") == 0 ||
311         strcmp(valueString, "true") == 0)
312         *paramPP = xmlrpc_build_value(envP, "b", 1);
313     else if (strcmp(valueString, "f") == 0 ||
314         strcmp(valueString, "false") == 0)
315         *paramPP = xmlrpc_build_value(envP, "b", 0);
316     else
317         setError(envP, "Boolean argument has unrecognized value '%s'.  "
318                  "recognized values are 't', 'f', 'true', and 'false'.",
319                  valueString);
320
321
322
323
324 static void
325 buildNil(xmlrpc_env *    const envP,
326          const char *    const valueString,
327          xmlrpc_value ** const paramPP) {
328
329     if (strlen(valueString) > 0)
330         setError(envP, "Nil argument has something after the 'n/'");
331     else {
332         *paramPP = xmlrpc_build_value(envP, "n");
333     }
334 }
335
336
337
338 static void
339 computeParameter(xmlrpc_env *    const envP,
340                  const char *    const paramArg,
341                  xmlrpc_value ** const paramPP) {
342
343     if (strncmp(paramArg, "s/", 2) == 0)
344         buildString(envP, &paramArg[2], paramPP);
345     else if (strncmp(paramArg, "i/", 2) == 0) 
346         buildInt(envP, &paramArg[2], paramPP);
347     else if (strncmp(paramArg, "d/", 2) == 0) 
348         buildDouble(envP, &paramArg[2], paramPP);
349     else if (strncmp(paramArg, "b/", 2) == 0)
350         buildBool(envP, &paramArg[2], paramPP);
351     else if (strncmp(paramArg, "n/", 2) == 0)
352         buildNil(envP, &paramArg[2], paramPP);
353     else {
354         /* It's not in normal type/value format, so we take it to be
355            the shortcut string notation 
356         */
357         buildString(envP, paramArg, paramPP);
358     }
359 }
360
361
362
363 static void
364 computeParamArray(xmlrpc_env *    const envP,
365                   unsigned int    const paramCount,
366                   const char **   const params,
367                   xmlrpc_value ** const paramArrayPP) {
368     
369     unsigned int i;
370
371     xmlrpc_value * paramArrayP;
372
373     paramArrayP = xmlrpc_build_value(envP, "()");
374
375     for (i = 0; i < paramCount && !envP->fault_occurred; ++i) {
376         xmlrpc_value * paramP;
377
378         computeParameter(envP, params[i], &paramP);
379
380         if (!envP->fault_occurred) {
381             xmlrpc_array_append_item(envP, paramArrayP, paramP);
382
383             xmlrpc_DECREF(paramP);
384         }
385     }
386     *paramArrayPP = paramArrayP;
387 }
388
389
390
391 static void
392 dumpResult(xmlrpc_value * const resultP) {
393
394     printf("Result:\n\n");
395
396     dumpValue("", resultP);
397 }
398
399
400
401 static void
402 doCall(xmlrpc_env *               const envP,
403        const char *               const transport,
404        const char *               const curlinterface,
405        xmlrpc_bool                const curlnoverifypeer,
406        xmlrpc_bool                const curlnoverifyhost,
407        const char *               const curluseragent,
408        const xmlrpc_server_info * const serverInfoP,
409        const char *               const methodName,
410        xmlrpc_value *             const paramArrayP,
411        xmlrpc_value **            const resultPP) {
412     
413     struct xmlrpc_clientparms clientparms;
414
415     XMLRPC_ASSERT(xmlrpc_value_type(paramArrayP) == XMLRPC_TYPE_ARRAY);
416
417     clientparms.transport = transport;
418
419     if (transport && strcmp(transport, "curl") == 0) {
420         struct xmlrpc_curl_xportparms * curlXportParmsP;
421         MALLOCVAR(curlXportParmsP);
422
423         curlXportParmsP->network_interface = curlinterface;
424         curlXportParmsP->no_ssl_verifypeer = curlnoverifypeer;
425         curlXportParmsP->no_ssl_verifyhost = curlnoverifyhost;
426         curlXportParmsP->user_agent        = curluseragent;
427         
428         clientparms.transportparmsP = (struct xmlrpc_xportparms *) 
429             curlXportParmsP;
430         clientparms.transportparm_size = XMLRPC_CXPSIZE(user_agent);
431     } else {
432         clientparms.transportparmsP = NULL;
433         clientparms.transportparm_size = 0;
434     }
435     xmlrpc_client_init2(envP, XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION, 
436                         &clientparms, XMLRPC_CPSIZE(transportparm_size));
437     if (!envP->fault_occurred) {
438         *resultPP = xmlrpc_client_call_server_params(
439             envP, serverInfoP, methodName, paramArrayP);
440     
441         xmlrpc_client_cleanup();
442     }
443     if (clientparms.transportparmsP)
444         free(clientparms.transportparmsP);
445 }
446
447
448
449 static void
450 createServerInfo(xmlrpc_env *          const envP,
451                  const char *          const serverUrl,
452                  const char *          const userName,
453                  const char *          const password,
454                  xmlrpc_server_info ** const serverInfoPP) {
455
456     xmlrpc_server_info * serverInfoP;
457
458     serverInfoP = xmlrpc_server_info_new(envP, serverUrl);
459     if (!envP->fault_occurred) {
460         if (userName) {
461             xmlrpc_server_info_set_basic_auth(
462                 envP, serverInfoP, userName, password);
463         }
464     }
465     *serverInfoPP = serverInfoP;
466 }
467
468
469
470 int 
471 main(int           const argc, 
472      const char ** const argv) {
473
474     struct cmdlineInfo cmdline;
475     xmlrpc_env env;
476     xmlrpc_value * paramArrayP;
477     xmlrpc_value * resultP;
478     const char * url;
479     xmlrpc_server_info * serverInfoP;
480
481     xmlrpc_env_init(&env);
482
483     parseCommandLine(&env, argc, argv, &cmdline);
484     die_if_fault_occurred(&env);
485
486     computeUrl(cmdline.url, &url);
487
488     computeParamArray(&env, cmdline.paramCount, cmdline.params, &paramArrayP);
489     die_if_fault_occurred(&env);
490
491     createServerInfo(&env, url, cmdline.username, cmdline.password,
492                      &serverInfoP);
493     die_if_fault_occurred(&env);
494
495     doCall(&env, cmdline.transport, cmdline.curlinterface,
496            cmdline.curlnoverifypeer, cmdline.curlnoverifyhost,
497            cmdline.curluseragent,
498            serverInfoP,
499            cmdline.methodName, paramArrayP, 
500            &resultP);
501     die_if_fault_occurred(&env);
502
503     dumpResult(resultP);
504     
505     strfree(url);
506
507     xmlrpc_DECREF(resultP);
508
509     freeCmdline(cmdline);
510
511     xmlrpc_env_clean(&env);
512     
513     return 0;
514 }