initial load of upstream version 1.06.32
[xmlrpc-c] / src / cpp / registry.cpp
diff --git a/src/cpp/registry.cpp b/src/cpp/registry.cpp
new file mode 100644 (file)
index 0000000..9a53b92
--- /dev/null
@@ -0,0 +1,365 @@
+#include <cassert>
+#include <string>
+#include <memory>
+#include <algorithm>
+
+#include "xmlrpc-c/girerr.hpp"
+using girerr::error;
+#include "xmlrpc-c/girmem.hpp"
+using girmem::autoObject;
+using girmem::autoObjectPtr;
+#include "xmlrpc-c/base.h"
+#include "xmlrpc-c/base.hpp"
+#include "env_wrap.hpp"
+
+#include "xmlrpc-c/registry.hpp"
+
+using namespace std;
+using namespace xmlrpc_c;
+
+
+namespace {
+
+void
+throwIfError(env_wrap const& env) {
+
+    if (env.env_c.fault_occurred)
+        throw(error(env.env_c.fault_string));
+}
+
+
+} // namespace
+
+namespace xmlrpc_c {
+
+
+method::method() : 
+        _signature("?"),
+        _help("No help is available for this method")
+        {};
+
+
+
+method::~method() {}
+
+
+
+methodPtr::methodPtr(method * const methodP) {
+    this->point(methodP);
+}
+
+
+
+method *
+methodPtr::operator->() const {
+
+    autoObject * const p(this->objectP);
+    return dynamic_cast<method *>(p);
+}
+
+
+
+defaultMethod::~defaultMethod() {}
+
+
+
+defaultMethodPtr::defaultMethodPtr() {}
+
+
+defaultMethodPtr::defaultMethodPtr(defaultMethod * const methodP) {
+    this->point(methodP);
+}
+
+
+
+defaultMethod *
+defaultMethodPtr::operator->() const {
+
+    autoObject * const p(this->objectP);
+    return dynamic_cast<defaultMethod *>(p);
+}
+
+
+
+defaultMethod *
+defaultMethodPtr::get() const {
+
+    autoObject * const p(this->objectP);
+    return dynamic_cast<defaultMethod *>(p);
+}
+
+
+
+registry::registry() {
+
+    env_wrap env;
+
+    this->c_registryP = xmlrpc_registry_new(&env.env_c);
+
+    throwIfError(env);
+}
+
+
+
+registry::~registry(void) {
+
+    xmlrpc_registry_free(this->c_registryP);
+}
+
+
+
+registryPtr::registryPtr() {}
+
+
+
+registryPtr::registryPtr(registry * const registryP) {
+    this->point(registryP);
+}
+
+
+
+registry *
+registryPtr::operator->() const {
+
+    autoObject * const p(this->objectP);
+    return dynamic_cast<registry *>(p);
+}
+
+
+
+registry *
+registryPtr::get() const {
+
+    autoObject * const p(this->objectP);
+    return dynamic_cast<registry *>(p);
+}
+
+
+
+static xmlrpc_c::paramList
+pListFromXmlrpcArray(xmlrpc_value * const arrayP) {
+/*----------------------------------------------------------------------------
+   Convert an XML-RPC array in C (not C++) form to a parameter list object
+   that can be passed to a method execute method.
+
+   This is glue code to allow us to hook up C++ Xmlrpc-c code to 
+   C Xmlrpc-c code.
+-----------------------------------------------------------------------------*/
+    env_wrap env;
+
+    XMLRPC_ASSERT_ARRAY_OK(arrayP);
+
+    unsigned int const arraySize = xmlrpc_array_size(&env.env_c, arrayP);
+
+    assert(!env.env_c.fault_occurred);
+
+    xmlrpc_c::paramList paramList(arraySize);
+    
+    for (unsigned int i = 0; i < arraySize; ++i) {
+        xmlrpc_value * arrayItemP;
+
+        xmlrpc_array_read_item(&env.env_c, arrayP, i, &arrayItemP);
+        assert(!env.env_c.fault_occurred);
+
+        paramList.add(xmlrpc_c::value(arrayItemP));
+        
+        xmlrpc_DECREF(arrayItemP);
+    }
+    return paramList;
+}
+
+
+
+static xmlrpc_value *
+c_executeMethod(xmlrpc_env *   const envP,
+                xmlrpc_value * const paramArrayP,
+                void *         const methodPtr) {
+/*----------------------------------------------------------------------------
+   This is a function designed to be called via a C registry to
+   execute an XML-RPC method, but use a C++ method object to do the
+   work.  You register this function as the method function and a
+   pointer to the C++ method object as the method data in the C
+   registry.
+
+   If we had a pure C++ registry, this would be unnecessary.
+
+   Since we can't throw an error back to the C code, we catch anything
+   the XML-RPC method's execute() method throws, and any error we
+   encounter in processing the result it returns, and turn it into an
+   XML-RPC method failure.  This will cause a leak if the execute()
+   method actually created a result, since it will not get destroyed.
+-----------------------------------------------------------------------------*/
+    xmlrpc_c::method * const methodP = 
+        static_cast<xmlrpc_c::method *>(methodPtr);
+    xmlrpc_c::paramList const paramList(pListFromXmlrpcArray(paramArrayP));
+
+    xmlrpc_value * retval;
+
+    try {
+        xmlrpc_c::value result;
+
+        try {
+            methodP->execute(paramList, &result);
+        } catch (xmlrpc_c::fault const& fault) {
+            xmlrpc_env_set_fault(envP, fault.getCode(), 
+                                 fault.getDescription().c_str()); 
+        } catch (girerr::error const& error) {
+            xmlrpc_env_set_fault(envP, 0, error.what());
+        }
+        if (envP->fault_occurred)
+            retval = NULL;
+        else
+            retval = result.cValue();
+    } catch (...) {
+        xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
+                             "Unexpected error executing the code for this "
+                             "particular method, detected by the Xmlrpc-c "
+                             "method registry code.  The method did not "
+                             "fail; rather, it did not complete at all.");
+        retval = NULL;
+    }
+    return retval;
+}
+
+
+static xmlrpc_value *
+c_executeDefaultMethod(xmlrpc_env *   const envP,
+                       const char *   const , // host
+                       const char *   const methodName,
+                       xmlrpc_value * const paramArrayP,
+                       void *         const methodPtr) {
+/*----------------------------------------------------------------------------
+   This is a function designed to be called via a C registry to
+   execute an XML-RPC method, but use a C++ method object to do the
+   work.  You register this function as the default method function and a
+   pointer to the C++ default method object as the method data in the C
+   registry.
+
+   If we had a pure C++ registry, this would be unnecessary.
+
+   Since we can't throw an error back to the C code, we catch anything
+   the XML-RPC method's execute() method throws, and any error we
+   encounter in processing the result it returns, and turn it into an
+   XML-RPC method failure.  This will cause a leak if the execute()
+   method actually created a result, since it will not get destroyed.
+-----------------------------------------------------------------------------*/
+    defaultMethod * const methodP = 
+        static_cast<defaultMethod *>(methodPtr);
+    paramList const paramList(pListFromXmlrpcArray(paramArrayP));
+
+    xmlrpc_value * retval;
+
+    try {
+        value result;
+        
+        try {
+            methodP->execute(methodName, paramList, &result);
+        } catch (xmlrpc_c::fault const& fault) {
+            xmlrpc_env_set_fault(envP, fault.getCode(), 
+                                 fault.getDescription().c_str()); 
+        } catch (girerr::error const& error) {
+            xmlrpc_env_set_fault(envP, 0, error.what());
+        }
+        if (envP->fault_occurred)
+            retval = NULL;
+        else
+            retval = result.cValue();
+    } catch (...) {
+        xmlrpc_env_set_fault(envP, XMLRPC_INTERNAL_ERROR,
+                             "Unexpected error executing the default "
+                             "method code, detected by the Xmlrpc-c "
+                             "method registry code.  The method did not "
+                             "fail; rather, it did not complete at all.");
+        retval = NULL;
+    }
+    return retval;
+}
+
+
+void
+registry::addMethod(string    const name,
+                    methodPtr const methodP) {
+
+    this->methodList.push_back(methodP);
+
+    env_wrap env;
+    
+       xmlrpc_registry_add_method_w_doc(
+        &env.env_c, this->c_registryP, NULL,
+        name.c_str(), &c_executeMethod, 
+        (void*) methodP.get(), 
+        methodP->signature().c_str(), methodP->help().c_str());
+
+    throwIfError(env);
+}
+
+
+
+void
+registry::setDefaultMethod(defaultMethodPtr const methodP) {
+
+    this->defaultMethodP = methodP;
+
+    env_wrap env;
+    
+    xmlrpc_registry_set_default_method(
+        &env.env_c, this->c_registryP,
+        &c_executeDefaultMethod, (void*) methodP.get());
+
+    throwIfError(env);
+}
+
+
+
+void
+registry::disableIntrospection() {
+
+    xmlrpc_registry_disable_introspection(this->c_registryP);
+}
+
+
+
+void
+registry::processCall(string   const& callXml,
+                      string * const  responseXmlP) const {
+/*----------------------------------------------------------------------------
+   Process an XML-RPC call whose XML is 'callXml'.
+
+   Return the response XML as *responseXmlP.
+
+   If we are unable to execute the call, we throw an error.  But if
+   the call executes and the method merely fails in an XML-RPC sense, we
+   don't.  In that case, *responseXmlP indicates the failure.
+-----------------------------------------------------------------------------*/
+    env_wrap env;
+    xmlrpc_mem_block * output;
+
+    // For the pure C++ version, this will have to parse 'callXml'
+    // into a method name and parameters, look up the method name in
+    // the registry, call the method's execute() method, then marshall
+    // the result into XML and return it as *responseXmlP.  It will
+    // also have to execute system methods (e.g. introspection)
+    // itself.  This will be more or less like what
+    // xmlrpc_registry_process_call() does.
+
+    output = xmlrpc_registry_process_call(
+        &env.env_c, this->c_registryP, NULL,
+        callXml.c_str(), callXml.length());
+
+    throwIfError(env);
+
+    *responseXmlP = string(XMLRPC_MEMBLOCK_CONTENTS(char, output),
+                           XMLRPC_MEMBLOCK_SIZE(char, output));
+    
+    xmlrpc_mem_block_free(output);
+}
+
+xmlrpc_registry *
+registry::c_registry() const {
+
+    return this->c_registryP;
+}
+
+}  // namespace