xmlrpc.c 17 KB

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