/* 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 #include #include #include "config.h" /* information about this build environment */ #include "casprintf.h" #include "mallocvar.h" #include "cmdline_parser.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) { *urlP = strdup(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 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, "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; } /* Forward declaration for recursion */ static void dumpValue(const char * const prefix, xmlrpc_value * const valueP); static void dumpInt(const char * const prefix, xmlrpc_value * const valueP) { xmlrpc_env env; xmlrpc_int value; xmlrpc_env_init(&env); xmlrpc_parse_value(&env, valueP, "i", &value); if (env.fault_occurred) printf("Unable to parse integer in result. %s\n", env.fault_string); else printf("%sInteger: %d\n", prefix, value); xmlrpc_env_clean(&env); } static void dumpBool(const char * const prefix, xmlrpc_value * const valueP) { xmlrpc_env env; xmlrpc_bool value; xmlrpc_env_init(&env); xmlrpc_parse_value(&env, valueP, "b", &value); if (env.fault_occurred) printf("Unable to parse boolean in result. %s\n", env.fault_string); else printf("%sBoolean: %s\n", prefix, value ? "TRUE" : "FALSE"); xmlrpc_env_clean(&env); } static void dumpDouble(const char * const prefix, xmlrpc_value * const valueP) { xmlrpc_env env; xmlrpc_double value; xmlrpc_env_init(&env); xmlrpc_parse_value(&env, valueP, "d", &value); if (env.fault_occurred) printf("Unable to parse floating point number in result. %s\n", env.fault_string); else printf("%sFloating Point: %f\n", prefix, value); xmlrpc_env_clean(&env); } static void dumpDatetime(const char * const prefix, xmlrpc_value * const valueP) { printf("%sDon't know how to print datetime value result %p.\n", prefix, valueP); } static void dumpString(const char * const prefix, xmlrpc_value * const valueP) { xmlrpc_env env; const char * value; xmlrpc_env_init(&env); xmlrpc_parse_value(&env, valueP, "s", &value); if (env.fault_occurred) printf("Unable to parse string in result. %s\n", env.fault_string); else printf("%sString: '%s'\n", prefix, value); xmlrpc_env_clean(&env); } static void dumpBase64(const char * const prefix, xmlrpc_value * const valueP) { xmlrpc_env env; const unsigned char * value; size_t length; xmlrpc_env_init(&env); xmlrpc_parse_value(&env, valueP, "6", &value, &length); if (env.fault_occurred) printf("Unable to parse base64 bit strnig in result. %s\n", env.fault_string); else { unsigned int i; printf("%sBit string: ", prefix); for (i = 0; i < length; ++i) printf("%02x", value[i]); } xmlrpc_env_clean(&env); } static void dumpArray(const char * const prefix, xmlrpc_value * const arrayP) { xmlrpc_env env; unsigned int arraySize; xmlrpc_env_init(&env); XMLRPC_ASSERT_ARRAY_OK(arrayP); arraySize = xmlrpc_array_size(&env, arrayP); if (env.fault_occurred) printf("Unable to get array size. %s\n", env.fault_string); else { int const spaceCount = strlen(prefix); unsigned int i; const char * blankPrefix; printf("%sArray of %u items:\n", prefix, arraySize); casprintf(&blankPrefix, "%*s", spaceCount, ""); for (i = 0; i < arraySize; ++i) { xmlrpc_value * valueP; xmlrpc_array_read_item(&env, arrayP, i, &valueP); if (env.fault_occurred) printf("Unable to get array item %u\n", i); else { const char * prefix2; casprintf(&prefix2, "%s Index %2u ", blankPrefix, i); dumpValue(prefix2, valueP); strfree(prefix2); xmlrpc_DECREF(valueP); } } strfree(blankPrefix); } xmlrpc_env_clean(&env); } static void dumpStructMember(const char * const prefix, xmlrpc_value * const structP, unsigned int const index) { xmlrpc_env env; xmlrpc_value * keyP; xmlrpc_value * valueP; xmlrpc_env_init(&env); xmlrpc_struct_read_member(&env, structP, index, &keyP, &valueP); if (env.fault_occurred) printf("Unable to get struct member %u\n", index); else { int const blankCount = strlen(prefix); const char * prefix2; const char * blankPrefix; casprintf(&prefix2, "%s Key: ", prefix); dumpValue(prefix2, keyP); strfree(prefix2); casprintf(&blankPrefix, "%*s", blankCount, ""); casprintf(&prefix2, "%s Value: ", blankPrefix); dumpValue(prefix2, valueP); strfree(prefix2); strfree(blankPrefix); xmlrpc_DECREF(keyP); xmlrpc_DECREF(valueP); } xmlrpc_env_clean(&env); } static void dumpStruct(const char * const prefix, xmlrpc_value * const structP) { xmlrpc_env env; unsigned int structSize; xmlrpc_env_init(&env); structSize = xmlrpc_struct_size(&env, structP); if (env.fault_occurred) printf("Unable to get struct size. %s\n", env.fault_string); else { unsigned int i; printf("%sStruct of %u members:\n", prefix, structSize); for (i = 0; i < structSize; ++i) { const char * prefix1; if (i == 0) prefix1 = strdup(prefix); else { int const blankCount = strlen(prefix); casprintf(&prefix1, "%*s", blankCount, ""); } dumpStructMember(prefix1, structP, i); strfree(prefix1); } } xmlrpc_env_clean(&env); } static void dumpCPtr(const char * const prefix, xmlrpc_value * const valueP) { xmlrpc_env env; const char * value; xmlrpc_env_init(&env); xmlrpc_parse_value(&env, valueP, "p", &value); if (env.fault_occurred) printf("Unable to parse C pointer in result. %s\n", env.fault_string); else printf("%sC pointer: '%p'\n", prefix, value); xmlrpc_env_clean(&env); } static void dumpNil(const char * const prefix, xmlrpc_value * const valueP) { xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_parse_value(&env, valueP, "n"); if (env.fault_occurred) printf("Unable to parse nil value in result. %s\n", env.fault_string); else printf("%sNil\n", prefix); xmlrpc_env_clean(&env); } static void dumpUnknown(const char * const prefix, xmlrpc_value * const valueP) { printf("%sDon't recognize the type of the result: %u.\n", prefix, xmlrpc_value_type(valueP)); printf("%sCan't print it.\n", prefix); } static void dumpValue(const char * const prefix, xmlrpc_value * const valueP) { switch (xmlrpc_value_type(valueP)) { case XMLRPC_TYPE_INT: dumpInt(prefix, valueP); break; case XMLRPC_TYPE_BOOL: dumpBool(prefix, valueP); break; case XMLRPC_TYPE_DOUBLE: dumpDouble(prefix, valueP); break; case XMLRPC_TYPE_DATETIME: dumpDatetime(prefix, valueP); break; case XMLRPC_TYPE_STRING: dumpString(prefix, valueP); break; case XMLRPC_TYPE_BASE64: dumpBase64(prefix, valueP); break; case XMLRPC_TYPE_ARRAY: dumpArray(prefix, valueP); break; case XMLRPC_TYPE_STRUCT: dumpStruct(prefix, valueP); break; case XMLRPC_TYPE_C_PTR: dumpCPtr(prefix, valueP); break; case XMLRPC_TYPE_NIL: dumpNil(prefix, valueP); break; default: dumpUnknown(prefix, valueP); } } 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; }