initial load of upstream version 1.06.32
[xmlrpc-c] / src / xmlrpc_server_abyss.c
diff --git a/src/xmlrpc_server_abyss.c b/src/xmlrpc_server_abyss.c
new file mode 100644 (file)
index 0000000..310a8c1
--- /dev/null
@@ -0,0 +1,1201 @@
+/* Copyright information is at the end of the file */
+
+#include "xmlrpc_config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#ifdef _WIN32
+#  include <io.h>
+#else
+#  include <signal.h>
+#  include <sys/wait.h>
+#  include <grp.h>
+#endif
+
+#include "mallocvar.h"
+#include "xmlrpc-c/abyss.h"
+
+#include "xmlrpc-c/base.h"
+#include "xmlrpc-c/server.h"
+#include "xmlrpc-c/base_int.h"
+#include "xmlrpc-c/string_int.h"
+#include "xmlrpc-c/server_abyss.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);
+    }
+}
+
+
+
+static void
+addAuthCookie(xmlrpc_env * const envP,
+              TSession *   const abyssSessionP,
+              const char * const authCookie) {
+
+    const char * cookieResponse;
+    
+    xmlrpc_asprintf(&cookieResponse, "auth=%s", authCookie);
+    
+    if (cookieResponse == xmlrpc_strsol)
+        xmlrpc_faultf(envP, "Insufficient memory to generate cookie "
+                      "response header.");
+    else {
+        ResponseAddField(abyssSessionP, "Set-Cookie", cookieResponse);
+    
+        xmlrpc_strfree(cookieResponse);
+    }
+}   
+    
+
+
+static void 
+sendXmlData(xmlrpc_env * const envP,
+            TSession *   const abyssSessionP, 
+            const char * const body, 
+            size_t       const len,
+            bool         const chunked) {
+/*----------------------------------------------------------------------------
+   Generate an HTTP response containing body 'body' of length 'len'
+   characters.
+
+   This is meant to run in the context of an Abyss URI handler for
+   Abyss session 'abyssSessionP'.
+-----------------------------------------------------------------------------*/
+    const char * 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.
+        */
+
+    /* Various bugs before Xmlrpc-c 1.05 caused the response to be not
+       chunked in the most basic case, but chunked if the client explicitly
+       requested keepalive.  I think it's better not to chunk, because
+       it's simpler, so I removed this in 1.05.  I don't know what the
+       purpose of chunking would be, and an original comment suggests
+       the author wasn't sure chunking was a good idea.
+
+       In 1.06 we added the user option to chunk.
+    */
+    if (chunked)
+        ResponseChunked(abyssSessionP);
+
+    ResponseStatus(abyssSessionP, 200);
+
+    if (http_cookie)
+        /* There's an auth cookie, so pass it back in the response. */
+        addAuthCookie(envP, abyssSessionP, http_cookie);
+
+    if ((size_t)(uint32_t)len != len)
+        xmlrpc_faultf(envP, "XML-RPC method generated a response too "
+                      "large for Abyss to send");
+    else {
+        uint32_t const abyssLen = (uint32_t)len;
+
+        ResponseContentType(abyssSessionP, "text/xml; charset=\"utf-8\"");
+        ResponseContentLength(abyssSessionP, abyssLen);
+        
+        ResponseWriteStart(abyssSessionP);
+        ResponseWriteBody(abyssSessionP, body, abyssLen);
+        ResponseWriteEnd(abyssSessionP);
+    }
+}
+
+
+
+static void
+sendError(TSession *   const abyssSessionP, 
+          unsigned int const status) {
+/*----------------------------------------------------------------------------
+  Send an error response back to the client.
+   
+-----------------------------------------------------------------------------*/
+    ResponseStatus(abyssSessionP, (uint16_t) status);
+    ResponseError(abyssSessionP);
+}
+
+
+
+static void
+traceChunkRead(TSession * const abyssSessionP) {
+
+    fprintf(stderr, "XML-RPC handler got a chunk of %u bytes\n",
+            (unsigned int)SessionReadDataAvail(abyssSessionP));
+}
+
+
+
+static void
+refillBufferFromConnection(xmlrpc_env * const envP,
+                           TSession *   const abyssSessionP,
+                           const char * const trace) {
+/*----------------------------------------------------------------------------
+   Get the next chunk of data from the connection into the buffer.
+-----------------------------------------------------------------------------*/
+    abyss_bool succeeded;
+
+    succeeded = SessionRefillBuffer(abyssSessionP);
+
+    if (!succeeded)
+        xmlrpc_env_set_fault_formatted(
+            envP, XMLRPC_TIMEOUT_ERROR, "Timed out waiting for "
+            "client to send its POST data");
+    else {
+        if (trace)
+            traceChunkRead(abyssSessionP);
+    }
+}
+
+
+
+static void
+getBody(xmlrpc_env *        const envP,
+        TSession *          const abyssSessionP,
+        size_t              const contentSize,
+        const char *        const trace,
+        xmlrpc_mem_block ** const bodyP) {
+/*----------------------------------------------------------------------------
+   Get the entire body, which is of size 'contentSize' bytes, 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;
+
+    if (trace)
+        fprintf(stderr, "XML-RPC handler processing body.  "
+                "Content Size = %u bytes\n", (unsigned)contentSize);
+
+    body = xmlrpc_mem_block_new(envP, 0);
+    if (!envP->fault_occurred) {
+        size_t bytesRead;
+        const char * chunkPtr;
+        size_t chunkLen;
+
+        bytesRead = 0;
+
+        while (!envP->fault_occurred && bytesRead < contentSize) {
+            SessionGetReadData(abyssSessionP, contentSize - bytesRead, 
+                               &chunkPtr, &chunkLen);
+            bytesRead += chunkLen;
+
+            assert(bytesRead <= contentSize);
+
+            XMLRPC_MEMBLOCK_APPEND(char, envP, body, chunkPtr, chunkLen);
+            if (bytesRead < contentSize)
+                refillBufferFromConnection(envP, abyssSessionP, trace);
+        }
+        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)
+        *httpErrorP = 400;
+    else {
+        const char * const sempos = strchr(content_type, ';');
+        unsigned int baselen;
+            /* Length of the base portion of the content type, e.g.
+               "text/xml" int "text/xml;charset=utf-8"
+            */
+
+        if (sempos)
+            baselen = sempos - content_type;
+        else
+            baselen = strlen(content_type);
+
+        if (!xmlrpc_strneq(content_type, "text/xml", baselen))
+            *httpErrorP = 400;
+        else
+            *httpErrorP = 0;
+    }
+}
+
+
+
+static void
+processContentLength(TSession *     const httpRequestP,
+                     size_t *       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 {
+        if (content_length[0] == '\0')
+            *httpErrorP = 400;
+        else {
+            unsigned long contentLengthValue;
+            char * tail;
+        
+            contentLengthValue = strtoul(content_length, &tail, 10);
+        
+            if (*tail != '\0')
+                /* There's non-numeric crap in the length */
+                *httpErrorP = 400;
+            else if (contentLengthValue < 1)
+                *httpErrorP = 400;
+            else if ((unsigned long)(size_t)contentLengthValue 
+                     != contentLengthValue)
+                *httpErrorP = 400;
+            else {
+                *httpErrorP = 0;
+                *inputLenP = (size_t)contentLengthValue;
+            }
+        }
+    }
+}
+
+
+
+static void
+traceHandlerCalled(TSession * const abyssSessionP) {
+    
+    const char * methodDesc;
+    const TRequestInfo * requestInfoP;
+
+    fprintf(stderr, "xmlrpc_server_abyss URI path handler called.\n");
+
+    SessionGetRequestInfo(abyssSessionP, &requestInfoP);
+
+    fprintf(stderr, "URI = '%s'\n", requestInfoP->uri);
+
+    switch (requestInfoP->method) {
+    case m_unknown: methodDesc = "unknown";   break;
+    case m_get:     methodDesc = "get";       break;
+    case m_put:     methodDesc = "put";       break;
+    case m_head:    methodDesc = "head";      break;
+    case m_post:    methodDesc = "post";      break;
+    case m_delete:  methodDesc = "delete";    break;
+    case m_trace:   methodDesc = "trace";     break;
+    case m_options: methodDesc = "m_options"; break;
+    default:        methodDesc = "?";
+    }
+    fprintf(stderr, "HTTP method = '%s'\n", methodDesc);
+
+    if (requestInfoP->query)
+        fprintf(stderr, "query (component of URL)='%s'\n",
+                requestInfoP->query);
+    else
+        fprintf(stderr, "URL has no query component\n");
+}
+
+
+
+static void
+processCall(TSession *        const abyssSessionP,
+            size_t            const contentSize,
+            xmlrpc_registry * const registryP,
+            bool              const wantChunk,
+            const char *      const trace) {
+/*----------------------------------------------------------------------------
+   Handle an RPC request.  This is an HTTP request that has the proper form
+   to be one of our RPCs.
+
+   Its content length is 'contentSize' bytes.
+-----------------------------------------------------------------------------*/
+    xmlrpc_env env;
+
+    if (trace)
+        fprintf(stderr,
+                "xmlrpc_server_abyss URI path handler processing RPC.\n");
+
+    xmlrpc_env_init(&env);
+
+    if (contentSize > 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)", contentSize);
+    else {
+        xmlrpc_mem_block *body;
+        /* Read XML data off the wire. */
+        getBody(&env, abyssSessionP, contentSize, trace, &body);
+        if (!env.fault_occurred) {
+            xmlrpc_mem_block * output;
+            /* Process the RPC. */
+            output = xmlrpc_registry_process_call(
+                &env, registryP, NULL, 
+                XMLRPC_MEMBLOCK_CONTENTS(char, body),
+                XMLRPC_MEMBLOCK_SIZE(char, body));
+            if (!env.fault_occurred) {
+                /* Send out the result. */
+                sendXmlData(&env, abyssSessionP, 
+                            XMLRPC_MEMBLOCK_CONTENTS(char, output),
+                            XMLRPC_MEMBLOCK_SIZE(char, output),
+                            wantChunk);
+                
+                XMLRPC_MEMBLOCK_FREE(char, output);
+            }
+            XMLRPC_MEMBLOCK_FREE(char, body);
+        }
+    }
+    if (env.fault_occurred) {
+        if (env.fault_code == XMLRPC_TIMEOUT_ERROR)
+            sendError(abyssSessionP, 408); /* 408 Request Timeout */
+        else
+            sendError(abyssSessionP, 500); /* 500 Internal Server Error */
+    }
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+/****************************************************************************
+    Abyss handlers (to be registered with and called by Abyss)
+****************************************************************************/
+
+static const char * trace_abyss;
+
+
+
+struct uriHandlerXmlrpc {
+/*----------------------------------------------------------------------------
+   This is the part of an Abyss HTTP request handler (aka URI handler)
+   that is specific to the Xmlrpc-c handler.
+-----------------------------------------------------------------------------*/
+    xmlrpc_registry * registryP;
+    const char *      uriPath;  /* malloc'ed */
+    bool              chunkResponse;
+        /* The handler should chunk its response whenever possible */
+};
+
+
+
+static void
+termUriHandler(void * const arg) {
+
+    struct uriHandlerXmlrpc * const uriHandlerXmlrpcP = arg;
+
+    xmlrpc_strfree(uriHandlerXmlrpcP->uriPath);
+    free(uriHandlerXmlrpcP);
+}
+
+
+
+static void
+handleXmlrpcReq(URIHandler2 * const this,
+                TSession *    const abyssSessionP,
+                abyss_bool *  const handledP) {
+/*----------------------------------------------------------------------------
+   Our job is to look at this HTTP request that the Abyss server is
+   trying to process and see if we can handle it.  If it's an XML-RPC
+   call for this XML-RPC server, we handle it.  If it's not, we refuse
+   it and Abyss can try some other handler.
+
+   Our return code is TRUE to mean we handled it; FALSE to mean we didn't.
+
+   Note that failing the request counts as handling it, and not handling
+   it does not mean we failed it.
+
+   This is an Abyss HTTP Request handler -- type URIHandler2.
+-----------------------------------------------------------------------------*/
+    struct uriHandlerXmlrpc * const uriHandlerXmlrpcP = this->userdata;
+
+    const TRequestInfo * requestInfoP;
+
+    if (trace_abyss)
+        traceHandlerCalled(abyssSessionP);
+
+    SessionGetRequestInfo(abyssSessionP, &requestInfoP);
+
+    /* Note that requestInfoP->uri is not the whole URI.  It is just
+       the "file name" part of it.
+    */
+    if (strcmp(requestInfoP->uri, uriHandlerXmlrpcP->uriPath) != 0)
+        /* It's for the path (e.g. "/RPC2") that we're supposed to
+           handle.
+        */
+        *handledP = FALSE;
+    else {
+        *handledP = TRUE;
+
+        /* We understand only the POST HTTP method.  For anything else, return
+           "405 Method Not Allowed". 
+        */
+        if (requestInfoP->method != m_post)
+            sendError(abyssSessionP, 405);
+        else {
+            unsigned int httpError;
+            storeCookies(abyssSessionP, &httpError);
+            if (httpError)
+                sendError(abyssSessionP, httpError);
+            else {
+                unsigned int httpError;
+                validateContentType(abyssSessionP, &httpError);
+                if (httpError)
+                    sendError(abyssSessionP, httpError);
+                else {
+                    unsigned int httpError;
+                    size_t contentSize;
+
+                    processContentLength(abyssSessionP, 
+                                         &contentSize, &httpError);
+                    if (httpError)
+                        sendError(abyssSessionP, httpError);
+                    else 
+                        processCall(abyssSessionP, contentSize,
+                                    uriHandlerXmlrpcP->registryP,
+                                    uriHandlerXmlrpcP->chunkResponse,
+                                    trace_abyss);
+                }
+            }
+        }
+    }
+    if (trace_abyss)
+        fprintf(stderr, "xmlrpc_server_abyss URI path handler returning.\n");
+}
+
+
+
+/*=========================================================================
+**  xmlrpc_server_abyss_default_handler
+**=========================================================================
+**  This handler returns a 404 Not Found for all requests. See the header
+**  for more documentation.
+*/
+
+static xmlrpc_bool 
+xmlrpc_server_abyss_default_handler(TSession * const sessionP) {
+
+    if (trace_abyss)
+        fprintf(stderr, "xmlrpc_server_abyss default handler called.\n");
+
+    sendError(sessionP, 404);
+
+    return TRUE;
+}
+
+
+
+static void 
+sigchld(int const signalClass ATTR_UNUSED) {
+/*----------------------------------------------------------------------------
+   This is a signal handler for a SIGCHLD signal (which informs us that
+   one of our child processes has terminated).
+
+   The only child processes we have are those that belong to the Abyss
+   server (and then only if the Abyss server was configured to use
+   forking as a threading mechanism), so we respond by passing the
+   signal on to the Abyss server.
+-----------------------------------------------------------------------------*/
+#ifndef WIN32
+    bool childrenLeft;
+    bool error;
+
+    assert(signalClass == SIGCHLD);
+
+    error = false;
+    childrenLeft = true;  /* initial assumption */
+    
+    /* Reap defunct children until there aren't any more. */
+    while (childrenLeft && !error) {
+        int status;
+        pid_t pid;
+
+        pid = waitpid((pid_t) -1, &status, WNOHANG);
+    
+        if (pid == 0)
+            childrenLeft = false;
+        else if (pid < 0) {
+            /* because of ptrace */
+            if (errno != EINTR)   
+                error = true;
+        } else
+            ServerHandleSigchld(pid);
+    }
+#endif /* WIN32 */
+}
+
+
+struct signalHandlers {
+    struct sigaction pipe;
+    struct sigaction chld;
+};
+
+
+
+static void
+setupSignalHandlers(struct signalHandlers * const oldHandlersP) {
+#ifndef WIN32
+    struct sigaction mysigaction;
+    
+    sigemptyset(&mysigaction.sa_mask);
+    mysigaction.sa_flags = 0;
+
+    /* This signal indicates connection closed in the middle */
+    mysigaction.sa_handler = SIG_IGN;
+    sigaction(SIGPIPE, &mysigaction, &oldHandlersP->pipe);
+    
+    /* This signal indicates a child process (request handler) has died */
+    mysigaction.sa_handler = sigchld;
+    sigaction(SIGCHLD, &mysigaction, &oldHandlersP->chld);
+#endif
+}    
+
+
+
+static void
+restoreSignalHandlers(struct signalHandlers const oldHandlers) {
+#ifndef WIN32
+
+    sigaction(SIGPIPE, &oldHandlers.pipe, NULL);
+    sigaction(SIGCHLD, &oldHandlers.chld, NULL);
+
+#endif
+}
+
+
+
+static void
+runServerDaemon(TServer *  const serverP,
+                runfirstFn const runfirst,
+                void *     const runfirstArg) {
+
+    struct signalHandlers oldHandlers;
+
+    setupSignalHandlers(&oldHandlers);
+
+    ServerUseSigchld(serverP);
+
+    ServerDaemonize(serverP);
+    
+    /* We run the user supplied runfirst after forking, but before accepting
+       connections (helpful when running with threads)
+    */
+    if (runfirst)
+        runfirst(runfirstArg);
+
+    ServerRun(serverP);
+
+    restoreSignalHandlers(oldHandlers);
+}
+
+
+
+static void
+setHandler(xmlrpc_env *      const envP,
+           TServer *         const srvP,
+           const char *      const uriPath,
+           xmlrpc_registry * const registryP,
+           bool              const chunkResponse) {
+    
+    struct uriHandlerXmlrpc * uriHandlerXmlrpcP;
+    URIHandler2 uriHandler;
+    abyss_bool success;
+
+    trace_abyss = getenv("XMLRPC_TRACE_ABYSS");
+                                 
+    MALLOCVAR_NOFAIL(uriHandlerXmlrpcP);
+
+    uriHandlerXmlrpcP->registryP     = registryP;
+    uriHandlerXmlrpcP->uriPath       = strdup(uriPath);
+    uriHandlerXmlrpcP->chunkResponse = chunkResponse;
+
+    uriHandler.handleReq2 = handleXmlrpcReq;
+    uriHandler.handleReq1 = NULL;
+    uriHandler.userdata   = uriHandlerXmlrpcP;
+    uriHandler.init       = NULL;
+    uriHandler.term       = &termUriHandler;
+
+    ServerAddHandler2(srvP, &uriHandler, &success);
+
+    if (!success)
+        xmlrpc_faultf(envP, "Abyss failed to register the Xmlrpc-c request "
+                      "handler.  ServerAddHandler2() failed.");
+
+    if (envP->fault_occurred)
+        free(uriHandlerXmlrpcP);
+}
+
+
+
+void
+xmlrpc_server_abyss_set_handler(xmlrpc_env *      const envP,
+                                TServer *         const srvP,
+                                const char *      const uriPath,
+                                xmlrpc_registry * const registryP) {
+
+    setHandler(envP, srvP, uriPath, registryP, false);
+}
+
+    
+
+static void
+setHandlers(TServer *         const srvP,
+            const char *      const uriPath,
+            xmlrpc_registry * const registryP,
+            bool              const chunkResponse) {
+
+    xmlrpc_env env;
+
+    xmlrpc_env_init(&env);
+
+    trace_abyss = getenv("XMLRPC_TRACE_ABYSS");
+                                 
+    setHandler(&env, srvP, uriPath, registryP, chunkResponse);
+    
+    if (env.fault_occurred)
+        abort();
+
+    ServerDefaultHandler(srvP, xmlrpc_server_abyss_default_handler);
+
+    xmlrpc_env_clean(&env);
+}
+
+
+
+void
+xmlrpc_server_abyss_set_handlers2(TServer *         const srvP,
+                                  const char *      const uriPath,
+                                  xmlrpc_registry * const registryP) {
+
+    setHandlers(srvP, uriPath, registryP, false);
+}
+
+
+
+void
+xmlrpc_server_abyss_set_handlers(TServer *         const srvP,
+                                 xmlrpc_registry * const registryP) {
+
+    setHandlers(srvP, "/RPC2", registryP, false);
+}
+
+
+
+static void
+oldHighLevelAbyssRun(xmlrpc_env *                      const envP ATTR_UNUSED,
+                     const xmlrpc_server_abyss_parms * const parmsP,
+                     unsigned int                      const parmSize) {
+/*----------------------------------------------------------------------------
+   This is the old deprecated interface, where the caller of the 
+   xmlrpc_server_abyss API supplies an Abyss configuration file and
+   we use it to daemonize (fork into the background, chdir, set uid, etc.)
+   and run the Abyss server.
+
+   The new preferred interface, implemented by normalLevelAbyssRun(),
+   instead lets Caller set up the process environment himself and pass
+   Abyss parameters in memory.  That's a more conventional and
+   flexible API.
+-----------------------------------------------------------------------------*/
+    TServer server;
+    runfirstFn runfirst;
+    void * runfirstArg;
+    
+    DateInit();
+    
+    ServerCreate(&server, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL);
+    
+    ConfReadServerFile(parmsP->config_file_name, &server);
+        
+    setHandlers(&server, "/RPC2", parmsP->registryP, false);
+        
+    ServerInit(&server);
+    
+    if (parmSize >= XMLRPC_APSIZE(runfirst_arg)) {
+        runfirst    = parmsP->runfirst;
+        runfirstArg = parmsP->runfirst_arg;
+    } else {
+        runfirst    = NULL;
+        runfirstArg = NULL;
+    }
+    runServerDaemon(&server, runfirst, runfirstArg);
+
+    ServerFree(&server);
+}
+
+
+
+static void
+setAdditionalServerParms(const xmlrpc_server_abyss_parms * const parmsP,
+                         unsigned int                      const parmSize,
+                         TServer *                         const serverP) {
+
+    /* The following ought to be parameters on ServerCreate(), but it
+       looks like plugging them straight into the TServer structure is
+       the only way to set them.  
+    */
+
+    if (parmSize >= XMLRPC_APSIZE(keepalive_timeout) &&
+        parmsP->keepalive_timeout > 0)
+        ServerSetKeepaliveTimeout(serverP, parmsP->keepalive_timeout);
+    if (parmSize >= XMLRPC_APSIZE(keepalive_max_conn) &&
+        parmsP->keepalive_max_conn > 0)
+        ServerSetKeepaliveMaxConn(serverP, parmsP->keepalive_max_conn);
+    if (parmSize >= XMLRPC_APSIZE(timeout) &&
+        parmsP->timeout > 0)
+        ServerSetTimeout(serverP, parmsP->timeout);
+    if (parmSize >= XMLRPC_APSIZE(dont_advertise))
+        ServerSetAdvertise(serverP, !parmsP->dont_advertise);
+}
+
+
+
+static void
+extractServerCreateParms(
+    xmlrpc_env *                      const envP,
+    const xmlrpc_server_abyss_parms * const parmsP,
+    unsigned int                      const parmSize,
+    abyss_bool *                      const socketBoundP,
+    unsigned int *                    const portNumberP,
+    TOsSocket *                       const socketFdP,
+    const char **                     const logFileNameP) {
+                   
+
+    if (parmSize >= XMLRPC_APSIZE(socket_bound))
+        *socketBoundP = parmsP->socket_bound;
+    else
+        *socketBoundP = FALSE;
+
+    if (*socketBoundP) {
+        if (parmSize < XMLRPC_APSIZE(socket_handle))
+            xmlrpc_faultf(envP, "socket_bound is true, but server parameter "
+                          "structure does not contain socket_handle (it's too "
+                          "short)");
+        else
+            *socketFdP = parmsP->socket_handle;
+    } else {
+        if (parmSize >= XMLRPC_APSIZE(port_number))
+            *portNumberP = parmsP->port_number;
+        else
+            *portNumberP = 8080;
+
+        if (*portNumberP > 0xffff)
+            xmlrpc_faultf(envP,
+                          "TCP port number %u exceeds the maximum possible "
+                          "TCP port number (65535)",
+                          *portNumberP);
+    }
+    if (!envP->fault_occurred) {
+        if (parmSize >= XMLRPC_APSIZE(log_file_name) &&
+            parmsP->log_file_name)
+            *logFileNameP = strdup(parmsP->log_file_name);
+        else
+            *logFileNameP = NULL;
+    }
+}
+
+
+
+static void
+createServerBoundSocket(xmlrpc_env * const envP,
+                        TOsSocket    const socketFd,
+                        const char * const logFileName,
+                        TServer *    const serverP,
+                        TSocket **   const socketPP) {
+
+    TSocket * socketP;
+    const char * error;
+    
+    SocketUnixCreateFd(socketFd, &socketP);
+    
+    if (!socketP)
+        xmlrpc_faultf(envP, "Unable to create Abyss socket out of "
+                      "file descriptor %d.", socketFd);
+    else {
+        ServerCreateSocket2(serverP, socketP, &error);
+        if (error) {
+            xmlrpc_faultf(envP, "Abyss failed to create server.  %s",
+                          error);
+            xmlrpc_strfree(error);
+        } else {
+            *socketPP = socketP;
+                    
+            ServerSetName(serverP, "XmlRpcServer");
+            
+            if (logFileName)
+                ServerSetLogFileName(serverP, logFileName);
+        }
+        if (envP->fault_occurred)
+                    SocketDestroy(socketP);
+    }
+}
+
+
+
+static void
+createServer(xmlrpc_env *                      const envP,
+             const xmlrpc_server_abyss_parms * const parmsP,
+             unsigned int                      const parmSize,
+             TServer *                         const serverP,
+             TSocket **                        const socketPP) {
+/*----------------------------------------------------------------------------
+   Create a bare server.  It will need further setup before it is ready
+   to use.
+-----------------------------------------------------------------------------*/
+    abyss_bool socketBound;
+    unsigned int portNumber;
+    TOsSocket socketFd;
+    const char * logFileName;
+
+    extractServerCreateParms(envP, parmsP, parmSize,
+                             &socketBound, &portNumber, &socketFd,
+                             &logFileName);
+
+    if (!envP->fault_occurred) {
+        if (socketBound)
+            createServerBoundSocket(envP, socketFd, logFileName,
+                                    serverP, socketPP);
+        else {
+            ServerCreate(serverP, "XmlRpcServer", portNumber, DEFAULT_DOCS, 
+                         logFileName);
+            
+            *socketPP = NULL;
+        }
+        if (logFileName)
+            xmlrpc_strfree(logFileName);
+    }
+}
+
+
+
+static bool
+chunkResponseParm(const xmlrpc_server_abyss_parms * const parmsP,
+                  unsigned int                      const parmSize) {
+
+    return
+        parmSize >= XMLRPC_APSIZE(chunk_response) &&
+        parmsP->chunk_response;
+}    
+
+
+
+static const char *
+uriPathParm(const xmlrpc_server_abyss_parms * const parmsP,
+            unsigned int                      const parmSize) {
+    
+    const char * uriPath;
+
+    if (parmSize >= XMLRPC_APSIZE(uri_path) && parmsP->uri_path)
+        uriPath = parmsP->uri_path;
+    else
+        uriPath = "/RPC2";
+
+    return uriPath;
+}
+
+
+
+static xmlrpc_server_shutdown_fn shutdownAbyss;
+
+static void
+shutdownAbyss(xmlrpc_env * const envP,
+              void *       const context,
+              const char * const comment ATTR_UNUSED) {
+/*----------------------------------------------------------------------------
+   Tell Abyss to wrap up whatever it's doing and shut down.
+
+   This is a server shutdown function to be registered in the method
+   registry, for use by the 'system.shutdown' system method.
+
+   After we return, Abyss will finish up the system.shutdown and any
+   other connections that are in progress, then the call to
+   ServerRun() etc. will return.  But Abyss may be stuck waiting for
+   something, such as the next HTTP connection.  In that case, until it
+   gets what it's waiting for, it won't even know it's supposed t shut
+   down.  In particular, a caller of system.shutdown may have to execute
+   one more RPC in order for the shutdown to happen.
+-----------------------------------------------------------------------------*/
+    TServer * const serverP = context;
+
+    xmlrpc_env_init(envP);
+    
+    ServerTerminate(serverP);
+}
+
+
+
+static void
+normalLevelAbyssRun(xmlrpc_env *                      const envP,
+                    const xmlrpc_server_abyss_parms * const parmsP,
+                    unsigned int                      const parmSize) {
+    
+    TServer server;
+    TSocket * socketP;
+
+    DateInit();
+
+    createServer(envP, parmsP, parmSize, &server, &socketP);
+
+    if (!envP->fault_occurred) {
+        struct signalHandlers oldHandlers;
+
+        setAdditionalServerParms(parmsP, parmSize, &server);
+
+        setHandlers(&server, uriPathParm(parmsP, parmSize), parmsP->registryP,
+                    chunkResponseParm(parmsP, parmSize));
+
+        ServerInit(&server);
+        
+        setupSignalHandlers(&oldHandlers);
+
+        ServerUseSigchld(&server);
+        
+        if (0)
+            /* Too much of a security risk.  In 1.07, there is a server
+               parameter to enable this.
+            */
+            xmlrpc_registry_set_shutdown(parmsP->registryP,
+                                         &shutdownAbyss, &server);
+        
+        ServerRun(&server);
+
+        restoreSignalHandlers(oldHandlers);
+
+        ServerFree(&server);
+
+        if (socketP)
+            SocketDestroy(socketP);
+    }
+}
+
+
+
+void
+xmlrpc_server_abyss(xmlrpc_env *                      const envP,
+                    const xmlrpc_server_abyss_parms * const parmsP,
+                    unsigned int                      const parmSize) {
+    XMLRPC_ASSERT_ENV_OK(envP);
+
+    if (parmSize < XMLRPC_APSIZE(registryP))
+        xmlrpc_faultf(envP,
+                      "You must specify members at least up through "
+                      "'registryP' in the server parameters argument.  "
+                      "That would mean the parameter size would be >= %lu "
+                      "but you specified a size of %u",
+                      XMLRPC_APSIZE(registryP), parmSize);
+    else {
+        if (parmsP->config_file_name)
+            oldHighLevelAbyssRun(envP, parmsP, parmSize);
+        else
+            normalLevelAbyssRun(envP, parmsP, parmSize);
+    }
+}
+
+
+
+/*=========================================================================
+  XML-RPC Server Method Registry
+
+  This is an old deprecated form of the server facilities that uses
+  global variables.
+=========================================================================*/
+
+/* These global variables must be treated as read-only after the
+   server has started.
+*/
+
+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.
+    */
+
+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);
+
+    setHandlers(&globalSrv, "/RPC2", builtin_registryP, false);
+}
+
+
+
+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);    
+}
+
+
+
+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);
+}
+
+
+
+void 
+xmlrpc_server_abyss_run_first(runfirstFn const runfirst,
+                              void *     const runfirstArg) {
+    
+    runServerDaemon(&globalSrv, runfirst, runfirstArg);
+}
+
+
+
+void 
+xmlrpc_server_abyss_run(void) {
+    runServerDaemon(&globalSrv, NULL, NULL);
+}
+
+
+
+/*
+** 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. 
+*/