--- /dev/null
+/* Make an XML-RPC call.
+
+ User specifies details of the call on the command line.
+
+ We print the result on Standard Output.
+
+ Example:
+
+ $ xmlrpc http://localhost:8080/RPC2 sample.add i/3 i/5
+ Result:
+ Integer: 8
+
+ $ xmlrpc localhost:8080 sample.add i/3 i/5
+ Result:
+ Integer: 8
+
+ This is just the beginnings of this program. It should be extended
+ to deal with all types of parameters and results.
+
+ An example of a good syntax for parameters would be:
+
+ $ xmlrpc http://www.oreillynet.com/meerkat/xml-rpc/server.php \
+ meerkat.getItems \
+ struct/{search:linux,descriptions:i/76,time_period:12hour}
+ Result:
+ Array:
+ Struct:
+ title: String: DatabaseJournal: OpenEdge-Based Finance ...
+ link: String: http://linuxtoday.com/news_story.php3?ltsn=...
+ description: String: "Finance application with embedded ...
+ Struct:
+ title: ...
+ link: ...
+ description: ...
+
+*/
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h" /* information about this build environment */
+#include "casprintf.h"
+#include "mallocvar.h"
+#include "cmdline_parser.h"
+#include "dumpvalue.h"
+
+#include "xmlrpc-c/base.h"
+#include "xmlrpc-c/client.h"
+
+#define NAME "xmlrpc command line program"
+#define VERSION "1.0"
+
+struct cmdlineInfo {
+ const char * url;
+ const char * username;
+ const char * password;
+ const char * methodName;
+ unsigned int paramCount;
+ const char ** params;
+ /* Array of parameters, in order. Has 'paramCount' entries. */
+ const char * transport;
+ /* Name of XML transport he wants to use. NULL if he has no
+ preference.
+ */
+ const char * curlinterface;
+ /* "network interface" parameter for the Curl transport. (Not
+ valid if 'transport' names a non-Curl transport).
+ */
+ xmlrpc_bool curlnoverifypeer;
+ xmlrpc_bool curlnoverifyhost;
+ const char * curluseragent;
+};
+
+
+
+static void
+die_if_fault_occurred (xmlrpc_env * const envP) {
+ if (envP->fault_occurred) {
+ fprintf(stderr, "Error: %s (%d)\n",
+ envP->fault_string, envP->fault_code);
+ exit(1);
+ }
+}
+
+
+
+static void GNU_PRINTF_ATTR(2,3)
+setError(xmlrpc_env * const envP, const char format[], ...) {
+ va_list args;
+ const char * faultString;
+
+ va_start(args, format);
+
+ cvasprintf(&faultString, format, args);
+ va_end(args);
+
+ xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR, faultString);
+
+ strfree(faultString);
+}
+
+
+
+static void
+processArguments(xmlrpc_env * const envP,
+ cmdlineParser const cp,
+ struct cmdlineInfo * const cmdlineP) {
+
+ if (cmd_argumentCount(cp) < 2)
+ setError(envP, "Not enough arguments. Need at least a URL and "
+ "method name.");
+ else {
+ unsigned int i;
+
+ cmdlineP->url = cmd_getArgument(cp, 0);
+ cmdlineP->methodName = cmd_getArgument(cp, 1);
+ cmdlineP->paramCount = cmd_argumentCount(cp) - 2;
+ MALLOCARRAY(cmdlineP->params, cmdlineP->paramCount);
+ for (i = 0; i < cmdlineP->paramCount; ++i)
+ cmdlineP->params[i] = cmd_getArgument(cp, i+2);
+ }
+}
+
+
+
+static void
+chooseTransport(xmlrpc_env * const envP ATTR_UNUSED,
+ cmdlineParser const cp,
+ const char ** const transportPP) {
+
+ const char * transportOpt = cmd_getOptionValueString(cp, "transport");
+
+ if (transportOpt) {
+ *transportPP = transportOpt;
+ } else {
+ if (cmd_optionIsPresent(cp, "curlinterface") ||
+ cmd_optionIsPresent(cp, "curlnoverifypeer") ||
+ cmd_optionIsPresent(cp, "curlnoverifyhost") ||
+ cmd_optionIsPresent(cp, "curluseragent"))
+
+ *transportPP = strdup("curl");
+ else
+ *transportPP = NULL;
+ }
+}
+
+
+
+static void
+parseCommandLine(xmlrpc_env * const envP,
+ int const argc,
+ const char ** const argv,
+ struct cmdlineInfo * const cmdlineP) {
+
+ cmdlineParser const cp = cmd_createOptionParser();
+
+ const char * error;
+
+ cmd_defineOption(cp, "transport", OPTTYPE_STRING);
+ cmd_defineOption(cp, "username", OPTTYPE_STRING);
+ cmd_defineOption(cp, "password", OPTTYPE_STRING);
+ cmd_defineOption(cp, "curlinterface", OPTTYPE_STRING);
+ cmd_defineOption(cp, "curlnoverifypeer", OPTTYPE_STRING);
+ cmd_defineOption(cp, "curlnoverifyhost", OPTTYPE_STRING);
+ cmd_defineOption(cp, "curluseragent", OPTTYPE_STRING);
+
+ cmd_processOptions(cp, argc, argv, &error);
+
+ if (error) {
+ setError(envP, "Command syntax error. %s", error);
+ strfree(error);
+ } else {
+ cmdlineP->username = cmd_getOptionValueString(cp, "username");
+ cmdlineP->password = cmd_getOptionValueString(cp, "password");
+
+ if (cmdlineP->username && !cmdlineP->password)
+ setError(envP, "When you specify -username, you must also "
+ "specify -password.");
+ else {
+ chooseTransport(envP, cp, &cmdlineP->transport);
+
+ cmdlineP->curlinterface =
+ cmd_getOptionValueString(cp, "curlinterface");
+ cmdlineP->curlnoverifypeer =
+ cmd_optionIsPresent(cp, "curlnoverifypeer");
+ cmdlineP->curlnoverifyhost =
+ cmd_optionIsPresent(cp, "curlnoverifyhost");
+ cmdlineP->curluseragent =
+ cmd_getOptionValueString(cp, "curluseragent");
+
+ if ((!cmdlineP->transport ||
+ strcmp(cmdlineP->transport, "curl") != 0)
+ &&
+ (cmdlineP->curlinterface ||
+ cmdlineP->curlnoverifypeer ||
+ cmdlineP->curlnoverifyhost ||
+ cmdlineP->curluseragent))
+ setError(envP, "You may not specify a Curl transport "
+ "option unless you also specify -transport=curl");
+
+ processArguments(envP, cp, cmdlineP);
+ }
+ }
+ cmd_destroyOptionParser(cp);
+}
+
+
+
+static void
+freeCmdline(struct cmdlineInfo const cmdline) {
+
+ unsigned int i;
+
+ strfree(cmdline.url);
+ strfree(cmdline.methodName);
+ if (cmdline.transport)
+ strfree(cmdline.transport);
+ if (cmdline.curlinterface)
+ strfree(cmdline.curlinterface);
+ if (cmdline.curluseragent)
+ strfree(cmdline.curluseragent);
+ if (cmdline.username)
+ strfree(cmdline.username);
+ if (cmdline.password)
+ strfree(cmdline.password);
+ for (i = 0; i < cmdline.paramCount; ++i)
+ strfree(cmdline.params[i]);
+}
+
+
+
+static void
+computeUrl(const char * const urlArg,
+ const char ** const urlP) {
+
+ if (strstr(urlArg, "://") != 0)
+ casprintf(urlP, "%s", urlArg);
+ else
+ casprintf(urlP, "http://%s/RPC2", urlArg);
+}
+
+
+
+static void
+buildString(xmlrpc_env * const envP,
+ const char * const valueString,
+ xmlrpc_value ** const paramPP) {
+
+ *paramPP = xmlrpc_build_value(envP, "s", valueString);
+}
+
+
+
+static void
+buildInt(xmlrpc_env * const envP,
+ const char * const valueString,
+ xmlrpc_value ** const paramPP) {
+
+ if (strlen(valueString) < 1)
+ setError(envP, "Integer argument has nothing after the 'i/'");
+ else {
+ long value;
+ char * tailptr;
+
+ value = strtol(valueString, &tailptr, 10);
+
+ if (*tailptr != '\0')
+ setError(envP,
+ "Integer argument has non-digit crap in it: '%s'",
+ tailptr);
+ else
+ *paramPP = xmlrpc_build_value(envP, "i", value);
+ }
+}
+
+
+
+static void
+buildDouble(xmlrpc_env * const envP,
+ const char * const valueString,
+ xmlrpc_value ** const paramPP) {
+
+ if (strlen(valueString) < 1)
+ setError(envP, "\"Double\" argument has nothing after the 'd/'");
+ else {
+ double value;
+ char * tailptr;
+
+ value = strtod(valueString, &tailptr);
+
+ if (*tailptr != '\0')
+ setError(envP,
+ "\"Double\" argument has non-decimal crap in it: '%s'",
+ tailptr);
+ else
+ *paramPP = xmlrpc_build_value(envP, "d", value);
+ }
+}
+
+
+
+static void
+buildBool(xmlrpc_env * const envP,
+ const char * const valueString,
+ xmlrpc_value ** const paramPP) {
+
+ if (strcmp(valueString, "t") == 0 ||
+ strcmp(valueString, "true") == 0)
+ *paramPP = xmlrpc_build_value(envP, "b", 1);
+ else if (strcmp(valueString, "f") == 0 ||
+ strcmp(valueString, "false") == 0)
+ *paramPP = xmlrpc_build_value(envP, "b", 0);
+ else
+ setError(envP, "Boolean argument has unrecognized value '%s'. "
+ "recognized values are 't', 'f', 'true', and 'false'.",
+ valueString);
+}
+
+
+
+static void
+buildNil(xmlrpc_env * const envP,
+ const char * const valueString,
+ xmlrpc_value ** const paramPP) {
+
+ if (strlen(valueString) > 0)
+ setError(envP, "Nil argument has something after the 'n/'");
+ else {
+ *paramPP = xmlrpc_build_value(envP, "n");
+ }
+}
+
+
+
+static void
+computeParameter(xmlrpc_env * const envP,
+ const char * const paramArg,
+ xmlrpc_value ** const paramPP) {
+
+ if (strncmp(paramArg, "s/", 2) == 0)
+ buildString(envP, ¶mArg[2], paramPP);
+ else if (strncmp(paramArg, "i/", 2) == 0)
+ buildInt(envP, ¶mArg[2], paramPP);
+ else if (strncmp(paramArg, "d/", 2) == 0)
+ buildDouble(envP, ¶mArg[2], paramPP);
+ else if (strncmp(paramArg, "b/", 2) == 0)
+ buildBool(envP, ¶mArg[2], paramPP);
+ else if (strncmp(paramArg, "n/", 2) == 0)
+ buildNil(envP, ¶mArg[2], paramPP);
+ else {
+ /* It's not in normal type/value format, so we take it to be
+ the shortcut string notation
+ */
+ buildString(envP, paramArg, paramPP);
+ }
+}
+
+
+
+static void
+computeParamArray(xmlrpc_env * const envP,
+ unsigned int const paramCount,
+ const char ** const params,
+ xmlrpc_value ** const paramArrayPP) {
+
+ unsigned int i;
+
+ xmlrpc_value * paramArrayP;
+
+ paramArrayP = xmlrpc_build_value(envP, "()");
+
+ for (i = 0; i < paramCount && !envP->fault_occurred; ++i) {
+ xmlrpc_value * paramP;
+
+ computeParameter(envP, params[i], ¶mP);
+
+ if (!envP->fault_occurred) {
+ xmlrpc_array_append_item(envP, paramArrayP, paramP);
+
+ xmlrpc_DECREF(paramP);
+ }
+ }
+ *paramArrayPP = paramArrayP;
+}
+
+
+
+static void
+dumpResult(xmlrpc_value * const resultP) {
+
+ printf("Result:\n\n");
+
+ dumpValue("", resultP);
+}
+
+
+
+static void
+doCall(xmlrpc_env * const envP,
+ const char * const transport,
+ const char * const curlinterface,
+ xmlrpc_bool const curlnoverifypeer,
+ xmlrpc_bool const curlnoverifyhost,
+ const char * const curluseragent,
+ const xmlrpc_server_info * const serverInfoP,
+ const char * const methodName,
+ xmlrpc_value * const paramArrayP,
+ xmlrpc_value ** const resultPP) {
+
+ struct xmlrpc_clientparms clientparms;
+
+ XMLRPC_ASSERT(xmlrpc_value_type(paramArrayP) == XMLRPC_TYPE_ARRAY);
+
+ clientparms.transport = transport;
+
+ if (transport && strcmp(transport, "curl") == 0) {
+ struct xmlrpc_curl_xportparms * curlXportParmsP;
+ MALLOCVAR(curlXportParmsP);
+
+ curlXportParmsP->network_interface = curlinterface;
+ curlXportParmsP->no_ssl_verifypeer = curlnoverifypeer;
+ curlXportParmsP->no_ssl_verifyhost = curlnoverifyhost;
+ curlXportParmsP->user_agent = curluseragent;
+
+ clientparms.transportparmsP = (struct xmlrpc_xportparms *)
+ curlXportParmsP;
+ clientparms.transportparm_size = XMLRPC_CXPSIZE(user_agent);
+ } else {
+ clientparms.transportparmsP = NULL;
+ clientparms.transportparm_size = 0;
+ }
+ xmlrpc_client_init2(envP, XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION,
+ &clientparms, XMLRPC_CPSIZE(transportparm_size));
+ if (!envP->fault_occurred) {
+ *resultPP = xmlrpc_client_call_server_params(
+ envP, serverInfoP, methodName, paramArrayP);
+
+ xmlrpc_client_cleanup();
+ }
+ if (clientparms.transportparmsP)
+ free(clientparms.transportparmsP);
+}
+
+
+
+static void
+createServerInfo(xmlrpc_env * const envP,
+ const char * const serverUrl,
+ const char * const userName,
+ const char * const password,
+ xmlrpc_server_info ** const serverInfoPP) {
+
+ xmlrpc_server_info * serverInfoP;
+
+ serverInfoP = xmlrpc_server_info_new(envP, serverUrl);
+ if (!envP->fault_occurred) {
+ if (userName) {
+ xmlrpc_server_info_set_basic_auth(
+ envP, serverInfoP, userName, password);
+ }
+ }
+ *serverInfoPP = serverInfoP;
+}
+
+
+
+int
+main(int const argc,
+ const char ** const argv) {
+
+ struct cmdlineInfo cmdline;
+ xmlrpc_env env;
+ xmlrpc_value * paramArrayP;
+ xmlrpc_value * resultP;
+ const char * url;
+ xmlrpc_server_info * serverInfoP;
+
+ xmlrpc_env_init(&env);
+
+ parseCommandLine(&env, argc, argv, &cmdline);
+ die_if_fault_occurred(&env);
+
+ computeUrl(cmdline.url, &url);
+
+ computeParamArray(&env, cmdline.paramCount, cmdline.params, ¶mArrayP);
+ die_if_fault_occurred(&env);
+
+ createServerInfo(&env, url, cmdline.username, cmdline.password,
+ &serverInfoP);
+ die_if_fault_occurred(&env);
+
+ doCall(&env, cmdline.transport, cmdline.curlinterface,
+ cmdline.curlnoverifypeer, cmdline.curlnoverifyhost,
+ cmdline.curluseragent,
+ serverInfoP,
+ cmdline.methodName, paramArrayP,
+ &resultP);
+ die_if_fault_occurred(&env);
+
+ dumpResult(resultP);
+
+ strfree(url);
+
+ xmlrpc_DECREF(resultP);
+
+ freeCmdline(cmdline);
+
+ xmlrpc_env_clean(&env);
+
+ return 0;
+}