X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=src%2Fregistry.c;fp=src%2Fregistry.c;h=549786185e594226fcce8e94f986bd3026df519c;hb=ce67d0cdeaa37c3e856e23ae4010480887165630;hp=0000000000000000000000000000000000000000;hpb=e355d4e7962400470f467b88f5568de9c8324475;p=xmlrpc-c diff --git a/src/registry.c b/src/registry.c new file mode 100644 index 0000000..5497861 --- /dev/null +++ b/src/registry.c @@ -0,0 +1,492 @@ +/* Copyright information is at end of file */ + +#include "xmlrpc_config.h" + +#include +#include +#include + +#include "bool.h" +#include "mallocvar.h" +#include "xmlrpc-c/base_int.h" +#include "xmlrpc-c/string_int.h" +#include "xmlrpc-c/base.h" +#include "xmlrpc-c/server.h" +#include "system_method.h" + +#include "registry.h" + +/*========================================================================= + XML-RPC Server Method Registry +=========================================================================== + A method registry is a list of XML-RPC methods for a server to + implement, along with the details of how to implement each -- most + notably a function pointer for a function that executes the method. + + To build an XML-RPC server, just add a communication facility. + + The registry object consists principally of an xmlrpc_value. That + xmlrpc_value is a struct, with method name as key. Each value in + the struct a "method info" array. A method info array has the + following items: + + 0: cptr: method function ptr + 1: cptr: user data + 2: array: signature list + 3: string: help text. + + The signature list array contains one item for each form of call (a + single method might have multiple forms, e.g. one takes two integer + arguments; another takes a single string). The array for each form + represents a signature. It has an item for each parameter and one + for the result. Item 0 is for the result, the rest are in order of + the parameters. Each item of that array is the name of the XML-RPC + element that represents that type, e.g. "int" or + "dateTime.iso8601". + + Example signature: + + (("int" + "double" + "double" + ) + ("int" + ) + ) + + The signature list array is empty to indicate that there is no signature + information in the registry (it doesn't mean there are no valid forms + of calling the method -- just that the registry declines to state). + + + WARNING: there's a basic problem with using xmlrpc_value objects to + represent the registry: xmlrpc_value objects are defined to be not + thread-safe: you can't use one from two threads at the same time. + But XML-RPC servers are often threaded, with multiple threads + simultaneously executing multiple RPCs. The normal Xmlrpc-c Abyss + server is a good example. + + As a hack to make this work, we use the old, deprecated "get" + functions that don't take a reference in the call dispatching code. + Maintaining the reference count is the only way that the the + thread-unsafety can manifest itself in our application. Since the + registry has at least one reference to each xmlrpc_value as long as + the registry exists, we don't really need the exact reference count, + so the deprecated functions work fine. + +=========================================================================*/ + + + +xmlrpc_registry * +xmlrpc_registry_new(xmlrpc_env * const envP) { + + xmlrpc_registry * registryP; + + XMLRPC_ASSERT_ENV_OK(envP); + + MALLOCVAR(registryP); + + if (registryP == NULL) + xmlrpc_faultf(envP, "Could not allocate memory for registry"); + else { + registryP->_introspection_enabled = true; + registryP->_default_method = NULL; + registryP->_preinvoke_method = NULL; + registryP->_shutdown_server_fn = NULL; + + registryP->_methods = xmlrpc_struct_new(envP); + if (!envP->fault_occurred) { + xmlrpc_installSystemMethods(envP, registryP); + } + if (envP->fault_occurred) + free(registryP); + } + return registryP; +} + + + +void +xmlrpc_registry_free(xmlrpc_registry * const registryP) { + + XMLRPC_ASSERT_PTR_OK(registryP); + XMLRPC_ASSERT(registryP->_methods != XMLRPC_BAD_POINTER); + + xmlrpc_DECREF(registryP->_methods); + + if (registryP->_default_method != NULL) + xmlrpc_DECREF(registryP->_default_method); + + if (registryP->_preinvoke_method != NULL) + xmlrpc_DECREF(registryP->_preinvoke_method); + + free(registryP); +} + + + +void +xmlrpc_registry_add_method_w_doc( + xmlrpc_env * const envP, + xmlrpc_registry * const registryP, + const char * const host ATTR_UNUSED, + const char * const methodName, + xmlrpc_method const method, + void * const userData, + const char * const signatureString, + const char * const help) { + + const char * const helpString = + help ? help : "No help is available for this method."; + + xmlrpc_env env; + xmlrpc_value * signatureListP; + + XMLRPC_ASSERT_ENV_OK(envP); + XMLRPC_ASSERT_PTR_OK(registryP); + XMLRPC_ASSERT(host == NULL); + XMLRPC_ASSERT_PTR_OK(methodName); + XMLRPC_ASSERT_PTR_OK(method); + + xmlrpc_env_init(&env); + + xmlrpc_buildSignatureArray(&env, signatureString, &signatureListP); + if (env.fault_occurred) + xmlrpc_faultf(envP, "Can't interpret signature string '%s'. %s", + signatureString, env.fault_string); + else { + xmlrpc_value * methodInfoP; + + XMLRPC_ASSERT_VALUE_OK(signatureListP); + + methodInfoP = xmlrpc_build_value(envP, "(ppVs)", (void*) method, + userData, signatureListP, helpString); + if (!envP->fault_occurred) { + xmlrpc_struct_set_value(envP, registryP->_methods, + methodName, methodInfoP); + + xmlrpc_DECREF(methodInfoP); + } + xmlrpc_DECREF(signatureListP); + } + xmlrpc_env_clean(&env); +} + + + +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."); +} + + + +/*========================================================================= +** 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); + } +} + + + + +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); + } +} + + + +void +xmlrpc_registry_set_shutdown(xmlrpc_registry * const registryP, + xmlrpc_server_shutdown_fn * const shutdownFn, + void * const context) { + + XMLRPC_ASSERT_PTR_OK(registryP); + XMLRPC_ASSERT_PTR_OK(shutdownFn); + + registryP->_shutdown_server_fn = shutdownFn; + + registryP->_shutdown_context = context; +} + + + +/*========================================================================= +** 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); +} + + + +void +xmlrpc_dispatchCall(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 * methodInfoP; + xmlrpc_env methodLookupEnv; + + xmlrpc_env_init(&methodLookupEnv); + + /* See comments at top of file about why we use the deprecated + xmlrpc_struct_get_value() here + */ + methodInfoP = xmlrpc_struct_get_value(&methodLookupEnv, + registryP->_methods, + methodName); + if (!methodLookupEnv.fault_occurred) + callNamedMethod(envP, methodInfoP, paramArrayP, resultPP); + else if (methodLookupEnv.fault_code == XMLRPC_INDEX_ERROR) { + 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); + } + } else + xmlrpc_faultf(envP, "failed to lookup method in registry's " + "internal method struct. %s", + methodLookupEnv.fault_string); + xmlrpc_env_clean(&methodLookupEnv); + } + /* 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 parseEnv; + + xmlrpc_env_init(&fault); + xmlrpc_env_init(&parseEnv); + + xmlrpc_parse_call(&parseEnv, xml_data, xml_len, + &methodName, ¶mArray); + + if (parseEnv.fault_occurred) + xmlrpc_env_set_fault_formatted( + &fault, XMLRPC_PARSE_ERROR, + "Call XML not a proper XML-RPC call. %s", + parseEnv.fault_string); + else { + xmlrpc_value * result; + + xmlrpc_dispatchCall(&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(&parseEnv); + 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; +} + + +/* 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. */