abyss_xmlrpc_server.c

00001 /* Copyright information is at the end of the file */
00002 
00003 #include <xmlrpc-c/config.h>
00004 
00005 #include <assert.h>
00006 #include <stdio.h>
00007 #include <stdlib.h>
00008 #include <string.h>
00009 #include <errno.h>
00010 #include <time.h>
00011 #include <fcntl.h>
00012 #ifdef _WIN32
00013 #  include <io.h>
00014 #else
00015 #  include <signal.h>
00016 #  include <sys/wait.h>
00017 #  include <grp.h>
00018 #endif
00019 
00020 #include "abyss_mallocvar.h"
00021 #include <xmlrpc-c/abyss.h>
00022 
00023 #include <xmlrpc-c/base.h>
00024 #include <xmlrpc-c/server.h>
00025 #include "abyss_xmlrpc_int.h"
00026 #include <xmlrpc-c/server_abyss.h>
00027 
00028 
00029 /*=========================================================================
00030 **  die_if_fault_occurred
00031 **=========================================================================
00032 **  If certain kinds of out-of-memory errors occur during server setup,
00033 **  we want to quit and print an error.
00034 */
00035 
00036 static void die_if_fault_occurred(xmlrpc_env *env) {
00037     if (env->fault_occurred) {
00038         fprintf(stderr, "Unexpected XML-RPC fault: %s (%d)\n",
00039                 env->fault_string, env->fault_code);
00040         exit(1);
00041     }
00042 }
00043 
00044 
00045 
00046 static void
00047 addAuthCookie(xmlrpc_env * const envP,
00048               TSession *   const abyssSessionP,
00049               const char * const authCookie) {
00050 
00051     const char * cookieResponse;
00052     
00053     xmlrpc_asprintf(&cookieResponse, "auth=%s", authCookie);
00054     
00055     if (cookieResponse == xmlrpc_strsol)
00056         xmlrpc_faultf(envP, "Insufficient memory to generate cookie "
00057                       "response header.");
00058     else {
00059         ResponseAddField(abyssSessionP, "Set-Cookie", cookieResponse);
00060     
00061         xmlrpc_strfree(cookieResponse);
00062     }
00063 }   
00064     
00065 
00066 
00067 static void 
00068 sendXmlData(xmlrpc_env * const envP,
00069             TSession *   const abyssSessionP, 
00070             const char * const body, 
00071             size_t       const len,
00072             bool         const chunked) {
00073 /*----------------------------------------------------------------------------
00074    Generate an HTTP response containing body 'body' of length 'len'
00075    characters.
00076 
00077    This is meant to run in the context of an Abyss URI handler for
00078    Abyss session 'abyssSessionP'.
00079 -----------------------------------------------------------------------------*/
00080     const char * http_cookie = NULL;
00081         /* This used to set http_cookie to getenv("HTTP_COOKIE"), but
00082            that doesn't make any sense -- environment variables are not
00083            appropriate for this.  So for now, cookie code is disabled.
00084            - Bryan 2004.10.03.
00085         */
00086 
00087     /* Various bugs before Xmlrpc-c 1.05 caused the response to be not
00088        chunked in the most basic case, but chunked if the client explicitly
00089        requested keepalive.  I think it's better not to chunk, because
00090        it's simpler, so I removed this in 1.05.  I don't know what the
00091        purpose of chunking would be, and an original comment suggests
00092        the author wasn't sure chunking was a good idea.
00093 
00094        In 1.06 we added the user option to chunk.
00095     */
00096     if (chunked)
00097         ResponseChunked(abyssSessionP);
00098 
00099     ResponseStatus(abyssSessionP, 200);
00100 
00101     if (http_cookie)
00102         /* There's an auth cookie, so pass it back in the response. */
00103         addAuthCookie(envP, abyssSessionP, http_cookie);
00104 
00105     if ((size_t)(uint32_t)len != len)
00106         xmlrpc_faultf(envP, "XML-RPC method generated a response too "
00107                       "large for Abyss to send");
00108     else {
00109         uint32_t const abyssLen = (uint32_t)len;
00110 
00111         ResponseContentType(abyssSessionP, "text/xml; charset=\"utf-8\"");
00112         ResponseContentLength(abyssSessionP, abyssLen);
00113         
00114         ResponseWriteStart(abyssSessionP);
00115         ResponseWriteBody(abyssSessionP, body, abyssLen);
00116         ResponseWriteEnd(abyssSessionP);
00117     }
00118 }
00119 
00120 
00121 
00122 static void
00123 sendError(TSession *   const abyssSessionP, 
00124           unsigned int const status) {
00125 /*----------------------------------------------------------------------------
00126   Send an error response back to the client.
00127    
00128 -----------------------------------------------------------------------------*/
00129     ResponseStatus(abyssSessionP, (uint16_t) status);
00130     ResponseError(abyssSessionP);
00131 }
00132 
00133 
00134 
00135 static void
00136 traceChunkRead(TSession * const abyssSessionP) {
00137 
00138     fprintf(stderr, "XML-RPC handler got a chunk of %u bytes\n",
00139             (unsigned int)SessionReadDataAvail(abyssSessionP));
00140 }
00141 
00142 
00143 
00144 static void
00145 refillBufferFromConnection(xmlrpc_env * const envP,
00146                            TSession *   const abyssSessionP,
00147                            const char * const trace) {
00148 /*----------------------------------------------------------------------------
00149    Get the next chunk of data from the connection into the buffer.
00150 -----------------------------------------------------------------------------*/
00151     abyss_bool succeeded;
00152 
00153     succeeded = SessionRefillBuffer(abyssSessionP);
00154 
00155     if (!succeeded)
00156         xmlrpc_env_set_fault_formatted(
00157             envP, XMLRPC_TIMEOUT_ERROR, "Timed out waiting for "
00158             "client to send its POST data");
00159     else {
00160         if (trace)
00161             traceChunkRead(abyssSessionP);
00162     }
00163 }
00164 
00165 
00166 
00167 static void
00168 getBody(xmlrpc_env *        const envP,
00169         TSession *          const abyssSessionP,
00170         size_t              const contentSize,
00171         const char *        const trace,
00172         xmlrpc_mem_block ** const bodyP) {
00173 /*----------------------------------------------------------------------------
00174    Get the entire body, which is of size 'contentSize' bytes, from the
00175    Abyss session and return it as the new memblock *bodyP.
00176 
00177    The first chunk of the body may already be in Abyss's buffer.  We
00178    retrieve that before reading more.
00179 -----------------------------------------------------------------------------*/
00180     xmlrpc_mem_block * body;
00181 
00182     if (trace)
00183         fprintf(stderr, "XML-RPC handler processing body.  "
00184                 "Content Size = %u bytes\n", (unsigned)contentSize);
00185 
00186     body = xmlrpc_mem_block_new(envP, 0);
00187     if (!envP->fault_occurred) {
00188         size_t bytesRead;
00189         const char * chunkPtr;
00190         size_t chunkLen;
00191 
00192         bytesRead = 0;
00193 
00194         while (!envP->fault_occurred && bytesRead < contentSize) {
00195             SessionGetReadData(abyssSessionP, contentSize - bytesRead, 
00196                                &chunkPtr, &chunkLen);
00197             bytesRead += chunkLen;
00198 
00199             assert(bytesRead <= contentSize);
00200 
00201             XMLRPC_MEMBLOCK_APPEND(char, envP, body, chunkPtr, chunkLen);
00202             if (bytesRead < contentSize)
00203                 refillBufferFromConnection(envP, abyssSessionP, trace);
00204         }
00205         if (envP->fault_occurred)
00206             xmlrpc_mem_block_free(body);
00207         else
00208             *bodyP = body;
00209     }
00210 }
00211 
00212 
00213 
00214 static void
00215 storeCookies(TSession *     const httpRequestP,
00216              unsigned int * const httpErrorP) {
00217 /*----------------------------------------------------------------------------
00218    Get the cookie settings from the HTTP headers and remember them for
00219    use in responses.
00220 -----------------------------------------------------------------------------*/
00221     const char * const cookie = RequestHeaderValue(httpRequestP, "cookie");
00222     if (cookie) {
00223         /* 
00224            Setting the value in an environment variable doesn't make
00225            any sense.  So for now, cookie code is disabled.
00226            -Bryan 04.10.03.
00227 
00228         setenv("HTTP_COOKIE", cookie, 1);
00229         */
00230     }
00231     /* TODO: parse HTTP_COOKIE to find auth pair, if there is one */
00232 
00233     *httpErrorP = 0;
00234 }
00235 
00236 
00237 
00238 
00239 static void
00240 validateContentType(TSession *     const httpRequestP,
00241                     unsigned int * const httpErrorP) {
00242 /*----------------------------------------------------------------------------
00243    If the client didn't specify a content-type of "text/xml", return      
00244    "400 Bad Request".  We can't allow the client to default this header,
00245    because some firewall software may rely on all XML-RPC requests
00246    using the POST method and a content-type of "text/xml". 
00247 -----------------------------------------------------------------------------*/
00248     const char * const content_type =
00249         RequestHeaderValue(httpRequestP, "content-type");
00250 
00251     if (content_type == NULL)
00252         *httpErrorP = 400;
00253     else {
00254         const char * const sempos = strchr(content_type, ';');
00255         unsigned int baselen;
00256             /* Length of the base portion of the content type, e.g.
00257                "text/xml" int "text/xml;charset=utf-8"
00258             */
00259 
00260         if (sempos)
00261             baselen = sempos - content_type;
00262         else
00263             baselen = strlen(content_type);
00264 
00265         if (!xmlrpc_strneq(content_type, "text/xml", baselen))
00266             *httpErrorP = 400;
00267         else
00268             *httpErrorP = 0;
00269     }
00270 }
00271 
00272 
00273 
00274 static void
00275 processContentLength(TSession *     const httpRequestP,
00276                      size_t *       const inputLenP,
00277                      unsigned int * const httpErrorP) {
00278 /*----------------------------------------------------------------------------
00279   Make sure the content length is present and non-zero.  This is
00280   technically required by XML-RPC, but we only enforce it because we
00281   don't want to figure out how to safely handle HTTP < 1.1 requests
00282   without it.  If the length is missing, return "411 Length Required". 
00283 -----------------------------------------------------------------------------*/
00284     const char * const content_length = 
00285         RequestHeaderValue(httpRequestP, "content-length");
00286 
00287     if (content_length == NULL)
00288         *httpErrorP = 411;
00289     else {
00290         if (content_length[0] == '\0')
00291             *httpErrorP = 400;
00292         else {
00293             unsigned long contentLengthValue;
00294             char * tail;
00295         
00296             contentLengthValue = strtoul(content_length, &tail, 10);
00297         
00298             if (*tail != '\0')
00299                 /* There's non-numeric crap in the length */
00300                 *httpErrorP = 400;
00301             else if (contentLengthValue < 1)
00302                 *httpErrorP = 400;
00303             else if ((unsigned long)(size_t)contentLengthValue 
00304                      != contentLengthValue)
00305                 *httpErrorP = 400;
00306             else {
00307                 *httpErrorP = 0;
00308                 *inputLenP = (size_t)contentLengthValue;
00309             }
00310         }
00311     }
00312 }
00313 
00314 
00315 
00316 static void
00317 traceHandlerCalled(TSession * const abyssSessionP) {
00318     
00319     const char * methodDesc;
00320     const TRequestInfo * requestInfoP;
00321 
00322     fprintf(stderr, "xmlrpc_server_abyss URI path handler called.\n");
00323 
00324     SessionGetRequestInfo(abyssSessionP, &requestInfoP);
00325 
00326     fprintf(stderr, "URI = '%s'\n", requestInfoP->uri);
00327 
00328     switch (requestInfoP->method) {
00329     case m_unknown: methodDesc = "unknown";   break;
00330     case m_get:     methodDesc = "get";       break;
00331     case m_put:     methodDesc = "put";       break;
00332     case m_head:    methodDesc = "head";      break;
00333     case m_post:    methodDesc = "post";      break;
00334     case m_delete:  methodDesc = "delete";    break;
00335     case m_trace:   methodDesc = "trace";     break;
00336     case m_options: methodDesc = "m_options"; break;
00337     default:        methodDesc = "?";
00338     }
00339     fprintf(stderr, "HTTP method = '%s'\n", methodDesc);
00340 
00341     if (requestInfoP->query)
00342         fprintf(stderr, "query (component of URL)='%s'\n",
00343                 requestInfoP->query);
00344     else
00345         fprintf(stderr, "URL has no query component\n");
00346 }
00347 
00348 
00349 
00350 static void
00351 processCall(TSession *        const abyssSessionP,
00352             size_t            const contentSize,
00353             xmlrpc_registry * const registryP,
00354             bool              const wantChunk,
00355             const char *      const trace) {
00356 /*----------------------------------------------------------------------------
00357    Handle an RPC request.  This is an HTTP request that has the proper form
00358    to be one of our RPCs.
00359 
00360    Its content length is 'contentSize' bytes.
00361 -----------------------------------------------------------------------------*/
00362     xmlrpc_env env;
00363 
00364     if (trace)
00365         fprintf(stderr,
00366                 "xmlrpc_server_abyss URI path handler processing RPC.\n");
00367 
00368     xmlrpc_env_init(&env);
00369 
00370     if (contentSize > xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
00371         xmlrpc_env_set_fault_formatted(
00372             &env, XMLRPC_LIMIT_EXCEEDED_ERROR,
00373             "XML-RPC request too large (%ld bytes)", (long)contentSize);
00374     else {
00375         xmlrpc_mem_block *body=0;
00376         /* Read XML data off the wire. */
00377         getBody(&env, abyssSessionP, contentSize, trace, &body);
00378         if (!env.fault_occurred) {
00379             xmlrpc_mem_block * output;
00380             /* Process the RPC. */
00381             output = xmlrpc_registry_process_call(
00382                 &env, registryP, NULL, 
00383                 XMLRPC_MEMBLOCK_CONTENTS(char, body),
00384                 XMLRPC_MEMBLOCK_SIZE(char, body));
00385             if (!env.fault_occurred) {
00386                 /* Send out the result. */
00387                 sendXmlData(&env, abyssSessionP, 
00388                             XMLRPC_MEMBLOCK_CONTENTS(char, output),
00389                             XMLRPC_MEMBLOCK_SIZE(char, output),
00390                             wantChunk);
00391                 
00392                 XMLRPC_MEMBLOCK_FREE(char, output);
00393             }
00394             XMLRPC_MEMBLOCK_FREE(char, body);
00395         }
00396     }
00397     if (env.fault_occurred) {
00398         if (env.fault_code == XMLRPC_TIMEOUT_ERROR)
00399             sendError(abyssSessionP, 408); /* 408 Request Timeout */
00400         else
00401             sendError(abyssSessionP, 500); /* 500 Internal Server Error */
00402     }
00403 
00404     xmlrpc_env_clean(&env);
00405 }
00406 
00407 
00408 
00409 /****************************************************************************
00410     Abyss handlers (to be registered with and called by Abyss)
00411 ****************************************************************************/
00412 
00413 static const char * trace_abyss;
00414 
00415 
00416 
00417 struct uriHandlerXmlrpc {
00418 /*----------------------------------------------------------------------------
00419    This is the part of an Abyss HTTP request handler (aka URI handler)
00420    that is specific to the Xmlrpc-c handler.
00421 -----------------------------------------------------------------------------*/
00422     xmlrpc_registry * registryP;
00423     const char *      uriPath;  /* malloc'ed */
00424     bool              chunkResponse;
00425         /* The handler should chunk its response whenever possible */
00426 };
00427 
00428 
00429 
00430 static void
00431 termUriHandler(void * const arg) {
00432 
00433     struct uriHandlerXmlrpc * const uriHandlerXmlrpcP = arg;
00434 
00435     xmlrpc_strfree(uriHandlerXmlrpcP->uriPath);
00436     free(uriHandlerXmlrpcP);
00437 }
00438 
00439 
00440 
00441 static void
00442 handleXmlrpcReq(URIHandler2 * const this,
00443                 TSession *    const abyssSessionP,
00444                 abyss_bool *  const handledP) {
00445 /*----------------------------------------------------------------------------
00446    Our job is to look at this HTTP request that the Abyss server is
00447    trying to process and see if we can handle it.  If it's an XML-RPC
00448    call for this XML-RPC server, we handle it.  If it's not, we refuse
00449    it and Abyss can try some other handler.
00450 
00451    Our return code is TRUE to mean we handled it; FALSE to mean we didn't.
00452 
00453    Note that failing the request counts as handling it, and not handling
00454    it does not mean we failed it.
00455 
00456    This is an Abyss HTTP Request handler -- type URIHandler2.
00457 -----------------------------------------------------------------------------*/
00458     struct uriHandlerXmlrpc * const uriHandlerXmlrpcP = this->userdata;
00459 
00460     const TRequestInfo * requestInfoP;
00461 
00462     if (trace_abyss)
00463         traceHandlerCalled(abyssSessionP);
00464 
00465     SessionGetRequestInfo(abyssSessionP, &requestInfoP);
00466 
00467     /* Note that requestInfoP->uri is not the whole URI.  It is just
00468        the "file name" part of it.
00469     */
00470     if (strcmp(requestInfoP->uri, uriHandlerXmlrpcP->uriPath) != 0)
00471         /* It's for the path (e.g. "/RPC2") that we're supposed to
00472            handle.
00473         */
00474         *handledP = FALSE;
00475     else {
00476         *handledP = TRUE;
00477 
00478         /* We understand only the POST HTTP method.  For anything else, return
00479            "405 Method Not Allowed". 
00480         */
00481         if (requestInfoP->method != m_post)
00482             sendError(abyssSessionP, 405);
00483         else {
00484             unsigned int httpError;
00485             storeCookies(abyssSessionP, &httpError);
00486             if (httpError)
00487                 sendError(abyssSessionP, httpError);
00488             else {
00489                 unsigned int httpError;
00490                 validateContentType(abyssSessionP, &httpError);
00491                 if (httpError)
00492                     sendError(abyssSessionP, httpError);
00493                 else {
00494                     unsigned int httpError;
00495                     size_t contentSize;
00496 
00497                     processContentLength(abyssSessionP, 
00498                                          &contentSize, &httpError);
00499                     if (httpError)
00500                         sendError(abyssSessionP, httpError);
00501                     else 
00502                         processCall(abyssSessionP, contentSize,
00503                                     uriHandlerXmlrpcP->registryP,
00504                                     uriHandlerXmlrpcP->chunkResponse,
00505                                     trace_abyss);
00506                 }
00507             }
00508         }
00509     }
00510     if (trace_abyss)
00511         fprintf(stderr, "xmlrpc_server_abyss URI path handler returning.\n");
00512 }
00513 
00514 
00515 
00516 /*=========================================================================
00517 **  xmlrpc_server_abyss_default_handler
00518 **=========================================================================
00519 **  This handler returns a 404 Not Found for all requests. See the header
00520 **  for more documentation.
00521 */
00522 
00523 static xmlrpc_bool 
00524 xmlrpc_server_abyss_default_handler(TSession * const sessionP) {
00525 
00526     if (trace_abyss)
00527         fprintf(stderr, "xmlrpc_server_abyss default handler called.\n");
00528 
00529     sendError(sessionP, 404);
00530 
00531     return TRUE;
00532 }
00533 
00534 
00535 
00536 static void 
00537 sigchld(int const signalClass ATTR_UNUSED) {
00538 /*----------------------------------------------------------------------------
00539    This is a signal handler for a SIGCHLD signal (which informs us that
00540    one of our child processes has terminated).
00541 
00542    The only child processes we have are those that belong to the Abyss
00543    server (and then only if the Abyss server was configured to use
00544    forking as a threading mechanism), so we respond by passing the
00545    signal on to the Abyss server.
00546 -----------------------------------------------------------------------------*/
00547 #ifndef WIN32
00548     bool childrenLeft;
00549     bool error;
00550 
00551     assert(signalClass == SIGCHLD);
00552 
00553     error = false;
00554     childrenLeft = true;  /* initial assumption */
00555     
00556     /* Reap defunct children until there aren't any more. */
00557     while (childrenLeft && !error) {
00558         int status;
00559         pid_t pid;
00560 
00561         pid = waitpid((pid_t) -1, &status, WNOHANG);
00562     
00563         if (pid == 0)
00564             childrenLeft = false;
00565         else if (pid < 0) {
00566             /* because of ptrace */
00567             if (errno != EINTR)   
00568                 error = true;
00569         } else
00570             ServerHandleSigchld(pid);
00571     }
00572 #endif /* WIN32 */
00573 }
00574 
00575 
00576 struct signalHandlers {
00577     struct sigaction pipe;
00578     struct sigaction chld;
00579 };
00580 
00581 
00582 
00583 static void
00584 setupSignalHandlers(struct signalHandlers * const oldHandlersP) {
00585 #ifndef WIN32
00586     struct sigaction mysigaction;
00587     
00588     sigemptyset(&mysigaction.sa_mask);
00589     mysigaction.sa_flags = 0;
00590 
00591     /* This signal indicates connection closed in the middle */
00592     mysigaction.sa_handler = SIG_IGN;
00593     sigaction(SIGPIPE, &mysigaction, &oldHandlersP->pipe);
00594     
00595     /* This signal indicates a child process (request handler) has died */
00596     mysigaction.sa_handler = sigchld;
00597     sigaction(SIGCHLD, &mysigaction, &oldHandlersP->chld);
00598 #endif
00599 }    
00600 
00601 
00602 
00603 static void
00604 restoreSignalHandlers(struct signalHandlers const oldHandlers) {
00605 #ifndef WIN32
00606 
00607     sigaction(SIGPIPE, &oldHandlers.pipe, NULL);
00608     sigaction(SIGCHLD, &oldHandlers.chld, NULL);
00609 
00610 #endif
00611 }
00612 
00613 
00614 
00615 static void
00616 runServerDaemon(TServer *  const serverP,
00617                 runfirstFn const runfirst,
00618                 void *     const runfirstArg) {
00619 
00620     struct signalHandlers oldHandlers;
00621 
00622     setupSignalHandlers(&oldHandlers);
00623 
00624     ServerUseSigchld(serverP);
00625 
00626     ServerDaemonize(serverP);
00627     
00628     /* We run the user supplied runfirst after forking, but before accepting
00629        connections (helpful when running with threads)
00630     */
00631     if (runfirst)
00632         runfirst(runfirstArg);
00633 
00634     ServerRun(serverP);
00635 
00636     restoreSignalHandlers(oldHandlers);
00637 }
00638 
00639 
00640 
00641 static void
00642 setHandler(xmlrpc_env *      const envP,
00643            TServer *         const srvP,
00644            const char *      const uriPath,
00645            xmlrpc_registry * const registryP,
00646            bool              const chunkResponse) {
00647     
00648     struct uriHandlerXmlrpc * uriHandlerXmlrpcP;
00649     URIHandler2 uriHandler;
00650     abyss_bool success;
00651 
00652     trace_abyss = getenv("XMLRPC_TRACE_ABYSS");
00653                                  
00654     MALLOCVAR_NOFAIL(uriHandlerXmlrpcP);
00655 
00656     uriHandlerXmlrpcP->registryP     = registryP;
00657     uriHandlerXmlrpcP->uriPath       = strdup(uriPath);
00658     uriHandlerXmlrpcP->chunkResponse = chunkResponse;
00659 
00660     uriHandler.handleReq2 = handleXmlrpcReq;
00661     uriHandler.handleReq1 = NULL;
00662     uriHandler.userdata   = uriHandlerXmlrpcP;
00663     uriHandler.init       = NULL;
00664     uriHandler.term       = &termUriHandler;
00665 
00666     ServerAddHandler2(srvP, &uriHandler, &success);
00667 
00668     if (!success)
00669         xmlrpc_faultf(envP, "Abyss failed to register the Xmlrpc-c request "
00670                       "handler.  ServerAddHandler2() failed.");
00671 
00672     if (envP->fault_occurred)
00673         free(uriHandlerXmlrpcP);
00674 }
00675 
00676 
00677 
00678 void
00679 xmlrpc_server_abyss_set_handler(xmlrpc_env *      const envP,
00680                                 TServer *         const srvP,
00681                                 const char *      const uriPath,
00682                                 xmlrpc_registry * const registryP) {
00683 
00684     setHandler(envP, srvP, uriPath, registryP, false);
00685 }
00686 
00687     
00688 
00689 static void
00690 setHandlers(TServer *         const srvP,
00691             const char *      const uriPath,
00692             xmlrpc_registry * const registryP,
00693             bool              const chunkResponse) {
00694 
00695     xmlrpc_env env;
00696 
00697     xmlrpc_env_init(&env);
00698 
00699     trace_abyss = getenv("XMLRPC_TRACE_ABYSS");
00700                                  
00701     setHandler(&env, srvP, uriPath, registryP, chunkResponse);
00702     
00703     if (env.fault_occurred)
00704         abort();
00705 
00706     ServerDefaultHandler(srvP, xmlrpc_server_abyss_default_handler);
00707 
00708     xmlrpc_env_clean(&env);
00709 }
00710 
00711 
00712 
00713 void
00714 xmlrpc_server_abyss_set_handlers2(TServer *         const srvP,
00715                                   const char *      const uriPath,
00716                                   xmlrpc_registry * const registryP) {
00717 
00718     setHandlers(srvP, uriPath, registryP, false);
00719 }
00720 
00721 
00722 
00723 void
00724 xmlrpc_server_abyss_set_handlers(TServer *         const srvP,
00725                                  xmlrpc_registry * const registryP) {
00726 
00727     setHandlers(srvP, "/RPC2", registryP, false);
00728 }
00729 
00730 
00731 
00732 static void
00733 oldHighLevelAbyssRun(xmlrpc_env *                      const envP ATTR_UNUSED,
00734                      const xmlrpc_server_abyss_parms * const parmsP,
00735                      unsigned int                      const parmSize) {
00736 /*----------------------------------------------------------------------------
00737    This is the old deprecated interface, where the caller of the 
00738    xmlrpc_server_abyss API supplies an Abyss configuration file and
00739    we use it to daemonize (fork into the background, chdir, set uid, etc.)
00740    and run the Abyss server.
00741 
00742    The new preferred interface, implemented by normalLevelAbyssRun(),
00743    instead lets Caller set up the process environment himself and pass
00744    Abyss parameters in memory.  That's a more conventional and
00745    flexible API.
00746 -----------------------------------------------------------------------------*/
00747     TServer server;
00748     runfirstFn runfirst;
00749     void * runfirstArg;
00750     
00751     DateInit();
00752     
00753     ServerCreate(&server, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL);
00754     
00755     ConfReadServerFile(parmsP->config_file_name, &server);
00756         
00757     setHandlers(&server, "/RPC2", parmsP->registryP, false);
00758         
00759     ServerInit(&server);
00760     
00761     if (parmSize >= XMLRPC_APSIZE(runfirst_arg)) {
00762         runfirst    = parmsP->runfirst;
00763         runfirstArg = parmsP->runfirst_arg;
00764     } else {
00765         runfirst    = NULL;
00766         runfirstArg = NULL;
00767     }
00768     runServerDaemon(&server, runfirst, runfirstArg);
00769 
00770     ServerFree(&server);
00771 }
00772 
00773 
00774 
00775 static void
00776 setAdditionalServerParms(const xmlrpc_server_abyss_parms * const parmsP,
00777                          unsigned int                      const parmSize,
00778                          TServer *                         const serverP) {
00779 
00780     /* The following ought to be parameters on ServerCreate(), but it
00781        looks like plugging them straight into the TServer structure is
00782        the only way to set them.  
00783     */
00784 
00785     if (parmSize >= XMLRPC_APSIZE(keepalive_timeout) &&
00786         parmsP->keepalive_timeout > 0)
00787         ServerSetKeepaliveTimeout(serverP, parmsP->keepalive_timeout);
00788     if (parmSize >= XMLRPC_APSIZE(keepalive_max_conn) &&
00789         parmsP->keepalive_max_conn > 0)
00790         ServerSetKeepaliveMaxConn(serverP, parmsP->keepalive_max_conn);
00791     if (parmSize >= XMLRPC_APSIZE(timeout) &&
00792         parmsP->timeout > 0)
00793         ServerSetTimeout(serverP, parmsP->timeout);
00794     if (parmSize >= XMLRPC_APSIZE(dont_advertise))
00795         ServerSetAdvertise(serverP, !parmsP->dont_advertise);
00796 }
00797 
00798 
00799 
00800 static void
00801 extractServerCreateParms(
00802     xmlrpc_env *                      const envP,
00803     const xmlrpc_server_abyss_parms * const parmsP,
00804     unsigned int                      const parmSize,
00805     abyss_bool *                      const socketBoundP,
00806     unsigned int *                    const portNumberP,
00807     TOsSocket *                       const socketFdP,
00808     const char **                     const logFileNameP) {
00809                    
00810 
00811     if (parmSize >= XMLRPC_APSIZE(socket_bound))
00812         *socketBoundP = parmsP->socket_bound;
00813     else
00814         *socketBoundP = FALSE;
00815 
00816     if (*socketBoundP) {
00817         if (parmSize < XMLRPC_APSIZE(socket_handle))
00818             xmlrpc_faultf(envP, "socket_bound is true, but server parameter "
00819                           "structure does not contain socket_handle (it's too "
00820                           "short)");
00821         else
00822             *socketFdP = parmsP->socket_handle;
00823     } else {
00824         if (parmSize >= XMLRPC_APSIZE(port_number))
00825             *portNumberP = parmsP->port_number;
00826         else
00827             *portNumberP = 8080;
00828 
00829         if (*portNumberP > 0xffff)
00830             xmlrpc_faultf(envP,
00831                           "TCP port number %u exceeds the maximum possible "
00832                           "TCP port number (65535)",
00833                           *portNumberP);
00834     }
00835     if (!envP->fault_occurred) {
00836         if (parmSize >= XMLRPC_APSIZE(log_file_name) &&
00837             parmsP->log_file_name)
00838             *logFileNameP = strdup(parmsP->log_file_name);
00839         else
00840             *logFileNameP = NULL;
00841     }
00842 }
00843 
00844 
00845 
00846 static void
00847 createServerBoundSocket(xmlrpc_env * const envP,
00848                         TOsSocket    const socketFd,
00849                         const char * const logFileName,
00850                         TServer *    const serverP,
00851                         TSocket **   const socketPP) {
00852 
00853     TSocket * socketP;
00854     const char * error;
00855     
00856     SocketUnixCreateFd(socketFd, &socketP);
00857     
00858     if (!socketP)
00859         xmlrpc_faultf(envP, "Unable to create Abyss socket out of "
00860                       "file descriptor %d.", socketFd);
00861     else {
00862         ServerCreateSocket2(serverP, socketP, &error);
00863         if (error) {
00864             xmlrpc_faultf(envP, "Abyss failed to create server.  %s",
00865                           error);
00866             xmlrpc_strfree(error);
00867         } else {
00868             *socketPP = socketP;
00869                     
00870             ServerSetName(serverP, "XmlRpcServer");
00871             
00872             if (logFileName)
00873                 ServerSetLogFileName(serverP, logFileName);
00874         }
00875         if (envP->fault_occurred)
00876                     SocketDestroy(socketP);
00877     }
00878 }
00879 
00880 
00881 
00882 static void
00883 createServer(xmlrpc_env *                      const envP,
00884              const xmlrpc_server_abyss_parms * const parmsP,
00885              unsigned int                      const parmSize,
00886              TServer *                         const serverP,
00887              TSocket **                        const socketPP) {
00888 /*----------------------------------------------------------------------------
00889    Create a bare server.  It will need further setup before it is ready
00890    to use.
00891 -----------------------------------------------------------------------------*/
00892     abyss_bool socketBound;
00893     unsigned int portNumber = 0;
00894     TOsSocket socketFd = 0;
00895     const char * logFileName=NULL;
00896 
00897     extractServerCreateParms(envP, parmsP, parmSize,
00898                              &socketBound, &portNumber, &socketFd,
00899                              &logFileName);
00900 
00901     if (!envP->fault_occurred) {
00902         if (socketBound)
00903             createServerBoundSocket(envP, socketFd, logFileName,
00904                                     serverP, socketPP);
00905         else {
00906             ServerCreate(serverP, "XmlRpcServer", portNumber, DEFAULT_DOCS, 
00907                          logFileName);
00908             
00909             *socketPP = NULL;
00910         }
00911         if (logFileName)
00912             xmlrpc_strfree(logFileName);
00913     }
00914 }
00915 
00916 
00917 
00918 static bool
00919 chunkResponseParm(const xmlrpc_server_abyss_parms * const parmsP,
00920                   unsigned int                      const parmSize) {
00921 
00922     return
00923         parmSize >= XMLRPC_APSIZE(chunk_response) &&
00924         parmsP->chunk_response;
00925 }    
00926 
00927 
00928 
00929 static const char *
00930 uriPathParm(const xmlrpc_server_abyss_parms * const parmsP,
00931             unsigned int                      const parmSize) {
00932     
00933     const char * uriPath;
00934 
00935     if (parmSize >= XMLRPC_APSIZE(uri_path) && parmsP->uri_path)
00936         uriPath = parmsP->uri_path;
00937     else
00938         uriPath = "/RPC2";
00939 
00940     return uriPath;
00941 }
00942 
00943 
00944 
00945 static xmlrpc_server_shutdown_fn shutdownAbyss;
00946 
00947 static void
00948 shutdownAbyss(xmlrpc_env * const envP,
00949               void *       const context,
00950               const char * const comment ATTR_UNUSED) {
00951 /*----------------------------------------------------------------------------
00952    Tell Abyss to wrap up whatever it's doing and shut down.
00953 
00954    This is a server shutdown function to be registered in the method
00955    registry, for use by the 'system.shutdown' system method.
00956 
00957    After we return, Abyss will finish up the system.shutdown and any
00958    other connections that are in progress, then the call to
00959    ServerRun() etc. will return.  But Abyss may be stuck waiting for
00960    something, such as the next HTTP connection.  In that case, until it
00961    gets what it's waiting for, it won't even know it's supposed t shut
00962    down.  In particular, a caller of system.shutdown may have to execute
00963    one more RPC in order for the shutdown to happen.
00964 -----------------------------------------------------------------------------*/
00965     TServer * const serverP = context;
00966 
00967     xmlrpc_env_init(envP);
00968     
00969     ServerTerminate(serverP);
00970 }
00971 
00972 
00973 
00974 static void
00975 normalLevelAbyssRun(xmlrpc_env *                      const envP,
00976                     const xmlrpc_server_abyss_parms * const parmsP,
00977                     unsigned int                      const parmSize) {
00978     
00979     TServer server;
00980     TSocket * socketP = 0;
00981 
00982     DateInit();
00983 
00984     createServer(envP, parmsP, parmSize, &server, &socketP);
00985 
00986     if (!envP->fault_occurred) {
00987         struct signalHandlers oldHandlers;
00988 
00989         setAdditionalServerParms(parmsP, parmSize, &server);
00990 
00991         setHandlers(&server, uriPathParm(parmsP, parmSize), parmsP->registryP,
00992                     chunkResponseParm(parmsP, parmSize));
00993 
00994         ServerInit(&server);
00995         
00996         setupSignalHandlers(&oldHandlers);
00997 
00998         ServerUseSigchld(&server);
00999         
01000         if (0)
01001             /* Too much of a security risk.  In 1.07, there is a server
01002                parameter to enable this.
01003             */
01004             xmlrpc_registry_set_shutdown(parmsP->registryP,
01005                                          &shutdownAbyss, &server);
01006         
01007         ServerRun(&server);
01008 
01009         restoreSignalHandlers(oldHandlers);
01010 
01011         ServerFree(&server);
01012 
01013         if (socketP)
01014             SocketDestroy(socketP);
01015     }
01016 }
01017 
01018 
01019 
01020 void
01021 xmlrpc_server_abyss(xmlrpc_env *                      const envP,
01022                     const xmlrpc_server_abyss_parms * const parmsP,
01023                     unsigned int                      const parmSize) {
01024  
01025     XMLRPC_ASSERT_ENV_OK(envP);
01026 
01027     if (parmSize < XMLRPC_APSIZE(registryP))
01028         xmlrpc_faultf(envP,
01029                       "You must specify members at least up through "
01030                       "'registryP' in the server parameters argument.  "
01031                       "That would mean the parameter size would be >= %lu "
01032                       "but you specified a size of %u",
01033                       XMLRPC_APSIZE(registryP), parmSize);
01034     else {
01035         if (parmsP->config_file_name)
01036             oldHighLevelAbyssRun(envP, parmsP, parmSize);
01037         else
01038             normalLevelAbyssRun(envP, parmsP, parmSize);
01039     }
01040 }
01041 
01042 
01043 
01044 /*=========================================================================
01045   XML-RPC Server Method Registry
01046 
01047   This is an old deprecated form of the server facilities that uses
01048   global variables.
01049 =========================================================================*/
01050 
01051 /* These global variables must be treated as read-only after the
01052    server has started.
01053 */
01054 
01055 static TServer globalSrv;
01056     /* When you use the old interface (xmlrpc_server_abyss_init(), etc.),
01057        this is the Abyss server to which they refer.  Obviously, there can be
01058        only one Abyss server per program using this interface.
01059     */
01060 
01061 static xmlrpc_registry * builtin_registryP;
01062 
01063 
01064 
01065 void 
01066 xmlrpc_server_abyss_init_registry(void) {
01067 
01068     /* This used to just create the registry and Caller would be
01069        responsible for adding the handlers that use it.
01070 
01071        But that isn't very modular -- the handlers and registry go
01072        together; there's no sense in using the built-in registry and
01073        not the built-in handlers because if you're custom building
01074        something, you can just make your own regular registry.  So now
01075        we tie them together, and we don't export our handlers.  
01076     */
01077     xmlrpc_env env;
01078 
01079     xmlrpc_env_init(&env);
01080     builtin_registryP = xmlrpc_registry_new(&env);
01081     die_if_fault_occurred(&env);
01082     xmlrpc_env_clean(&env);
01083 
01084     setHandlers(&globalSrv, "/RPC2", builtin_registryP, false);
01085 }
01086 
01087 
01088 
01089 xmlrpc_registry *
01090 xmlrpc_server_abyss_registry(void) {
01091 
01092     /* This is highly deprecated.  If you want to mess with a registry,
01093        make your own with xmlrpc_registry_new() -- don't mess with the
01094        internal one.
01095     */
01096     return builtin_registryP;
01097 }
01098 
01099 
01100 
01101 /* A quick & easy shorthand for adding a method. */
01102 void 
01103 xmlrpc_server_abyss_add_method(char *        const method_name,
01104                                xmlrpc_method const method,
01105                                void *        const user_data) {
01106     xmlrpc_env env;
01107 
01108     xmlrpc_env_init(&env);
01109     xmlrpc_registry_add_method(&env, builtin_registryP, NULL, method_name,
01110                                method, user_data);
01111     die_if_fault_occurred(&env);
01112     xmlrpc_env_clean(&env);
01113 }
01114 
01115 
01116 
01117 void
01118 xmlrpc_server_abyss_add_method_w_doc(char *        const method_name,
01119                                      xmlrpc_method const method,
01120                                      void *        const user_data,
01121                                      char *        const signature,
01122                                      char *        const help) {
01123 
01124     xmlrpc_env env;
01125     xmlrpc_env_init(&env);
01126     xmlrpc_registry_add_method_w_doc(
01127         &env, builtin_registryP, NULL, method_name,
01128         method, user_data, signature, help);
01129     die_if_fault_occurred(&env);
01130     xmlrpc_env_clean(&env);    
01131 }
01132 
01133 
01134 
01135 void 
01136 xmlrpc_server_abyss_init(int          const flags ATTR_UNUSED, 
01137                          const char * const config_file) {
01138 
01139     DateInit();
01140     MIMETypeInit();
01141 
01142     ServerCreate(&globalSrv, "XmlRpcServer", 8080, DEFAULT_DOCS, NULL);
01143     
01144     ConfReadServerFile(config_file, &globalSrv);
01145 
01146     xmlrpc_server_abyss_init_registry();
01147         /* Installs /RPC2 handler and default handler that use the
01148            built-in registry.
01149         */
01150 
01151     ServerInit(&globalSrv);
01152 }
01153 
01154 
01155 
01156 void 
01157 xmlrpc_server_abyss_run_first(runfirstFn const runfirst,
01158                               void *     const runfirstArg) {
01159     
01160     runServerDaemon(&globalSrv, runfirst, runfirstArg);
01161 }
01162 
01163 
01164 
01165 void 
01166 xmlrpc_server_abyss_run(void) {
01167     runServerDaemon(&globalSrv, NULL, NULL);
01168 }
01169 
01170 
01171 
01172 /*
01173 ** Copyright (C) 2001 by First Peer, Inc. All rights reserved.
01174 **
01175 ** Redistribution and use in source and binary forms, with or without
01176 ** modification, are permitted provided that the following conditions
01177 ** are met:
01178 ** 1. Redistributions of source code must retain the above copyright
01179 **    notice, this list of conditions and the following disclaimer.
01180 ** 2. Redistributions in binary form must reproduce the above copyright
01181 **    notice, this list of conditions and the following disclaimer in the
01182 **    documentation and/or other materials provided with the distribution.
01183 ** 3. The name of the author may not be used to endorse or promote products
01184 **    derived from this software without specific prior written permission. 
01185 **  
01186 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
01187 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
01188 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
01189 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
01190 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
01191 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
01192 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
01193 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
01194 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
01195 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
01196 ** SUCH DAMAGE.
01197 **
01198 ** There is more copyright information in the bottom half of this file. 
01199 ** Please see it for more details. 
01200 */