summaryrefslogtreecommitdiffstats
path: root/Utilities/cmxmlrpc/xmlrpc_server_abyss.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmxmlrpc/xmlrpc_server_abyss.c')
-rw-r--r--Utilities/cmxmlrpc/xmlrpc_server_abyss.c799
1 files changed, 799 insertions, 0 deletions
diff --git a/Utilities/cmxmlrpc/xmlrpc_server_abyss.c b/Utilities/cmxmlrpc/xmlrpc_server_abyss.c
new file mode 100644
index 0000000..5964c33
--- /dev/null
+++ b/Utilities/cmxmlrpc/xmlrpc_server_abyss.c
@@ -0,0 +1,799 @@
+/* Copyright (C) 2001 by First Peer, Inc. 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.
+**
+** There is more copyright information in the bottom half of this file.
+** Please see it for more details. */
+
+#include "xmlrpc_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "abyss.h"
+
+#include "xmlrpc.h"
+#include "xmlrpc_server.h"
+#include "xmlrpc_int.h"
+#include "xmlrpc_server_abyss.h"
+#include "xmlrpc_server_abyss_int.h"
+
+
+/*=========================================================================
+** die_if_fault_occurred
+**=========================================================================
+** If certain kinds of out-of-memory errors occur during server setup,
+** we want to quit and print an error.
+*/
+
+static void die_if_fault_occurred(xmlrpc_env *env) {
+ if (env->fault_occurred) {
+ fprintf(stderr, "Unexpected XML-RPC fault: %s (%d)\n",
+ env->fault_string, env->fault_code);
+ exit(1);
+ }
+}
+
+
+
+/*=========================================================================
+** send_xml_data
+**=========================================================================
+** Blast some XML data back to the client.
+*/
+
+static void
+send_xml_data (TSession * const r,
+ char * const buffer,
+ uint64 const len) {
+
+ const char * const http_cookie = NULL;
+ /* This used to set http_cookie to getenv("HTTP_COOKIE"), but
+ that doesn't make any sense -- environment variables are not
+ appropriate for this. So for now, cookie code is disabled.
+ - Bryan 2004.10.03.
+ */
+
+ /* fwrite(buffer, sizeof(char), len, stderr); */
+
+ /* XXX - Is it safe to chunk our response? */
+ ResponseChunked(r);
+
+ ResponseStatus(r, 200);
+
+ if (http_cookie) {
+ /* There's an auth cookie, so pass it back in the response. */
+
+ char *cookie_response;
+
+ cookie_response = malloc(10+strlen(http_cookie));
+ sprintf(cookie_response, "auth=%s", http_cookie);
+
+ /* Return abyss response. */
+ ResponseAddField(r, "Set-Cookie", cookie_response);
+
+ free(cookie_response);
+ }
+
+
+ ResponseContentType(r, "text/xml; charset=\"utf-8\"");
+ ResponseContentLength(r, len);
+
+ ResponseWrite(r);
+
+ HTTPWrite(r, buffer, len);
+ HTTPWriteEnd(r);
+}
+
+
+
+/*=========================================================================
+** send_error
+**=========================================================================
+** Send an error back to the client.
+*/
+
+static void
+send_error(TSession * const abyssSessionP,
+ unsigned int const status) {
+
+ ResponseStatus(abyssSessionP, (uint16) status);
+ ResponseError(abyssSessionP);
+}
+
+
+
+/*=========================================================================
+** get_buffer_data
+**=========================================================================
+** Extract some data from the TConn's underlying input buffer. Do not
+** extract more than 'max'.
+*/
+
+static void
+get_buffer_data(TSession * const r,
+ int const max,
+ char ** const out_start,
+ int * const out_len) {
+
+ /* Point to the start of our data. */
+ *out_start = &r->conn->buffer[r->conn->bufferpos];
+
+ /* Decide how much data to retrieve. */
+ *out_len = r->conn->buffersize - r->conn->bufferpos;
+ if (*out_len > max)
+ *out_len = max;
+
+ /* Update our buffer position. */
+ r->conn->bufferpos += *out_len;
+}
+
+
+
+/*=========================================================================
+** get_body
+**=========================================================================
+** Slurp the body of the request into an xmlrpc_mem_block.
+*/
+
+static void
+getBody(xmlrpc_env * const envP,
+ TSession * const abyssSessionP,
+ unsigned int const contentSize,
+ xmlrpc_mem_block ** const bodyP) {
+/*----------------------------------------------------------------------------
+ Get the entire body from the Abyss session and return it as the new
+ memblock *bodyP.
+
+ The first chunk of the body may already be in Abyss's buffer. We
+ retrieve that before reading more.
+-----------------------------------------------------------------------------*/
+ xmlrpc_mem_block * body;
+
+ body = xmlrpc_mem_block_new(envP, 0);
+ if (!envP->fault_occurred) {
+ unsigned int bytesRead;
+ char * chunkPtr;
+ int chunkLen;
+
+ bytesRead = 0;
+
+ while (!envP->fault_occurred && bytesRead < contentSize) {
+ get_buffer_data(abyssSessionP, contentSize - bytesRead,
+ &chunkPtr, &chunkLen);
+ bytesRead += chunkLen;
+
+ XMLRPC_TYPED_MEM_BLOCK_APPEND(char, envP, body,
+ chunkPtr, chunkLen);
+
+ if (bytesRead < contentSize) {
+ /* Get the next chunk of data from the connection into the
+ buffer
+ */
+ abyss_bool succeeded;
+
+ /* Reset our read buffer & flush data from previous reads. */
+ ConnReadInit(abyssSessionP->conn);
+
+ /* Read more network data into our buffer. If we encounter
+ a timeout, exit immediately. We're very forgiving about
+ the timeout here. We allow a full timeout per network
+ read, which would allow somebody to keep a connection
+ alive nearly indefinitely. But it's hard to do anything
+ intelligent here without very complicated code.
+ */
+ succeeded = ConnRead(abyssSessionP->conn,
+ abyssSessionP->server->timeout);
+ if (!succeeded)
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_TIMEOUT_ERROR, "Timed out waiting for "
+ "client to send its POST data");
+ }
+ }
+ if (envP->fault_occurred)
+ xmlrpc_mem_block_free(body);
+ else
+ *bodyP = body;
+ }
+}
+
+
+
+static void
+storeCookies(TSession * const httpRequestP,
+ unsigned int * const httpErrorP) {
+/*----------------------------------------------------------------------------
+ Get the cookie settings from the HTTP headers and remember them for
+ use in responses.
+-----------------------------------------------------------------------------*/
+ const char * const cookie = RequestHeaderValue(httpRequestP, "cookie");
+ if (cookie) {
+ /*
+ Setting the value in an environment variable doesn't make
+ any sense. So for now, cookie code is disabled.
+ -Bryan 04.10.03.
+
+ setenv("HTTP_COOKIE", cookie, 1);
+ */
+ }
+ /* TODO: parse HTTP_COOKIE to find auth pair, if there is one */
+
+ *httpErrorP = 0;
+}
+
+
+
+
+static void
+validateContentType(TSession * const httpRequestP,
+ unsigned int * const httpErrorP) {
+/*----------------------------------------------------------------------------
+ If the client didn't specify a content-type of "text/xml", return
+ "400 Bad Request". We can't allow the client to default this header,
+ because some firewall software may rely on all XML-RPC requests
+ using the POST method and a content-type of "text/xml".
+-----------------------------------------------------------------------------*/
+ const char * const content_type =
+ RequestHeaderValue(httpRequestP, "content-type");
+ if (content_type == NULL || strcmp(content_type, "text/xml") != 0)
+ *httpErrorP = 400;
+ else
+ *httpErrorP = 0;
+}
+
+
+
+static void
+processContentLength(TSession * const httpRequestP,
+ unsigned int * const inputLenP,
+ unsigned int * const httpErrorP) {
+/*----------------------------------------------------------------------------
+ Make sure the content length is present and non-zero. This is
+ technically required by XML-RPC, but we only enforce it because we
+ don't want to figure out how to safely handle HTTP < 1.1 requests
+ without it. If the length is missing, return "411 Length Required".
+-----------------------------------------------------------------------------*/
+ const char * const content_length =
+ RequestHeaderValue(httpRequestP, "content-length");
+ if (content_length == NULL)
+ *httpErrorP = 411;
+ else {
+ int const contentLengthValue = atoi(content_length);
+ if (contentLengthValue <= 0)
+ *httpErrorP = 400;
+ else {
+ *httpErrorP = 0;
+ *inputLenP = (unsigned int)contentLengthValue;
+ }
+ }
+}
+
+
+/****************************************************************************
+ Abyss handlers (to be registered with and called by Abyss)
+****************************************************************************/
+
+/* XXX - This variable is *not* currently threadsafe. Once the server has
+** been started, it must be treated as read-only. */
+static xmlrpc_registry *global_registryP;
+
+static const char * trace_abyss;
+
+static void
+processCall(TSession * const abyssSessionP,
+ int const inputLen) {
+/*----------------------------------------------------------------------------
+ Handle an RPC request. This is an HTTP request that has the proper form
+ to be one of our RPCs.
+-----------------------------------------------------------------------------*/
+ xmlrpc_env env;
+
+ if (trace_abyss)
+ fprintf(stderr, "xmlrpc_server_abyss RPC2 handler processing RPC.\n");
+
+ xmlrpc_env_init(&env);
+
+ /* SECURITY: Make sure our content length is legal.
+ XXX - We can cast 'inputLen' because we know it's >= 0, yes?
+ */
+ if ((size_t) inputLen > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
+ xmlrpc_env_set_fault_formatted(
+ &env, XMLRPC_LIMIT_EXCEEDED_ERROR,
+ "XML-RPC request too large (%d bytes)", inputLen);
+ else {
+ xmlrpc_mem_block *body;
+ /* Read XML data off the wire. */
+ getBody(&env, abyssSessionP, inputLen, &body);
+ if (!env.fault_occurred) {
+ xmlrpc_mem_block * output;
+ /* Process the RPC. */
+ output = xmlrpc_registry_process_call(
+ &env, global_registryP, NULL,
+ XMLRPC_MEMBLOCK_CONTENTS(char, body),
+ XMLRPC_MEMBLOCK_SIZE(char, body));
+ if (!env.fault_occurred) {
+ /* Send our the result. */
+ send_xml_data(abyssSessionP,
+ XMLRPC_MEMBLOCK_CONTENTS(char, output),
+ XMLRPC_MEMBLOCK_SIZE(char, output));
+
+ XMLRPC_MEMBLOCK_FREE(char, output);
+ }
+ XMLRPC_MEMBLOCK_FREE(char, body);
+ }
+ }
+ if (env.fault_occurred) {
+ if (env.fault_code == XMLRPC_TIMEOUT_ERROR)
+ send_error(abyssSessionP, 408); /* 408 Request Timeout */
+ else
+ send_error(abyssSessionP, 500); /* 500 Internal Server Error */
+ }
+
+ xmlrpc_env_clean(&env);
+}
+
+
+
+/*=========================================================================
+** xmlrpc_server_abyss_rpc2_handler
+**=========================================================================
+** This handler processes all requests to '/RPC2'. See the header for
+** more documentation.
+*/
+
+xmlrpc_bool
+xmlrpc_server_abyss_rpc2_handler (TSession * const r) {
+
+ xmlrpc_bool retval;
+
+ if (trace_abyss)
+ fprintf(stderr, "xmlrpc_server_abyss RPC2 handler called.\n");
+
+ /* We handle only requests to /RPC2, the default XML-RPC URL.
+ Everything else we pass through to other handlers.
+ */
+ if (strcmp(r->uri, "/RPC2") != 0)
+ retval = FALSE;
+ else {
+ retval = TRUE;
+
+ /* We understand only the POST HTTP method. For anything else, return
+ "405 Method Not Allowed".
+ */
+ if (r->method != m_post)
+ send_error(r, 405);
+ else {
+ unsigned int httpError;
+ storeCookies(r, &httpError);
+ if (httpError)
+ send_error(r, httpError);
+ else {
+ unsigned int httpError;
+ validateContentType(r, &httpError);
+ if (httpError)
+ send_error(r, httpError);
+ else {
+ unsigned int httpError;
+ int inputLen;
+
+ processContentLength(r, &inputLen, &httpError);
+ if (httpError)
+ send_error(r, httpError);
+
+ processCall(r, inputLen);
+ }
+ }
+ }
+ }
+ if (trace_abyss)
+ fprintf(stderr, "xmlrpc_server_abyss RPC2 handler returning.\n");
+ return retval;
+}
+
+
+
+/*=========================================================================
+** xmlrpc_server_abyss_default_handler
+**=========================================================================
+** This handler returns a 404 Not Found for all requests. See the header
+** for more documentation.
+*/
+
+xmlrpc_bool
+xmlrpc_server_abyss_default_handler (TSession * const r) {
+ send_error(r, 404);
+
+ return TRUE;
+}
+
+
+
+/**************************************************************************
+**
+** The code below was adapted from the main.c file of the Abyss webserver
+** project. In addition to the other copyrights on this file, the following
+** code is also under this copyright:
+**
+** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
+** 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 <time.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include <io.h>
+#else
+/* Must check this
+#include <sys/io.h>
+*/
+#endif /* _WIN32 */
+
+#ifdef _UNIX
+#include <sys/signal.h>
+#include <sys/wait.h>
+#include <grp.h>
+#endif
+
+
+#ifdef _UNIX
+static void
+sigterm(int const sig) {
+ TraceExit("Signal %d received. Exiting...\n",sig);
+}
+#endif
+
+
+#ifdef _UNIX
+static void
+sigchld(int const sig ATTR_UNUSED) {
+/*----------------------------------------------------------------------------
+ This is a signal handler for a SIGCHLD signal (which informs us that
+ one of our child processes has terminated).
+
+ We respond by reaping the zombie process.
+
+ Implementation note: In some systems, just setting the signal handler
+ to SIG_IGN (ignore signal) does this. In others, it doesn't.
+-----------------------------------------------------------------------------*/
+ pid_t pid;
+ int status;
+
+ /* Reap defunct children until there aren't any more. */
+ for (;;) {
+ pid = waitpid( (pid_t) -1, &status, WNOHANG );
+
+ /* none left */
+ if (pid==0)
+ break;
+
+ if (pid<0) {
+ /* because of ptrace */
+ if (errno==EINTR)
+ continue;
+
+ break;
+ }
+ }
+}
+#endif /* _UNIX */
+
+static TServer globalSrv;
+ /* When you use the old interface (xmlrpc_server_abyss_init(), etc.),
+ this is the Abyss server to which they refer. Obviously, there can be
+ only one Abyss server per program using this interface.
+ */
+
+
+void
+xmlrpc_server_abyss_init(int const flags ATTR_UNUSED,
+ const char * const config_file) {
+
+ DateInit();
+ MIMETypeInit();
+
+ ServerCreate(&globalSrv, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL);
+
+ ConfReadServerFile(config_file, &globalSrv);
+
+ xmlrpc_server_abyss_init_registry();
+ /* Installs /RPC2 handler and default handler that use the
+ built-in registry.
+ */
+
+ ServerInit(&globalSrv);
+}
+
+
+
+static void
+setupSignalHandlers(void) {
+#ifdef _UNIX
+ struct sigaction mysigaction;
+
+ sigemptyset(&mysigaction.sa_mask);
+ mysigaction.sa_flags = 0;
+
+ /* These signals abort the program, with tracing */
+ mysigaction.sa_handler = sigterm;
+ sigaction(SIGTERM, &mysigaction, NULL);
+ sigaction(SIGINT, &mysigaction, NULL);
+ sigaction(SIGHUP, &mysigaction, NULL);
+ sigaction(SIGUSR1, &mysigaction, NULL);
+
+ /* This signal indicates connection closed in the middle */
+ mysigaction.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &mysigaction, NULL);
+
+ /* This signal indicates a child process (request handler) has died */
+ mysigaction.sa_handler = sigchld;
+ sigaction(SIGCHLD, &mysigaction, NULL);
+#endif
+}
+
+
+
+static void
+runServer(TServer * const srvP,
+ runfirstFn const runfirst,
+ void * const runfirstArg) {
+
+ setupSignalHandlers();
+
+#ifdef _UNIX
+ /* Become a daemon */
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ TraceExit("Unable to become a daemon");
+ default:
+ exit(0);
+ };
+
+ setsid();
+
+ /* Change the current user if we are root */
+ if (getuid()==0) {
+ if (srvP->uid == (uid_t)-1)
+ TraceExit("Can't run under root privileges. "
+ "Please add a User option in your "
+ "Abyss configuration file.");
+
+#ifdef HAVE_SETGROUPS
+ if (setgroups(0,NULL)==(-1))
+ TraceExit("Failed to setup the group.");
+ if (srvP->gid != (gid_t)-1)
+ if (setgid(srvP->gid)==(-1))
+ TraceExit("Failed to change the group.");
+#endif
+
+ if (setuid(srvP->uid) == -1)
+ TraceExit("Failed to change the user.");
+ };
+
+ if (srvP->pidfile!=(-1)) {
+ char z[16];
+
+ sprintf(z,"%d",getpid());
+ FileWrite(&srvP->pidfile,z,strlen(z));
+ FileClose(&srvP->pidfile);
+ };
+#endif
+
+ /* We run the user supplied runfirst after forking, but before accepting
+ connections (helpful when running with threads)
+ */
+ if (runfirst)
+ runfirst(runfirstArg);
+
+ ServerRun(srvP);
+
+ /* We can't exist here because ServerRun doesn't return */
+ XMLRPC_ASSERT(FALSE);
+}
+
+
+
+void
+xmlrpc_server_abyss_run_first(runfirstFn const runfirst,
+ void * const runfirstArg) {
+
+ runServer(&globalSrv, runfirst, runfirstArg);
+}
+
+
+
+void
+xmlrpc_server_abyss_run(void) {
+ runServer(&globalSrv, NULL, NULL);
+}
+
+
+
+void
+xmlrpc_server_abyss_set_handlers(TServer * const srvP,
+ xmlrpc_registry * const registryP) {
+
+ /* Abyss ought to have a way to register with a handler an argument
+ that gets passed to the handler every time it is called. That's
+ where we should put the registry handle. But we don't find such
+ a thing in Abyss, so we use the global variable 'global_registryP'.
+ */
+ global_registryP = registryP;
+
+ trace_abyss = getenv("XMLRPC_TRACE_ABYSS");
+
+ ServerAddHandler(srvP, xmlrpc_server_abyss_rpc2_handler);
+ ServerDefaultHandler(srvP, xmlrpc_server_abyss_default_handler);
+}
+
+
+
+void
+xmlrpc_server_abyss(xmlrpc_env * const envP,
+ const xmlrpc_server_abyss_parms * const parmsP,
+ unsigned int const parm_size) {
+
+ XMLRPC_ASSERT_ENV_OK(envP);
+
+ if (parm_size < XMLRPC_APSIZE(registryP))
+ xmlrpc_env_set_fault_formatted(
+ envP, XMLRPC_INTERNAL_ERROR,
+ "You must specify members at least up through "
+ "'registryP' in the server parameters argument. "
+ "That would mean the parameter size would be >= %u "
+ "but you specified a size of %u",
+ XMLRPC_APSIZE(registryP), parm_size);
+ else {
+ TServer srv;
+ runfirstFn runfirst;
+ void * runfirstArg;
+
+ DateInit();
+ MIMETypeInit();
+
+ ServerCreate(&srv, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL);
+
+ ConfReadServerFile(parmsP->config_file_name, &srv);
+
+ xmlrpc_server_abyss_set_handlers(&srv, parmsP->registryP);
+
+ ServerInit(&srv);
+
+ if (parm_size >= XMLRPC_APSIZE(runfirst_arg)) {
+ runfirst = parmsP->runfirst;
+ runfirstArg = parmsP->runfirst_arg;
+ } else {
+ runfirst = NULL;
+ runfirstArg = NULL;
+ }
+ runServer(&srv, runfirst, runfirstArg);
+ }
+}
+
+
+
+/*=========================================================================
+** XML-RPC Server Method Registry
+**=========================================================================
+** A simple front-end to our method registry.
+*/
+
+/* XXX - This variable is *not* currently threadsafe. Once the server has
+** been started, it must be treated as read-only. */
+static xmlrpc_registry *builtin_registryP;
+
+void
+xmlrpc_server_abyss_init_registry(void) {
+
+ /* This used to just create the registry and Caller would be
+ responsible for adding the handlers that use it.
+
+ But that isn't very modular -- the handlers and registry go
+ together; there's no sense in using the built-in registry and
+ not the built-in handlers because if you're custom building
+ something, you can just make your own regular registry. So now
+ we tie them together, and we don't export our handlers.
+ */
+ xmlrpc_env env;
+
+ xmlrpc_env_init(&env);
+ builtin_registryP = xmlrpc_registry_new(&env);
+ die_if_fault_occurred(&env);
+ xmlrpc_env_clean(&env);
+
+ xmlrpc_server_abyss_set_handlers(&globalSrv, builtin_registryP);
+}
+
+
+
+xmlrpc_registry *
+xmlrpc_server_abyss_registry(void) {
+
+ /* This is highly deprecated. If you want to mess with a registry,
+ make your own with xmlrpc_registry_new() -- don't mess with the
+ internal one.
+ */
+ return builtin_registryP;
+}
+
+
+
+/* A quick & easy shorthand for adding a method. */
+void
+xmlrpc_server_abyss_add_method (char * const method_name,
+ xmlrpc_method const method,
+ void * const user_data) {
+ xmlrpc_env env;
+
+ xmlrpc_env_init(&env);
+ xmlrpc_registry_add_method(&env, builtin_registryP, NULL, method_name,
+ method, user_data);
+ die_if_fault_occurred(&env);
+ xmlrpc_env_clean(&env);
+}
+
+
+
+void
+xmlrpc_server_abyss_add_method_w_doc (char * const method_name,
+ xmlrpc_method const method,
+ void * const user_data,
+ char * const signature,
+ char * const help) {
+
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+ xmlrpc_registry_add_method_w_doc(
+ &env, builtin_registryP, NULL, method_name,
+ method, user_data, signature, help);
+ die_if_fault_occurred(&env);
+ xmlrpc_env_clean(&env);
+}