/* Copyright information is at end of file */ #include "xmlrpc_config.h" #include #include #include #include "xmlrpc-c/base_int.h" #include "xmlrpc-c/string_int.h" #include "xmlrpc-c/base.h" #include "xmlrpc-c/server.h" #include "version.h" #include "registry.h" #include "method.h" #include "system_method.h" struct systemMethodReg { /*---------------------------------------------------------------------------- Information needed to register a system method -----------------------------------------------------------------------------*/ const char * const methodName; xmlrpc_method2 const methodFunction; const char * const signatureString; const char * const helpText; }; void xmlrpc_registry_disable_introspection(xmlrpc_registry * const registryP) { XMLRPC_ASSERT_PTR_OK(registryP); registryP->introspectionEnabled = false; } /*========================================================================= system.multicall =========================================================================*/ static void callOneMethod(xmlrpc_env * const envP, xmlrpc_registry * const registryP, xmlrpc_value * const rpcDescP, void * const callInfo, xmlrpc_value ** const resultPP) { const char * methodName; xmlrpc_value * paramArrayP; XMLRPC_ASSERT_ENV_OK(envP); if (xmlrpc_value_type(rpcDescP) != XMLRPC_TYPE_STRUCT) xmlrpc_env_set_fault_formatted( envP, XMLRPC_TYPE_ERROR, "An element of the multicall array is type %u, but should " "be a struct (with members 'methodName' and 'params')", xmlrpc_value_type(rpcDescP)); else { xmlrpc_decompose_value(envP, rpcDescP, "{s:s,s:A,*}", "methodName", &methodName, "params", ¶mArrayP); if (!envP->fault_occurred) { /* Watch out for a deep recursion attack. */ if (xmlrpc_streq(methodName, "system.multicall")) xmlrpc_env_set_fault_formatted( envP, XMLRPC_REQUEST_REFUSED_ERROR, "Recursive system.multicall forbidden"); else { xmlrpc_env env; xmlrpc_value * resultValP; xmlrpc_env_init(&env); xmlrpc_dispatchCall(&env, registryP, methodName, paramArrayP, callInfo, &resultValP); if (env.fault_occurred) { /* Method failed, so result is a fault structure */ *resultPP = xmlrpc_build_value( envP, "{s:i,s:s}", "faultCode", (xmlrpc_int32) env.fault_code, "faultString", env.fault_string); } else { *resultPP = xmlrpc_build_value(envP, "(V)", resultValP); xmlrpc_DECREF(resultValP); } xmlrpc_env_clean(&env); } xmlrpc_DECREF(paramArrayP); xmlrpc_strfree(methodName); } } } static void getMethListFromMulticallPlist(xmlrpc_env * const envP, xmlrpc_value * const paramArrayP, xmlrpc_value ** const methlistPP) { if (xmlrpc_array_size(envP, paramArrayP) != 1) xmlrpc_env_set_fault_formatted( envP, XMLRPC_PARSE_ERROR, "system.multicall takes one parameter, which is an " "array, each element describing one RPC. You " "supplied %u arguments", xmlrpc_array_size(envP, paramArrayP)); else { xmlrpc_value * methlistP; xmlrpc_array_read_item(envP, paramArrayP, 0, &methlistP); XMLRPC_ASSERT_ENV_OK(envP); if (xmlrpc_value_type(methlistP) != XMLRPC_TYPE_ARRAY) xmlrpc_env_set_fault_formatted( envP, XMLRPC_TYPE_ERROR, "system.multicall's parameter should be an array, " "each element describing one RPC. But it is type " "%u instead.", xmlrpc_value_type(methlistP)); else *methlistPP = methlistP; if (envP->fault_occurred) xmlrpc_DECREF(methlistP); } } static xmlrpc_value * system_multicall(xmlrpc_env * const envP, xmlrpc_value * const paramArrayP, void * const serverInfo, void * const callInfo) { xmlrpc_registry * registryP; xmlrpc_value * resultsP; xmlrpc_value * methlistP; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT_ARRAY_OK(paramArrayP); XMLRPC_ASSERT_PTR_OK(serverInfo); resultsP = NULL; /* defeat compiler warning */ /* Turn our arguments into something more useful. */ registryP = (xmlrpc_registry*) serverInfo; getMethListFromMulticallPlist(envP, paramArrayP, &methlistP); if (!envP->fault_occurred) { /* Create an initially empty result list. */ resultsP = xmlrpc_array_new(envP); if (!envP->fault_occurred) { /* Loop over our input list, calling each method in turn. */ unsigned int const methodCount = xmlrpc_array_size(envP, methlistP); unsigned int i; for (i = 0; i < methodCount && !envP->fault_occurred; ++i) { xmlrpc_value * const methinfoP = xmlrpc_array_get_item(envP, methlistP, i); xmlrpc_value * resultP; XMLRPC_ASSERT_ENV_OK(envP); callOneMethod(envP, registryP, methinfoP, callInfo, &resultP); if (!envP->fault_occurred) { /* Append this method result to our master array. */ xmlrpc_array_append_item(envP, resultsP, resultP); xmlrpc_DECREF(resultP); } } if (envP->fault_occurred) xmlrpc_DECREF(resultsP); xmlrpc_DECREF(methlistP); } } return resultsP; } static struct systemMethodReg const methodMulticall = { "system.multicall", &system_multicall, "A:A", "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.", }; /*========================================================================= system.listMethods =========================================================================*/ static void createMethodListArray(xmlrpc_env * const envP, xmlrpc_registry * const registryP, xmlrpc_value ** const methodListPP) { /*---------------------------------------------------------------------------- Create as an XML-RPC array value a list of names of methods registered in registry 'registryP'. This is the type of value that the system.listMethods method is supposed to return. -----------------------------------------------------------------------------*/ xmlrpc_value * methodListP; methodListP = xmlrpc_array_new(envP); if (!envP->fault_occurred) { xmlrpc_methodNode * methodNodeP; for (methodNodeP = registryP->methodListP->firstMethodP; methodNodeP && !envP->fault_occurred; methodNodeP = methodNodeP->nextP) { xmlrpc_value * methodNameVP; methodNameVP = xmlrpc_string_new(envP, methodNodeP->methodName); if (!envP->fault_occurred) { xmlrpc_array_append_item(envP, methodListP, methodNameVP); xmlrpc_DECREF(methodNameVP); } } if (envP->fault_occurred) xmlrpc_DECREF(methodListP); } *methodListPP = methodListP; } static xmlrpc_value * system_listMethods(xmlrpc_env * const envP, xmlrpc_value * const paramArrayP, void * const serverInfo, void * const callInfo ATTR_UNUSED) { xmlrpc_registry * const registryP = serverInfo; xmlrpc_value * retvalP; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT_VALUE_OK(paramArrayP); XMLRPC_ASSERT_PTR_OK(serverInfo); xmlrpc_decompose_value(envP, paramArrayP, "()"); if (!envP->fault_occurred) { if (!registryP->introspectionEnabled) xmlrpc_env_set_fault_formatted( envP, XMLRPC_INTROSPECTION_DISABLED_ERROR, "Introspection is disabled in this server " "for security reasons"); else createMethodListArray(envP, registryP, &retvalP); } return retvalP; } static struct systemMethodReg const methodListMethods = { "system.listMethods", &system_listMethods, "A:", "Return an array of all available XML-RPC methods on this server.", }; /*========================================================================= system.methodHelp =========================================================================*/ static void getHelpString(xmlrpc_env * const envP, const char * const methodName, xmlrpc_registry * const registryP, xmlrpc_value ** const helpStringPP) { xmlrpc_methodInfo * methodP; xmlrpc_methodListLookupByName(registryP->methodListP, methodName, &methodP); if (!methodP) xmlrpc_env_set_fault_formatted( envP, XMLRPC_NO_SUCH_METHOD_ERROR, "Method '%s' does not exist", methodName); else *helpStringPP = xmlrpc_string_new(envP, methodP->helpText); } static xmlrpc_value * system_methodHelp(xmlrpc_env * const envP, xmlrpc_value * const paramArrayP, void * const serverInfo, void * const callInfo ATTR_UNUSED) { xmlrpc_registry * const registryP = serverInfo; xmlrpc_value * retvalP; const char * methodName; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT_VALUE_OK(paramArrayP); XMLRPC_ASSERT_PTR_OK(serverInfo); xmlrpc_decompose_value(envP, paramArrayP, "(s)", &methodName); if (!envP->fault_occurred) { if (!registryP->introspectionEnabled) xmlrpc_env_set_fault_formatted( envP, XMLRPC_INTROSPECTION_DISABLED_ERROR, "Introspection is disabled in this server " "for security reasons"); else getHelpString(envP, methodName, registryP, &retvalP); } xmlrpc_strfree(methodName); return retvalP; } static struct systemMethodReg const methodMethodHelp = { "system.methodHelp", &system_methodHelp, "s:s", "Given the name of a method, return a help string.", }; /*========================================================================= system.methodSignature ==========================================================================*/ static void buildNoSigSuppliedResult(xmlrpc_env * const envP, xmlrpc_value ** const resultPP) { xmlrpc_env env; xmlrpc_env_init(&env); *resultPP = xmlrpc_string_new(&env, "undef"); if (env.fault_occurred) xmlrpc_faultf(envP, "Unable to construct 'undef'. %s", env.fault_string); xmlrpc_env_clean(&env); } static void buildSignatureValue(xmlrpc_env * const envP, struct xmlrpc_signature * const signatureP, xmlrpc_value ** const sigValuePP) { xmlrpc_value * sigValueP; unsigned int i; sigValueP = xmlrpc_array_new(envP); { xmlrpc_value * retTypeVP; retTypeVP = xmlrpc_string_new(envP, signatureP->retType); xmlrpc_array_append_item(envP, sigValueP, retTypeVP); xmlrpc_DECREF(retTypeVP); } for (i = 0; i < signatureP->argCount && !envP->fault_occurred; ++i) { xmlrpc_value * argTypeVP; argTypeVP = xmlrpc_string_new(envP, signatureP->argList[i]); if (!envP->fault_occurred) { xmlrpc_array_append_item(envP, sigValueP, argTypeVP); xmlrpc_DECREF(argTypeVP); } } if (envP->fault_occurred) xmlrpc_DECREF(sigValueP); else *sigValuePP = sigValueP; } static void getSignatureList(xmlrpc_env * const envP, xmlrpc_registry * const registryP, const char * const methodName, xmlrpc_value ** const signatureListPP) { /*---------------------------------------------------------------------------- Get the signature list array for method named 'methodName' from registry 'registryP'. If there is no signature information for the method in the registry, return *signatureListPP == NULL. Nonexistent method is considered a failure. -----------------------------------------------------------------------------*/ xmlrpc_methodInfo * methodP; xmlrpc_methodListLookupByName(registryP->methodListP, methodName, &methodP); if (!methodP) xmlrpc_env_set_fault_formatted( envP, XMLRPC_NO_SUCH_METHOD_ERROR, "Method '%s' does not exist", methodName); else { if (!methodP->signatureListP->firstSignatureP) *signatureListPP = NULL; else { xmlrpc_value * signatureListP; signatureListP = xmlrpc_array_new(envP); if (!envP->fault_occurred) { struct xmlrpc_signature * signatureP; for (signatureP = methodP->signatureListP->firstSignatureP; signatureP && !envP->fault_occurred; signatureP = signatureP->nextP) { xmlrpc_value * signatureVP; buildSignatureValue(envP, signatureP, &signatureVP); xmlrpc_array_append_item(envP, signatureListP, signatureVP); xmlrpc_DECREF(signatureVP); } if (envP->fault_occurred) xmlrpc_DECREF(signatureListP); } *signatureListPP = signatureListP; } } } static xmlrpc_value * system_methodSignature(xmlrpc_env * const envP, xmlrpc_value * const paramArrayP, void * const serverInfo, void * const callInfo ATTR_UNUSED) { xmlrpc_registry * const registryP = (xmlrpc_registry *) serverInfo; xmlrpc_value * retvalP; const char * methodName; xmlrpc_env env; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT_VALUE_OK(paramArrayP); XMLRPC_ASSERT_PTR_OK(serverInfo); retvalP = NULL; /* quiet compiler unset variable warning */ xmlrpc_env_init(&env); /* Turn our arguments into something more useful. */ xmlrpc_decompose_value(&env, paramArrayP, "(s)", &methodName); if (env.fault_occurred) xmlrpc_env_set_fault_formatted( envP, env.fault_code, "Invalid parameter list. %s", env.fault_string); else { if (!registryP->introspectionEnabled) xmlrpc_env_set_fault(envP, XMLRPC_INTROSPECTION_DISABLED_ERROR, "Introspection disabled on this server"); else { xmlrpc_value * signatureListP; getSignatureList(envP, registryP, methodName, &signatureListP); if (!envP->fault_occurred) { if (signatureListP) retvalP = signatureListP; else buildNoSigSuppliedResult(envP, &retvalP); } } xmlrpc_strfree(methodName); } xmlrpc_env_clean(&env); return retvalP; } static struct systemMethodReg const methodMethodSignature = { "system.methodSignature", &system_methodSignature, "s:s", "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.", }; /*========================================================================= system.shutdown ==========================================================================*/ static xmlrpc_value * system_shutdown(xmlrpc_env * const envP, xmlrpc_value * const paramArrayP, void * const serverInfo, void * const callInfo) { xmlrpc_registry * const registryP = (xmlrpc_registry *) serverInfo; xmlrpc_value * retvalP; const char * comment; xmlrpc_env env; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT_VALUE_OK(paramArrayP); XMLRPC_ASSERT_PTR_OK(serverInfo); xmlrpc_env_init(&env); retvalP = NULL; /* quiet compiler warning */ /* Turn our arguments into something more useful. */ xmlrpc_decompose_value(&env, paramArrayP, "(s)", &comment); if (env.fault_occurred) xmlrpc_env_set_fault_formatted( envP, env.fault_code, "Invalid parameter list. %s", env.fault_string); else { if (!registryP->shutdownServerFn) xmlrpc_env_set_fault( envP, 0, "This server program is not capable of " "shutting down"); else { registryP->shutdownServerFn( &env, registryP->shutdownContext, comment, callInfo); if (env.fault_occurred) xmlrpc_env_set_fault(envP, env.fault_code, env.fault_string); else { retvalP = xmlrpc_int_new(&env, 0); if (env.fault_occurred) xmlrpc_faultf(envP, "Failed to construct return value. %s", env.fault_string); } } xmlrpc_strfree(comment); } xmlrpc_env_clean(&env); return retvalP; } static struct systemMethodReg const methodShutdown = { "system.shutdown", &system_shutdown, "i:s", "Shut down the server. Return code is always zero.", }; /*========================================================================= system.capabilities =========================================================================*/ static void getCapabilities(xmlrpc_env * const envP, xmlrpc_registry * const registryP ATTR_UNUSED, xmlrpc_value ** const capabilitiesPP) { *capabilitiesPP = xmlrpc_build_value( envP, "{s:s,s:i,s:i,s:i,s:i}", "facility", "xmlrpc-c", "version_major", XMLRPC_VERSION_MAJOR, "version_minor", XMLRPC_VERSION_MINOR, "version_point", XMLRPC_VERSION_POINT, "protocol_version", 2 ); } static xmlrpc_value * system_capabilities(xmlrpc_env * const envP, xmlrpc_value * const paramArrayP, void * const serverInfo, void * const callInfo ATTR_UNUSED) { xmlrpc_registry * const registryP = serverInfo; xmlrpc_value * retvalP; unsigned int paramCount; XMLRPC_ASSERT_ENV_OK(envP); XMLRPC_ASSERT_VALUE_OK(paramArrayP); XMLRPC_ASSERT_PTR_OK(serverInfo); paramCount = xmlrpc_array_size(envP, paramArrayP); if (paramCount > 0) xmlrpc_env_set_fault_formatted( envP, XMLRPC_INDEX_ERROR, "There are no parameters. You supplied %u", paramCount); else getCapabilities(envP, registryP, &retvalP); return retvalP; } static struct systemMethodReg const methodCapabilities = { "system.capabilities", &system_capabilities, "S:", "Return the capabilities of XML-RPC server. This includes the " "version number of the XML-RPC For C/C++ software" }; /*============================================================================ Installer of system methods ============================================================================*/ static void registerSystemMethod(xmlrpc_env * const envP, xmlrpc_registry * const registryP, struct systemMethodReg const methodReg) { xmlrpc_env env; xmlrpc_env_init(&env); xmlrpc_registry_add_method2( &env, registryP, methodReg.methodName, methodReg.methodFunction, methodReg.signatureString, methodReg.helpText, registryP); if (env.fault_occurred) xmlrpc_faultf(envP, "Failed to register '%s' system method. %s", methodReg.methodName, env.fault_string); xmlrpc_env_clean(&env); } void xmlrpc_installSystemMethods(xmlrpc_env * const envP, xmlrpc_registry * const registryP) { /*---------------------------------------------------------------------------- Install the built-in methods (system.*) into registry 'registryP'. -----------------------------------------------------------------------------*/ if (!envP->fault_occurred) registerSystemMethod(envP, registryP, methodListMethods); if (!envP->fault_occurred) registerSystemMethod(envP, registryP, methodMethodSignature); if (!envP->fault_occurred) registerSystemMethod(envP, registryP, methodMethodHelp); if (!envP->fault_occurred) registerSystemMethod(envP, registryP, methodMulticall); if (!envP->fault_occurred) registerSystemMethod(envP, registryP, methodShutdown); if (!envP->fault_occurred) registerSystemMethod(envP, registryP, methodCapabilities); } /* 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. */