| /* 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); |
| } |