summaryrefslogtreecommitdiffstats
path: root/Utilities/cmxmlrpc/xmlrpc_registry.c
diff options
context:
space:
mode:
authorAndy Cedilnik <andy.cedilnik@kitware.com>2005-02-22 18:08:27 (GMT)
committerAndy Cedilnik <andy.cedilnik@kitware.com>2005-02-22 18:08:27 (GMT)
commitb9b4ea0f7bbf0313d8aa5e354ee56f912969763c (patch)
treeba5e857cda91949be14f508e6df490e1e2176f40 /Utilities/cmxmlrpc/xmlrpc_registry.c
parentbfcb4b693763bb11f841094d2ca9852a64e5d33b (diff)
downloadCMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.zip
CMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.tar.gz
CMake-b9b4ea0f7bbf0313d8aa5e354ee56f912969763c.tar.bz2
ENH: Initial import
Diffstat (limited to 'Utilities/cmxmlrpc/xmlrpc_registry.c')
-rw-r--r--Utilities/cmxmlrpc/xmlrpc_registry.c829
1 files changed, 829 insertions, 0 deletions
diff --git a/Utilities/cmxmlrpc/xmlrpc_registry.c b/Utilities/cmxmlrpc/xmlrpc_registry.c
new file mode 100644
index 0000000..41e5ac1
--- /dev/null
+++ b/Utilities/cmxmlrpc/xmlrpc_registry.c
@@ -0,0 +1,829 @@
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+** Copyright (C) 2001 by Eric Kidd. All rights reserved.
+** Copyright (C) 2001 by Luke Howard. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE. */
+
+#include "xmlrpc_config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xmlrpc.h"
+#include "xmlrpc_server.h"
+#include "xmlrpc_int.h"
+
+/*=========================================================================
+** XML-RPC Server Method Registry
+**=========================================================================
+** A method registry maintains a list of functions, and handles
+** dispatching. To build an XML-RPC server, just add a communications
+** protocol. :-)
+*/
+
+static void
+install_system_methods (xmlrpc_env *env, xmlrpc_registry *registry);
+
+xmlrpc_registry *
+xmlrpc_registry_new(xmlrpc_env *env) {
+
+ xmlrpc_value *methods;
+ xmlrpc_registry *registry;
+ int registry_valid;
+
+ XMLRPC_ASSERT_ENV_OK(env);
+
+ /* Error-handling preconditions. */
+ methods = NULL;
+ registry = NULL;
+ registry_valid = 0;
+
+ /* Allocate our memory. */
+ methods = xmlrpc_struct_new(env);
+ XMLRPC_FAIL_IF_FAULT(env);
+ registry = (xmlrpc_registry*) malloc(sizeof(xmlrpc_registry));
+ XMLRPC_FAIL_IF_NULL(registry, env, XMLRPC_INTERNAL_ERROR,
+ "Could not allocate memory for registry");
+
+ /* Set everything up. */
+ registry->_introspection_enabled = 1;
+ registry->_methods = methods;
+ registry->_default_method = NULL;
+ registry->_preinvoke_method = NULL;
+ registry_valid = 1;
+
+ /* Install our system methods. */
+ install_system_methods(env, registry);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+ if (env->fault_occurred) {
+ if (registry_valid) {
+ xmlrpc_registry_free(registry);
+ } else {
+ if (methods)
+ xmlrpc_DECREF(methods);
+ if (registry)
+ free(registry);
+ }
+ return NULL;
+ }
+ return registry;
+}
+
+
+
+void
+xmlrpc_registry_free(xmlrpc_registry * registry) {
+
+ XMLRPC_ASSERT_PTR_OK(registry);
+ XMLRPC_ASSERT(registry->_methods != XMLRPC_BAD_POINTER);
+
+ xmlrpc_DECREF(registry->_methods);
+ registry->_methods = XMLRPC_BAD_POINTER;
+ if (registry->_default_method != NULL)
+ xmlrpc_DECREF(registry->_default_method);
+ if (registry->_preinvoke_method != NULL)
+ xmlrpc_DECREF(registry->_preinvoke_method);
+ free(registry);
+}
+
+
+
+/*=========================================================================
+** xmlrpc_registry_disable_introspection
+**=========================================================================
+** See xmlrpc.h for more documentation.
+*/
+
+void
+xmlrpc_registry_disable_introspection(xmlrpc_registry * registry) {
+ XMLRPC_ASSERT_PTR_OK(registry);
+ registry->_introspection_enabled = 0;
+}
+
+
+
+/*=========================================================================
+** xmlrpc_registry_add_method
+**=========================================================================
+** See xmlrpc.h for more documentation.
+*/
+
+void
+xmlrpc_registry_add_method(xmlrpc_env *env,
+ xmlrpc_registry *registry,
+ const char *host,
+ const char *method_name,
+ xmlrpc_method method,
+ void *user_data) {
+
+ xmlrpc_registry_add_method_w_doc (env, registry, host, method_name,
+ method, user_data, "?",
+ "No help is available for this method.");
+}
+
+
+
+void
+xmlrpc_registry_add_method_w_doc(xmlrpc_env *env,
+ xmlrpc_registry *registry,
+ const char *host,
+ const char *method_name,
+ xmlrpc_method method,
+ void *user_data,
+ const char *signature,
+ const char *help) {
+ xmlrpc_value *method_info;
+
+ XMLRPC_ASSERT_ENV_OK(env);
+ XMLRPC_ASSERT_PTR_OK(registry);
+ XMLRPC_ASSERT(host == NULL);
+ XMLRPC_ASSERT_PTR_OK(method_name);
+ XMLRPC_ASSERT_PTR_OK(method);
+
+ /* Error-handling preconditions. */
+ method_info = NULL;
+
+ /* Store our method and user data into our hash table. */
+ method_info = xmlrpc_build_value(env, "(ppss)", (void*) method, user_data,
+ signature, help);
+ XMLRPC_FAIL_IF_FAULT(env);
+ xmlrpc_struct_set_value(env, registry->_methods, method_name, method_info);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+ if (method_info)
+ xmlrpc_DECREF(method_info);
+
+}
+
+
+
+/*=========================================================================
+** xmlrpc_registry_set_default_method
+**=========================================================================
+** See xmlrpc.h for more documentation.
+*/
+
+void
+xmlrpc_registry_set_default_method(xmlrpc_env *env,
+ xmlrpc_registry *registry,
+ xmlrpc_default_method handler,
+ void *user_data) {
+ xmlrpc_value *method_info;
+
+ XMLRPC_ASSERT_ENV_OK(env);
+ XMLRPC_ASSERT_PTR_OK(registry);
+ XMLRPC_ASSERT_PTR_OK(handler);
+
+ /* Error-handling preconditions. */
+ method_info = NULL;
+
+ /* Store our method and user data into our hash table. */
+ method_info = xmlrpc_build_value(env, "(pp)", (void*) handler, user_data);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Dispose of any pre-existing default method and install ours. */
+ if (registry->_default_method)
+ xmlrpc_DECREF(registry->_default_method);
+ registry->_default_method = method_info;
+
+cleanup:
+ if (env->fault_occurred) {
+ if (method_info)
+ xmlrpc_DECREF(method_info);
+ }
+}
+
+
+
+/*=========================================================================
+** xmlrpc_registry_set_preinvoke_method
+**=========================================================================
+** See xmlrpc.h for more documentation.
+*/
+
+void
+xmlrpc_registry_set_preinvoke_method(xmlrpc_env *env,
+ xmlrpc_registry *registry,
+ xmlrpc_preinvoke_method handler,
+ void *user_data) {
+ xmlrpc_value *method_info;
+
+ XMLRPC_ASSERT_ENV_OK(env);
+ XMLRPC_ASSERT_PTR_OK(registry);
+ XMLRPC_ASSERT_PTR_OK(handler);
+
+ /* Error-handling preconditions. */
+ method_info = NULL;
+
+ /* Store our method and user data into our hash table. */
+ method_info = xmlrpc_build_value(env, "(pp)", (void*) handler, user_data);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Dispose of any pre-existing preinvoke method and install ours. */
+ if (registry->_preinvoke_method)
+ xmlrpc_DECREF(registry->_preinvoke_method);
+ registry->_preinvoke_method = method_info;
+
+ cleanup:
+ if (env->fault_occurred) {
+ if (method_info)
+ xmlrpc_DECREF(method_info);
+ }
+}
+
+
+
+/*=========================================================================
+** dispatch_call
+**=========================================================================
+** An internal method which actually does the dispatch. This may get
+** prettified and exported at some point in the future.
+*/
+
+static void
+callPreinvokeMethodIfAny(xmlrpc_env * const envP,
+ xmlrpc_registry * const registryP,
+ const char * const methodName,
+ xmlrpc_value * const paramArrayP) {
+
+ /* Get the preinvoke method, if it is set. */
+ if (registryP->_preinvoke_method) {
+ xmlrpc_preinvoke_method preinvoke_method;
+ void * user_data;
+
+ xmlrpc_parse_value(envP, registryP->_preinvoke_method, "(pp)",
+ &preinvoke_method, &user_data);
+ if (!envP->fault_occurred)
+ (*preinvoke_method)(envP, methodName,
+ paramArrayP, user_data);
+ }
+}
+
+
+
+static void
+callDefaultMethod(xmlrpc_env * const envP,
+ xmlrpc_value * const defaultMethodInfo,
+ const char * const methodName,
+ xmlrpc_value * const paramArrayP,
+ xmlrpc_value ** const resultPP) {
+
+ xmlrpc_default_method default_method;
+ void * user_data;
+
+ xmlrpc_parse_value(envP, defaultMethodInfo, "(pp)",
+ &default_method, &user_data);
+
+ if (!envP->fault_occurred)
+ *resultPP = (*default_method)(envP, NULL, methodName,
+ paramArrayP, user_data);
+}
+
+
+
+static void
+callNamedMethod(xmlrpc_env * const envP,
+ xmlrpc_value * const methodInfo,
+ xmlrpc_value * const paramArrayP,
+ xmlrpc_value ** const resultPP) {
+
+ xmlrpc_method method;
+ void * user_data;
+
+ xmlrpc_parse_value(envP, methodInfo, "(pp*)", &method, &user_data);
+ if (!envP->fault_occurred)
+ *resultPP = (*method)(envP, paramArrayP, user_data);
+}
+
+
+
+static void
+dispatch_call(xmlrpc_env * const envP,
+ xmlrpc_registry * const registryP,
+ const char * const methodName,
+ xmlrpc_value * const paramArrayP,
+ xmlrpc_value ** const resultPP) {
+
+ callPreinvokeMethodIfAny(envP, registryP, methodName, paramArrayP);
+ if (!envP->fault_occurred) {
+ xmlrpc_value * method_info;
+
+ /* Look up the method info for the named method. */
+ xmlrpc_struct_find_value(envP, registryP->_methods,
+ methodName, &method_info);
+ if (!envP->fault_occurred) {
+ if (method_info)
+ callNamedMethod(envP, method_info, paramArrayP, resultPP);
+ else {
+ if (registryP->_default_method)
+ callDefaultMethod(envP, registryP->_default_method,
+ methodName, paramArrayP,
+ resultPP);
+ else {
+ /* No matching method, and no default. */
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_NO_SUCH_METHOD_ERROR,
+ "Method '%s' not defined", methodName);
+ }
+ }
+ }
+ }
+ /* For backward compatibility, for sloppy users: */
+ if (envP->fault_occurred)
+ *resultPP = NULL;
+}
+
+
+
+/*=========================================================================
+** xmlrpc_registry_process_call
+**=========================================================================
+**
+*/
+
+xmlrpc_mem_block *
+xmlrpc_registry_process_call(xmlrpc_env * const envP,
+ xmlrpc_registry * const registryP,
+ const char * const host ATTR_UNUSED,
+ const char * const xml_data,
+ size_t const xml_len) {
+
+ xmlrpc_mem_block * output;
+
+ XMLRPC_ASSERT_ENV_OK(envP);
+ XMLRPC_ASSERT_PTR_OK(xml_data);
+
+ xmlrpc_traceXml("XML-RPC CALL", xml_data, xml_len);
+
+ /* Allocate our output buffer.
+ ** If this fails, we need to die in a special fashion. */
+ output = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
+ if (!envP->fault_occurred) {
+ const char * methodName;
+ xmlrpc_value * paramArray;
+ xmlrpc_env fault;
+
+ xmlrpc_env_init(&fault);
+
+ xmlrpc_parse_call(&fault, xml_data, xml_len,
+ &methodName, &paramArray);
+
+ if (!fault.fault_occurred) {
+ xmlrpc_value * result;
+
+ dispatch_call(&fault, registryP, methodName, paramArray, &result);
+
+ if (!fault.fault_occurred) {
+ xmlrpc_serialize_response(envP, output, result);
+
+ /* A comment here used to say that
+ xmlrpc_serialize_response() could fail and "leave
+ stuff in the buffer." Don't know what that means,
+ but it sounds like something that needs to be
+ fixed. The old code aborted the program here if
+ xmlrpc_serialize_repsonse() failed. 04.11.17
+ */
+ xmlrpc_DECREF(result);
+ }
+ xmlrpc_strfree(methodName);
+ xmlrpc_DECREF(paramArray);
+ }
+ if (!envP->fault_occurred && fault.fault_occurred)
+ xmlrpc_serialize_fault(envP, output, &fault);
+
+ xmlrpc_env_clean(&fault);
+
+ if (envP->fault_occurred)
+ XMLRPC_MEMBLOCK_FREE(char, output);
+ else
+ xmlrpc_traceXml("XML-RPC RESPONSE",
+ XMLRPC_MEMBLOCK_CONTENTS(char, output),
+ XMLRPC_MEMBLOCK_SIZE(char, output));
+ }
+ return output;
+}
+
+
+
+/*=========================================================================
+** system.multicall
+**=========================================================================
+** Low-tech support for transparent, boxed methods.
+*/
+
+static char *multicall_help =
+"Process an array of calls, and return an array of results. Calls should "
+"be structs of the form {'methodName': string, 'params': array}. Each "
+"result will either be a single-item array containg the result value, or "
+"a struct of the form {'faultCode': int, 'faultString': string}. This "
+"is useful when you need to make lots of small calls without lots of "
+"round trips.";
+
+static xmlrpc_value *
+call_one_method(xmlrpc_env *env, xmlrpc_registry *registry,
+ xmlrpc_value *method_info) {
+
+ xmlrpc_value *result_val, *result;
+ char *method_name;
+ xmlrpc_value *param_array;
+
+ /* Error-handling preconditions. */
+ result = result_val = NULL;
+
+ /* Extract our method name and parameters. */
+ xmlrpc_parse_value(env, method_info, "{s:s,s:A,*}",
+ "methodName", &method_name,
+ "params", &param_array);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Watch out for a deep recursion attack. */
+ if (strcmp(method_name, "system.multicall") == 0)
+ XMLRPC_FAIL(env, XMLRPC_REQUEST_REFUSED_ERROR,
+ "Recursive system.multicall strictly forbidden");
+
+ /* Perform the call. */
+ dispatch_call(env, registry, method_name, param_array, &result_val);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Build our one-item result array. */
+ result = xmlrpc_build_value(env, "(V)", result_val);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+ if (result_val)
+ xmlrpc_DECREF(result_val);
+ if (env->fault_occurred) {
+ if (result)
+ xmlrpc_DECREF(result);
+ return NULL;
+ }
+ return result;
+}
+
+
+
+static xmlrpc_value *
+system_multicall(xmlrpc_env *env,
+ xmlrpc_value *param_array,
+ void *user_data) {
+
+ xmlrpc_registry *registry;
+ xmlrpc_value *methlist, *methinfo, *results, *result;
+ size_t size, i;
+ xmlrpc_env env2;
+
+ XMLRPC_ASSERT_ENV_OK(env);
+ XMLRPC_ASSERT_VALUE_OK(param_array);
+ XMLRPC_ASSERT_PTR_OK(user_data);
+
+ /* Error-handling preconditions. */
+ results = result = NULL;
+ xmlrpc_env_init(&env2);
+
+ /* Turn our arguments into something more useful. */
+ registry = (xmlrpc_registry*) user_data;
+ xmlrpc_parse_value(env, param_array, "(A)", &methlist);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Create an empty result list. */
+ results = xmlrpc_build_value(env, "()");
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Loop over our input list, calling each method in turn. */
+ size = xmlrpc_array_size(env, methlist);
+ XMLRPC_ASSERT_ENV_OK(env);
+ for (i = 0; i < size; i++) {
+ methinfo = xmlrpc_array_get_item(env, methlist, i);
+ XMLRPC_ASSERT_ENV_OK(env);
+
+ /* Call our method. */
+ xmlrpc_env_clean(&env2);
+ xmlrpc_env_init(&env2);
+ result = call_one_method(&env2, registry, methinfo);
+
+ /* Turn any fault into a structure. */
+ if (env2.fault_occurred) {
+ XMLRPC_ASSERT(result == NULL);
+ result =
+ xmlrpc_build_value(env, "{s:i,s:s}",
+ "faultCode", (xmlrpc_int32) env2.fault_code,
+ "faultString", env2.fault_string);
+ XMLRPC_FAIL_IF_FAULT(env);
+ }
+
+ /* Append this method result to our master array. */
+ xmlrpc_array_append_item(env, results, result);
+ xmlrpc_DECREF(result);
+ result = NULL;
+ XMLRPC_FAIL_IF_FAULT(env);
+ }
+
+ cleanup:
+ xmlrpc_env_clean(&env2);
+ if (result)
+ xmlrpc_DECREF(result);
+ if (env->fault_occurred) {
+ if (results)
+ xmlrpc_DECREF(results);
+ return NULL;
+ }
+ return results;
+}
+
+
+
+/*=========================================================================
+** system.listMethods
+**=========================================================================
+** List all available methods by name.
+*/
+
+static char *listMethods_help =
+"Return an array of all available XML-RPC methods on this server.";
+
+static xmlrpc_value *
+system_listMethods(xmlrpc_env *env,
+ xmlrpc_value *param_array,
+ void *user_data) {
+
+ xmlrpc_registry *registry;
+ xmlrpc_value *method_names, *method_name, *method_info;
+ size_t size, i;
+
+ XMLRPC_ASSERT_ENV_OK(env);
+ XMLRPC_ASSERT_VALUE_OK(param_array);
+ XMLRPC_ASSERT_PTR_OK(user_data);
+
+ /* Error-handling preconditions. */
+ method_names = NULL;
+
+ /* Turn our arguments into something more useful. */
+ registry = (xmlrpc_registry*) user_data;
+ xmlrpc_parse_value(env, param_array, "()");
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Make sure we're allowed to introspect. */
+ if (!registry->_introspection_enabled)
+ XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
+ "Introspection disabled for security reasons");
+
+ /* Iterate over all the methods in the registry, adding their names
+ ** to a list. */
+ method_names = xmlrpc_build_value(env, "()");
+ XMLRPC_FAIL_IF_FAULT(env);
+ size = xmlrpc_struct_size(env, registry->_methods);
+ XMLRPC_FAIL_IF_FAULT(env);
+ for (i = 0; i < size; i++) {
+ xmlrpc_struct_get_key_and_value(env, registry->_methods, i,
+ &method_name, &method_info);
+ XMLRPC_FAIL_IF_FAULT(env);
+ xmlrpc_array_append_item(env, method_names, method_name);
+ XMLRPC_FAIL_IF_FAULT(env);
+ }
+
+ cleanup:
+ if (env->fault_occurred) {
+ if (method_names)
+ xmlrpc_DECREF(method_names);
+ return NULL;
+ }
+ return method_names;
+}
+
+
+
+/*=========================================================================
+** system.methodHelp
+**=========================================================================
+** Get the help string for a particular method.
+*/
+
+static char *methodHelp_help =
+"Given the name of a method, return a help string.";
+
+static xmlrpc_value *
+system_methodHelp(xmlrpc_env *env,
+ xmlrpc_value *param_array,
+ void *user_data) {
+
+ xmlrpc_registry *registry;
+ char *method_name;
+ xmlrpc_value *ignored1, *ignored2, *ignored3, *help;
+
+ XMLRPC_ASSERT_ENV_OK(env);
+ XMLRPC_ASSERT_VALUE_OK(param_array);
+ XMLRPC_ASSERT_PTR_OK(user_data);
+
+ /* Turn our arguments into something more useful. */
+ registry = (xmlrpc_registry*) user_data;
+ xmlrpc_parse_value(env, param_array, "(s)", &method_name);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Make sure we're allowed to introspect. */
+ if (!registry->_introspection_enabled)
+ XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
+ "Introspection disabled for security reasons");
+
+ /* Get our documentation string. */
+ xmlrpc_parse_value(env, registry->_methods, "{s:(VVVV*),*}",
+ method_name, &ignored1, &ignored2, &ignored3, &help);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+ if (env->fault_occurred)
+ return NULL;
+ xmlrpc_INCREF(help);
+ return help;
+}
+
+
+
+/*=========================================================================
+** system.methodSignature
+**=========================================================================
+** Return an array of arrays describing possible signatures for this
+** method.
+**
+** XXX - This is the ugliest function in the entire library.
+*/
+
+static char *methodSignature_help =
+"Given the name of a method, return an array of legal signatures. "
+"Each signature is an array of strings. The first item of each signature "
+"is the return type, and any others items are parameter types.";
+
+static char *bad_sig_str =
+"Application has incorrect method signature information";
+
+#define BAD_SIG(env) \
+ XMLRPC_FAIL((env), XMLRPC_INTERNAL_ERROR, bad_sig_str);
+
+static xmlrpc_value *
+system_methodSignature(xmlrpc_env *env,
+ xmlrpc_value *param_array,
+ void *user_data) {
+
+ xmlrpc_registry *registry;
+ char *method_name;
+ xmlrpc_value *ignored1, *ignored2, *ignored3;
+ xmlrpc_value *item, *current, *result;
+ int at_sig_start;
+ char *sig, *code;
+
+ XMLRPC_ASSERT_ENV_OK(env);
+ XMLRPC_ASSERT_VALUE_OK(param_array);
+ XMLRPC_ASSERT_PTR_OK(user_data);
+
+ /* Error-handling preconditions. */
+ item = current = result = NULL;
+
+ /* Turn our arguments into something more useful. */
+ registry = (xmlrpc_registry*) user_data;
+ xmlrpc_parse_value(env, param_array, "(s)", &method_name);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Make sure we're allowed to introspect. */
+ if (!registry->_introspection_enabled)
+ XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
+ "Introspection disabled for security reasons");
+
+ /* Get our signature string. */
+ xmlrpc_parse_value(env, registry->_methods, "{s:(VVsV*),*}",
+ method_name, &ignored1, &ignored2, &sig, &ignored3);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ if (sig[0] == '?' && sig[1] == '\0') {
+ /* No signature supplied. */
+ result = xmlrpc_build_value(env, "s", "undef");
+ XMLRPC_FAIL_IF_FAULT(env);
+ } else {
+ /* Build an array of arrays. */
+ current = xmlrpc_build_value(env, "()");
+ XMLRPC_FAIL_IF_FAULT(env);
+ result = xmlrpc_build_value(env, "(V)", current);
+ XMLRPC_FAIL_IF_FAULT(env);
+ at_sig_start = 1;
+
+ do {
+ next_loop:
+
+ /* Process the current code. */
+ switch (*(sig++)) {
+ case 'i': code = "int"; break;
+ case 'b': code = "boolean"; break;
+ case 'd': code = "double"; break;
+ case 's': code = "string"; break;
+ case '8': code = "dateTime.iso8601"; break;
+ case '6': code = "base64"; break;
+ case 'S': code = "struct"; break;
+ case 'A': code = "array"; break;
+
+ case ',':
+ /* Start a new signature array. */
+ if (at_sig_start)
+ BAD_SIG(env);
+ xmlrpc_DECREF(current);
+ current = xmlrpc_build_value(env, "()");
+ XMLRPC_FAIL_IF_FAULT(env);
+ xmlrpc_array_append_item(env, result, current);
+ XMLRPC_FAIL_IF_FAULT(env);
+ at_sig_start = 1;
+ goto next_loop;
+
+ default:
+ BAD_SIG(env);
+ }
+
+ /* Append the appropriate string to our current signature. */
+ item = xmlrpc_build_value(env, "s", code);
+ XMLRPC_FAIL_IF_FAULT(env);
+ xmlrpc_array_append_item(env, current, item);
+ xmlrpc_DECREF(item);
+ item = NULL;
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ /* Advance to the next code, and skip over ':' if necessary. */
+ if (at_sig_start) {
+ if (*sig != ':')
+ BAD_SIG(env);
+ sig++;
+ at_sig_start = 0;
+ }
+
+ } while (*sig != '\0');
+ }
+
+ cleanup:
+ if (item)
+ xmlrpc_DECREF(item);
+ if (current)
+ xmlrpc_DECREF(current);
+ if (env->fault_occurred) {
+ if (result)
+ xmlrpc_DECREF(result);
+ return NULL;
+ }
+ return result;
+}
+
+
+
+/*=========================================================================
+** install_system_methods
+**=========================================================================
+** Install the standard methods under system.*.
+** This particular function is highly experimental, and may disappear
+** without warning.
+*/
+
+static void
+install_system_methods(xmlrpc_env *env, xmlrpc_registry *registry) {
+
+ xmlrpc_registry_add_method_w_doc(env, registry, NULL,
+ "system.listMethods",
+ &system_listMethods, registry,
+ "A:", listMethods_help);
+ XMLRPC_FAIL_IF_FAULT(env);
+ xmlrpc_registry_add_method_w_doc(env, registry, NULL,
+ "system.methodSignature",
+ &system_methodSignature, registry,
+ "A:s", methodSignature_help);
+ XMLRPC_FAIL_IF_FAULT(env);
+ xmlrpc_registry_add_method_w_doc(env, registry, NULL,
+ "system.methodHelp",
+ &system_methodHelp, registry,
+ "s:s", methodHelp_help);
+ XMLRPC_FAIL_IF_FAULT(env);
+ xmlrpc_registry_add_method_w_doc(env, registry, NULL,
+ "system.multicall",
+ &system_multicall, registry,
+ "A:A", multicall_help);
+ XMLRPC_FAIL_IF_FAULT(env);
+
+ cleanup:
+ return;
+}