|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define _XOPEN_SOURCE 600 |
|
|
|
|
|
#include <stdlib.h> |
|
|
#include <stdio.h> |
|
|
#include <string.h> |
|
|
#include <errno.h> |
|
|
#include <assert.h> |
|
|
|
|
|
#include "xmlrpc_config.h" |
|
|
#include "bool.h" |
|
|
#include "int.h" |
|
|
#include "mallocvar.h" |
|
|
#include "girstring.h" |
|
|
#include "casprintf.h" |
|
|
#include "string_parser.h" |
|
|
#include "cmdline_parser.h" |
|
|
#include "dumpvalue.h" |
|
|
|
|
|
#include "xmlrpc-c/base.h" |
|
|
#include "xmlrpc-c/client.h" |
|
|
#include "xmlrpc-c/string_int.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; |
|
|
|
|
|
const char * transport; |
|
|
|
|
|
|
|
|
|
|
|
const char * curlinterface; |
|
|
|
|
|
|
|
|
|
|
|
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, "Failed. %s\n", envP->fault_string); |
|
|
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_NOFAIL(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 || |
|
|
!streq(cmdlineP->transport, "curl")) |
|
|
&& |
|
|
(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); |
|
|
} |
|
|
|
|
|
|
|
|
enum TokenType {COMMA, COLON, LEFTPAREN, RIGHTPAREN, |
|
|
LEFTBRACE, RIGHTBRACE, END}; |
|
|
|
|
|
static const char * |
|
|
tokenTypeName(enum TokenType const type) { |
|
|
|
|
|
switch (type) { |
|
|
case COMMA: return "comma"; |
|
|
case COLON: return "colon"; |
|
|
case LEFTPAREN: return "left parenthesis"; |
|
|
case RIGHTPAREN: return "right parenthesis"; |
|
|
case LEFTBRACE: return "left brace"; |
|
|
case RIGHTBRACE: return "right brace"; |
|
|
case END: return "end of string"; |
|
|
} |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
getDelimiter(xmlrpc_env * const envP, |
|
|
const char ** const cursorP, |
|
|
enum TokenType * const typeP) { |
|
|
|
|
|
const char * cursor; |
|
|
enum TokenType tokenType; |
|
|
|
|
|
cursor = *cursorP; |
|
|
|
|
|
switch (*cursor) { |
|
|
case ',': tokenType = COMMA; break; |
|
|
case ':': tokenType = COLON; break; |
|
|
case '(': tokenType = LEFTPAREN; break; |
|
|
case ')': tokenType = RIGHTPAREN; break; |
|
|
case '{': tokenType = LEFTBRACE; break; |
|
|
case '}': tokenType = RIGHTBRACE; break; |
|
|
case '\0': tokenType = END; break; |
|
|
default: |
|
|
setError(envP, "Unrecognized delimiter character '%c'", *cursor); |
|
|
} |
|
|
|
|
|
if (!envP->fault_occurred && tokenType != END) |
|
|
++cursor; |
|
|
|
|
|
*cursorP = cursor; |
|
|
*typeP = tokenType; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
getCdata(xmlrpc_env * const envP, |
|
|
const char ** const cursorP, |
|
|
const char ** const cdataP) { |
|
|
|
|
|
size_t const cdataSizeBound = strlen(*cursorP) + 1; |
|
|
|
|
|
char * text; |
|
|
|
|
|
MALLOCARRAY(text, cdataSizeBound); |
|
|
|
|
|
if (text == NULL) |
|
|
setError(envP, "Failed to allocate a buffer of size %u " |
|
|
"to compute cdata", (unsigned)cdataSizeBound); |
|
|
else { |
|
|
unsigned int textCursor; |
|
|
bool end; |
|
|
const char * cursor; |
|
|
|
|
|
cursor = *cursorP; |
|
|
|
|
|
for (textCursor = 0, end = false; !end; ) { |
|
|
switch (*cursor) { |
|
|
case ',': |
|
|
case ':': |
|
|
case '(': |
|
|
case ')': |
|
|
case '{': |
|
|
case '}': |
|
|
case '\0': |
|
|
end = true; |
|
|
break; |
|
|
case '\\': { |
|
|
++cursor; |
|
|
if (!*cursor) |
|
|
setError(envP, "Nothing after escape character ('\\')"); |
|
|
else |
|
|
text[textCursor++] = *cursor++; |
|
|
}; break; |
|
|
default: |
|
|
text[textCursor++]= *cursor++; |
|
|
} |
|
|
assert(textCursor <= cdataSizeBound); |
|
|
} |
|
|
text[textCursor++] = '\0'; |
|
|
|
|
|
assert(textCursor <= cdataSizeBound); |
|
|
|
|
|
*cdataP = text; |
|
|
*cursorP = cursor; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
buildValue(xmlrpc_env * const envP, |
|
|
const char ** const cursorP, |
|
|
xmlrpc_value ** const valuePP); |
|
|
|
|
|
|
|
|
static void |
|
|
buildString(xmlrpc_env * const envP, |
|
|
const char * const valueString, |
|
|
xmlrpc_value ** const paramPP) { |
|
|
|
|
|
*paramPP = xmlrpc_string_new(envP, valueString); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
interpretHex(xmlrpc_env * const envP, |
|
|
const char * const valueString, |
|
|
size_t const valueStringSize, |
|
|
unsigned char * const byteString) { |
|
|
|
|
|
size_t bsCursor; |
|
|
size_t strCursor; |
|
|
|
|
|
for (strCursor = 0, bsCursor = 0; |
|
|
strCursor < valueStringSize && !envP->fault_occurred; |
|
|
) { |
|
|
int rc; |
|
|
|
|
|
rc = sscanf(&valueString[strCursor], "%2hhx", |
|
|
&byteString[bsCursor++]); |
|
|
|
|
|
if (rc != 1) |
|
|
xmlrpc_faultf(envP, "Invalid hex data '%s'", |
|
|
&valueString[strCursor]); |
|
|
else |
|
|
strCursor += 2; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
buildBytestring(xmlrpc_env * const envP, |
|
|
const char * const valueString, |
|
|
xmlrpc_value ** const paramPP) { |
|
|
|
|
|
size_t const valueStringSize = strlen(valueString); |
|
|
|
|
|
if (valueStringSize / 2 * 2 != valueStringSize) |
|
|
xmlrpc_faultf(envP, "Hexadecimal text is not an even " |
|
|
"number of characters (it is %u characters)", |
|
|
(unsigned)strlen(valueString)); |
|
|
else { |
|
|
size_t const byteStringSize = strlen(valueString)/2; |
|
|
|
|
|
unsigned char * byteString; |
|
|
|
|
|
MALLOCARRAY(byteString, byteStringSize); |
|
|
|
|
|
if (byteString == NULL) |
|
|
xmlrpc_faultf(envP, "Failed to allocate %u-byte buffer", |
|
|
(unsigned)byteStringSize); |
|
|
else { |
|
|
interpretHex(envP, valueString, valueStringSize, byteString); |
|
|
|
|
|
if (!envP->fault_occurred) |
|
|
*paramPP = xmlrpc_base64_new(envP, byteStringSize, byteString); |
|
|
|
|
|
free(byteString); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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 { |
|
|
int value; |
|
|
const char * error; |
|
|
|
|
|
interpretInt(valueString, &value, &error); |
|
|
|
|
|
if (error) { |
|
|
setError(envP, "'%s' is not a valid 32-bit integer. %s", |
|
|
valueString, error); |
|
|
strfree(error); |
|
|
} else |
|
|
*paramPP = xmlrpc_int_new(envP, value); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
buildBool(xmlrpc_env * const envP, |
|
|
const char * const valueString, |
|
|
xmlrpc_value ** const paramPP) { |
|
|
|
|
|
if (streq(valueString, "t") || streq(valueString, "true")) |
|
|
*paramPP = xmlrpc_bool_new(envP, true); |
|
|
else if (streq(valueString, "f") || streq(valueString, "false")) |
|
|
*paramPP = xmlrpc_bool_new(envP, false); |
|
|
else |
|
|
setError(envP, "Boolean argument has unrecognized value '%s'. " |
|
|
"recognized values are 't', 'f', 'true', and 'false'.", |
|
|
valueString); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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_double_new(envP, value); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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_nil_new(envP); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
buildI8(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 { |
|
|
int64_t value; |
|
|
const char * error; |
|
|
|
|
|
interpretLl(valueString, &value, &error); |
|
|
|
|
|
if (error) { |
|
|
setError(envP, "'%s' is not a valid 64-bit integer. %s", |
|
|
valueString, error); |
|
|
strfree(error); |
|
|
} else |
|
|
*paramPP = xmlrpc_i8_new(envP, value); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
addArrayItem(xmlrpc_env * const envP, |
|
|
const char ** const cursorP, |
|
|
xmlrpc_value * const arrayP, |
|
|
bool * const endP) { |
|
|
|
|
|
xmlrpc_value * itemP; |
|
|
|
|
|
buildValue(envP, cursorP, &itemP); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
xmlrpc_array_append_item(envP, arrayP, itemP); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
enum TokenType delim; |
|
|
|
|
|
getDelimiter(envP, cursorP, &delim); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
switch (delim) { |
|
|
case COMMA: break; |
|
|
case RIGHTPAREN: *endP = true; break; |
|
|
default: |
|
|
setError(envP, "Array specifier has garbage where " |
|
|
"there should be a comma " |
|
|
"(element separator) " |
|
|
"or close parenthesis " |
|
|
"(marking the end of the element list)"); |
|
|
} |
|
|
} |
|
|
} |
|
|
xmlrpc_DECREF(itemP); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
buildArray(xmlrpc_env * const envP, |
|
|
const char ** const cursorP, |
|
|
xmlrpc_value ** const valuePP) { |
|
|
|
|
|
enum TokenType tokenType; |
|
|
|
|
|
getDelimiter(envP, cursorP, &tokenType); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
if (tokenType != LEFTPAREN) |
|
|
setError(envP, "Array specifier value starts with %s instead of " |
|
|
"left parenthesis", tokenTypeName(tokenType)); |
|
|
else { |
|
|
xmlrpc_value * const arrayP = xmlrpc_array_new(envP); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
bool end; |
|
|
for (end = false; !end && !envP->fault_occurred; ) |
|
|
addArrayItem(envP, cursorP, arrayP, &end); |
|
|
|
|
|
if (envP->fault_occurred) |
|
|
xmlrpc_DECREF(arrayP); |
|
|
else |
|
|
*valuePP = arrayP; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
addStructMember(xmlrpc_env * const envP, |
|
|
const char ** const cursorP, |
|
|
xmlrpc_value * const structP, |
|
|
bool * const endP) { |
|
|
|
|
|
const char * key; |
|
|
|
|
|
getCdata(envP, cursorP, &key); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
enum TokenType delim; |
|
|
|
|
|
getDelimiter(envP, cursorP, &delim); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
if (delim != COLON) |
|
|
setError(envP, "Something other than a colon follows the " |
|
|
"key value '%s' in a structure member.", key); |
|
|
else { |
|
|
xmlrpc_value * valueP; |
|
|
|
|
|
buildValue(envP, cursorP, &valueP); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
xmlrpc_struct_set_value(envP, structP, key, valueP); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
enum TokenType delim; |
|
|
|
|
|
getDelimiter(envP, cursorP, &delim); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
switch (delim) { |
|
|
case COMMA: break; |
|
|
case RIGHTBRACE: *endP = true; break; |
|
|
default: |
|
|
setError(envP, "Struct specifier " |
|
|
"has garbage where " |
|
|
"there should be a comma " |
|
|
"(member separator) " |
|
|
"or close brace " |
|
|
"(marking the end of the " |
|
|
"member list)"); |
|
|
} |
|
|
} |
|
|
} |
|
|
xmlrpc_DECREF(valueP); |
|
|
} |
|
|
} |
|
|
} |
|
|
strfree(key); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
buildStruct(xmlrpc_env * const envP, |
|
|
const char ** const cursorP, |
|
|
xmlrpc_value ** const valuePP) { |
|
|
|
|
|
enum TokenType tokenType; |
|
|
|
|
|
getDelimiter(envP, cursorP, &tokenType); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
if (tokenType != LEFTBRACE) |
|
|
setError(envP, "Struct specifier value starts with %s instead of " |
|
|
"left brace", tokenTypeName(tokenType)); |
|
|
else { |
|
|
xmlrpc_value * const structP = xmlrpc_struct_new(envP); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
bool end; |
|
|
for (end = false; !end && !envP->fault_occurred; ) |
|
|
addStructMember(envP, cursorP, structP, &end); |
|
|
|
|
|
if (envP->fault_occurred) |
|
|
xmlrpc_DECREF(structP); |
|
|
else |
|
|
*valuePP = structP; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
buildValue(xmlrpc_env * const envP, |
|
|
const char ** const cursorP, |
|
|
xmlrpc_value ** const valuePP) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char * cdata; |
|
|
|
|
|
getCdata(envP, cursorP, &cdata); |
|
|
|
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
if (strlen(cdata) == 0) |
|
|
setError(envP, "Expected value type specifier such as 'i/' or " |
|
|
"'array/' but found '%s'", *cursorP); |
|
|
|
|
|
if (xmlrpc_strneq(cdata, "s/", 2)) |
|
|
buildString(envP, &cdata[2], valuePP); |
|
|
else if (xmlrpc_strneq(cdata, "h/", 2)) |
|
|
buildBytestring(envP, &cdata[2], valuePP); |
|
|
else if (xmlrpc_strneq(cdata, "i/", 2)) |
|
|
buildInt(envP, &cdata[2], valuePP); |
|
|
else if (xmlrpc_strneq(cdata, "I/", 2)) |
|
|
buildI8(envP, &cdata[2], valuePP); |
|
|
else if (xmlrpc_strneq(cdata, "d/", 2)) |
|
|
buildDouble(envP, &cdata[2], valuePP); |
|
|
else if (xmlrpc_strneq(cdata, "b/", 2)) |
|
|
buildBool(envP, &cdata[2], valuePP); |
|
|
else if (xmlrpc_strneq(cdata, "n/", 2)) |
|
|
buildNil(envP, &cdata[2], valuePP); |
|
|
else if (xmlrpc_strneq(cdata, "array/", 6)) { |
|
|
if (cdata[6] != '\0') |
|
|
setError(envP, "Junk after 'array/' instead of " |
|
|
"left parenthesis: '%s'", &cdata[6]); |
|
|
else |
|
|
buildArray(envP, cursorP, valuePP); |
|
|
} else if (xmlrpc_strneq(cdata, "struct/", 7)) { |
|
|
if (cdata[7] != '\0') |
|
|
setError(envP, "Junk after 'struct/' instead of " |
|
|
"left brace: '%s'", &cdata[7]); |
|
|
else |
|
|
buildStruct(envP, cursorP, valuePP); |
|
|
} else { |
|
|
|
|
|
|
|
|
|
|
|
buildString(envP, cdata, valuePP); |
|
|
} |
|
|
strfree(cdata); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
computeParam(xmlrpc_env * const envP, |
|
|
const char * const paramArg, |
|
|
xmlrpc_value ** const paramPP) { |
|
|
|
|
|
const char * cursor; |
|
|
|
|
|
cursor = ¶mArg[0]; |
|
|
|
|
|
buildValue(envP, &cursor, paramPP); |
|
|
|
|
|
if (!envP->fault_occurred) { |
|
|
if (*cursor != '\0') |
|
|
setError(envP, "Junk after parameter specification: '%s'", cursor); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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_array_new(envP); |
|
|
|
|
|
for (i = 0; i < paramCount && !envP->fault_occurred; ++i) { |
|
|
xmlrpc_value * paramP; |
|
|
xmlrpc_env paramEnv; |
|
|
|
|
|
xmlrpc_env_init(¶mEnv); |
|
|
|
|
|
computeParam(¶mEnv, params[i], ¶mP); |
|
|
|
|
|
if (!paramEnv.fault_occurred) { |
|
|
xmlrpc_array_append_item(¶mEnv, paramArrayP, paramP); |
|
|
|
|
|
xmlrpc_DECREF(paramP); |
|
|
} |
|
|
if (paramEnv.fault_occurred) |
|
|
setError(envP, "Invalid specification of parameter %u " |
|
|
"(starting at zero). %s", i, paramEnv.fault_string); |
|
|
|
|
|
xmlrpc_env_clean(¶mEnv); |
|
|
} |
|
|
*paramArrayPP = paramArrayP; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
dumpResult(xmlrpc_value * const resultP) { |
|
|
|
|
|
printf("Result:\n\n"); |
|
|
|
|
|
dumpValue("", resultP); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
callWithClient(xmlrpc_env * const envP, |
|
|
const xmlrpc_server_info * const serverInfoP, |
|
|
const char * const methodName, |
|
|
xmlrpc_value * const paramArrayP, |
|
|
xmlrpc_value ** const resultPP) { |
|
|
|
|
|
xmlrpc_env env; |
|
|
xmlrpc_env_init(&env); |
|
|
*resultPP = xmlrpc_client_call_server_params( |
|
|
&env, serverInfoP, methodName, paramArrayP); |
|
|
|
|
|
if (env.fault_occurred) |
|
|
xmlrpc_faultf(envP, "Call failed. %s. (XML-RPC fault code %d)", |
|
|
env.fault_string, env.fault_code); |
|
|
xmlrpc_env_clean(&env); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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 && streq(transport, "curl")) { |
|
|
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 = 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) { |
|
|
callWithClient(envP, serverInfoP, methodName, paramArrayP, resultPP); |
|
|
|
|
|
xmlrpc_client_cleanup(); |
|
|
} |
|
|
if (clientparms.transportparmsP) |
|
|
free((void*)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; |
|
|
} |
|
|
|