initial load of upstream version 1.06.32
[xmlrpc-c] / src / registry.c
1 /* Copyright information is at end of file */
2
3 #include "xmlrpc_config.h"
4
5 #include <assert.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "bool.h"
10 #include "mallocvar.h"
11 #include "xmlrpc-c/base_int.h"
12 #include "xmlrpc-c/string_int.h"
13 #include "xmlrpc-c/base.h"
14 #include "xmlrpc-c/server.h"
15 #include "system_method.h"
16
17 #include "registry.h"
18
19 /*=========================================================================
20   XML-RPC Server Method Registry
21 ===========================================================================
22   A method registry is a list of XML-RPC methods for a server to
23   implement, along with the details of how to implement each -- most
24   notably a function pointer for a function that executes the method.
25
26   To build an XML-RPC server, just add a communication facility.
27
28   The registry object consists principally of an xmlrpc_value.  That
29   xmlrpc_value is a struct, with method name as key.  Each value in
30   the struct a "method info" array.  A method info array has the
31   following items:
32
33      0: cptr: method function ptr
34      1: cptr: user data
35      2: array: signature list
36      3: string: help text.
37     
38   The signature list array contains one item for each form of call (a
39   single method might have multiple forms, e.g. one takes two integer
40   arguments; another takes a single string).  The array for each form
41   represents a signature.  It has an item for each parameter and one
42   for the result.  Item 0 is for the result, the rest are in order of
43   the parameters.  Each item of that array is the name of the XML-RPC
44   element that represents that type, e.g.  "int" or
45   "dateTime.iso8601".
46
47   Example signature:
48
49     (("int"
50       "double"
51       "double"
52      )
53      ("int"
54      )
55     )
56
57   The signature list array is empty to indicate that there is no signature
58   information in the registry (it doesn't mean there are no valid forms
59   of calling the method -- just that the registry declines to state).
60
61
62   WARNING: there's a basic problem with using xmlrpc_value objects to
63   represent the registry: xmlrpc_value objects are defined to be not
64   thread-safe: you can't use one from two threads at the same time.
65   But XML-RPC servers are often threaded, with multiple threads
66   simultaneously executing multiple RPCs.  The normal Xmlrpc-c Abyss
67   server is a good example.
68
69   As a hack to make this work, we use the old, deprecated "get"
70   functions that don't take a reference in the call dispatching code.
71   Maintaining the reference count is the only way that the the
72   thread-unsafety can manifest itself in our application.  Since the
73   registry has at least one reference to each xmlrpc_value as long as
74   the registry exists, we don't really need the exact reference count,
75   so the deprecated functions work fine.
76
77 =========================================================================*/
78
79
80
81 xmlrpc_registry *
82 xmlrpc_registry_new(xmlrpc_env * const envP) {
83
84     xmlrpc_registry * registryP;
85
86     XMLRPC_ASSERT_ENV_OK(envP);
87     
88     MALLOCVAR(registryP);
89
90     if (registryP == NULL)
91         xmlrpc_faultf(envP, "Could not allocate memory for registry");
92     else {
93         registryP->_introspection_enabled = true;
94         registryP->_default_method        = NULL;
95         registryP->_preinvoke_method      = NULL;
96         registryP->_shutdown_server_fn    = NULL;
97
98         registryP->_methods = xmlrpc_struct_new(envP);
99         if (!envP->fault_occurred) {
100             xmlrpc_installSystemMethods(envP, registryP);
101         }    
102         if (envP->fault_occurred)
103             free(registryP);
104     }
105     return registryP;
106 }
107
108
109
110 void 
111 xmlrpc_registry_free(xmlrpc_registry * const registryP) {
112
113     XMLRPC_ASSERT_PTR_OK(registryP);
114     XMLRPC_ASSERT(registryP->_methods != XMLRPC_BAD_POINTER);
115
116     xmlrpc_DECREF(registryP->_methods);
117
118     if (registryP->_default_method != NULL)
119         xmlrpc_DECREF(registryP->_default_method);
120
121     if (registryP->_preinvoke_method != NULL)
122         xmlrpc_DECREF(registryP->_preinvoke_method);
123
124     free(registryP);
125 }
126
127
128
129 void 
130 xmlrpc_registry_add_method_w_doc(
131     xmlrpc_env *      const envP,
132     xmlrpc_registry * const registryP,
133     const char *      const host ATTR_UNUSED,
134     const char *      const methodName,
135     xmlrpc_method     const method,
136     void *            const userData,
137     const char *      const signatureString,
138     const char *      const help) {
139
140     const char * const helpString =
141         help ? help : "No help is available for this method.";
142
143     xmlrpc_env env;
144     xmlrpc_value * signatureListP;
145
146     XMLRPC_ASSERT_ENV_OK(envP);
147     XMLRPC_ASSERT_PTR_OK(registryP);
148     XMLRPC_ASSERT(host == NULL);
149     XMLRPC_ASSERT_PTR_OK(methodName);
150     XMLRPC_ASSERT_PTR_OK(method);
151
152     xmlrpc_env_init(&env);
153
154     xmlrpc_buildSignatureArray(&env, signatureString, &signatureListP);
155     if (env.fault_occurred)
156         xmlrpc_faultf(envP, "Can't interpret signature string '%s'.  %s",
157                       signatureString, env.fault_string);
158     else {
159         xmlrpc_value * methodInfoP;
160
161         XMLRPC_ASSERT_VALUE_OK(signatureListP);
162
163         methodInfoP = xmlrpc_build_value(envP, "(ppVs)", (void*) method,
164                                          userData, signatureListP, helpString);
165         if (!envP->fault_occurred) {
166             xmlrpc_struct_set_value(envP, registryP->_methods,
167                                     methodName, methodInfoP);
168
169             xmlrpc_DECREF(methodInfoP);
170         }
171         xmlrpc_DECREF(signatureListP);
172     }
173     xmlrpc_env_clean(&env);
174 }
175
176
177
178 void 
179 xmlrpc_registry_add_method(xmlrpc_env *env,
180                            xmlrpc_registry *registry,
181                            const char *host,
182                            const char *method_name,
183                            xmlrpc_method method,
184                            void *user_data) {
185
186     xmlrpc_registry_add_method_w_doc (env, registry, host, method_name,
187                       method, user_data, "?",
188                       "No help is available for this method.");
189 }
190
191
192
193 /*=========================================================================
194 **  xmlrpc_registry_set_default_method
195 **=========================================================================
196 **  See xmlrpc.h for more documentation.
197 */
198
199 void 
200 xmlrpc_registry_set_default_method(xmlrpc_env *env,
201                                    xmlrpc_registry *registry,
202                                    xmlrpc_default_method handler,
203                                    void *user_data) {
204     xmlrpc_value *method_info;
205
206     XMLRPC_ASSERT_ENV_OK(env);
207     XMLRPC_ASSERT_PTR_OK(registry);
208     XMLRPC_ASSERT_PTR_OK(handler);
209
210     /* Error-handling preconditions. */
211     method_info = NULL;
212     
213     /* Store our method and user data into our hash table. */
214     method_info = xmlrpc_build_value(env, "(pp)", (void*) handler, user_data);
215     XMLRPC_FAIL_IF_FAULT(env);
216
217     /* Dispose of any pre-existing default method and install ours. */
218     if (registry->_default_method)
219         xmlrpc_DECREF(registry->_default_method);
220     registry->_default_method = method_info;
221     
222 cleanup:
223     if (env->fault_occurred) {
224         if (method_info)
225             xmlrpc_DECREF(method_info);
226     }
227 }
228
229
230
231
232 void 
233 xmlrpc_registry_set_preinvoke_method(xmlrpc_env *env,
234                                      xmlrpc_registry *registry,
235                                      xmlrpc_preinvoke_method handler,
236                                      void *user_data) {
237     xmlrpc_value *method_info;
238
239     XMLRPC_ASSERT_ENV_OK(env);
240     XMLRPC_ASSERT_PTR_OK(registry);
241     XMLRPC_ASSERT_PTR_OK(handler);
242
243     /* Error-handling preconditions. */
244     method_info = NULL;
245
246     /* Store our method and user data into our hash table. */
247     method_info = xmlrpc_build_value(env, "(pp)", (void*) handler, user_data);
248     XMLRPC_FAIL_IF_FAULT(env);
249
250     /* Dispose of any pre-existing preinvoke method and install ours. */
251     if (registry->_preinvoke_method)
252         xmlrpc_DECREF(registry->_preinvoke_method);
253     registry->_preinvoke_method = method_info;
254
255  cleanup:
256     if (env->fault_occurred) {
257         if (method_info)
258             xmlrpc_DECREF(method_info);
259     }
260 }
261
262
263
264 void
265 xmlrpc_registry_set_shutdown(xmlrpc_registry *           const registryP,
266                              xmlrpc_server_shutdown_fn * const shutdownFn,
267                              void *                      const context) {
268
269     XMLRPC_ASSERT_PTR_OK(registryP);
270     XMLRPC_ASSERT_PTR_OK(shutdownFn);
271
272     registryP->_shutdown_server_fn = shutdownFn;
273
274     registryP->_shutdown_context = context;
275 }
276
277
278
279 /*=========================================================================
280 **  dispatch_call
281 **=========================================================================
282 **  An internal method which actually does the dispatch. This may get
283 **  prettified and exported at some point in the future.
284 */
285
286 static void
287 callPreinvokeMethodIfAny(xmlrpc_env *      const envP,
288                          xmlrpc_registry * const registryP,
289                          const char *      const methodName,
290                          xmlrpc_value *    const paramArrayP) {
291
292     /* Get the preinvoke method, if it is set. */
293     if (registryP->_preinvoke_method) {
294         xmlrpc_preinvoke_method preinvoke_method;
295         void * user_data;
296
297         xmlrpc_parse_value(envP, registryP->_preinvoke_method, "(pp)",
298                            &preinvoke_method, &user_data);
299         if (!envP->fault_occurred)
300             (*preinvoke_method)(envP, methodName,
301                                 paramArrayP, user_data);
302     }
303 }
304
305
306
307 static void
308 callDefaultMethod(xmlrpc_env *    const envP,
309                   xmlrpc_value *  const defaultMethodInfo,
310                   const char *    const methodName,
311                   xmlrpc_value *  const paramArrayP,
312                   xmlrpc_value ** const resultPP) {
313
314     xmlrpc_default_method default_method;
315     void * user_data;
316
317     xmlrpc_parse_value(envP, defaultMethodInfo, "(pp)",
318                        &default_method, &user_data);
319
320     if (!envP->fault_occurred)
321         *resultPP = (*default_method)(envP, NULL, methodName,
322                                       paramArrayP, user_data);
323 }
324     
325
326
327 static void
328 callNamedMethod(xmlrpc_env *    const envP,
329                 xmlrpc_value *  const methodInfo,
330                 xmlrpc_value *  const paramArrayP,
331                 xmlrpc_value ** const resultPP) {
332
333     xmlrpc_method method;
334     void * user_data;
335     
336     xmlrpc_parse_value(envP, methodInfo, "(pp*)", &method, &user_data);
337     if (!envP->fault_occurred)
338         *resultPP = (*method)(envP, paramArrayP, user_data);
339 }
340
341
342
343 void
344 xmlrpc_dispatchCall(xmlrpc_env *      const envP, 
345                     xmlrpc_registry * const registryP,
346                     const char *      const methodName, 
347                     xmlrpc_value *    const paramArrayP,
348                     xmlrpc_value **   const resultPP) {
349
350     callPreinvokeMethodIfAny(envP, registryP, methodName, paramArrayP);
351     if (!envP->fault_occurred) {
352         xmlrpc_value * methodInfoP;
353         xmlrpc_env methodLookupEnv;
354
355         xmlrpc_env_init(&methodLookupEnv);
356
357         /* See comments at top of file about why we use the deprecated
358            xmlrpc_struct_get_value() here
359         */
360         methodInfoP = xmlrpc_struct_get_value(&methodLookupEnv,
361                                               registryP->_methods,
362                                               methodName);
363         if (!methodLookupEnv.fault_occurred)
364             callNamedMethod(envP, methodInfoP, paramArrayP, resultPP);
365         else if (methodLookupEnv.fault_code == XMLRPC_INDEX_ERROR) {
366             if (registryP->_default_method)
367                 callDefaultMethod(envP, registryP->_default_method, 
368                                   methodName, paramArrayP,
369                                   resultPP);
370             else {
371                 /* No matching method, and no default. */
372                 xmlrpc_env_set_fault_formatted(
373                     envP, XMLRPC_NO_SUCH_METHOD_ERROR,
374                     "Method '%s' not defined", methodName);
375             }
376         } else
377             xmlrpc_faultf(envP, "failed to lookup method in registry's "
378                           "internal method struct.  %s",
379                           methodLookupEnv.fault_string);
380         xmlrpc_env_clean(&methodLookupEnv); 
381     }
382     /* For backward compatibility, for sloppy users: */
383     if (envP->fault_occurred)
384         *resultPP = NULL;
385 }
386
387
388
389 /*=========================================================================
390 **  xmlrpc_registry_process_call
391 **=========================================================================
392 **
393 */
394
395 xmlrpc_mem_block *
396 xmlrpc_registry_process_call(xmlrpc_env *      const envP,
397                              xmlrpc_registry * const registryP,
398                              const char *      const host ATTR_UNUSED,
399                              const char *      const xml_data,
400                              size_t            const xml_len) {
401
402     xmlrpc_mem_block * output;
403
404     XMLRPC_ASSERT_ENV_OK(envP);
405     XMLRPC_ASSERT_PTR_OK(xml_data);
406     
407     xmlrpc_traceXml("XML-RPC CALL", xml_data, xml_len);
408
409     /* Allocate our output buffer.
410     ** If this fails, we need to die in a special fashion. */
411     output = XMLRPC_MEMBLOCK_NEW(char, envP, 0);
412     if (!envP->fault_occurred) {
413         const char * methodName;
414         xmlrpc_value * paramArray;
415         xmlrpc_env fault;
416         xmlrpc_env parseEnv;
417
418         xmlrpc_env_init(&fault);
419         xmlrpc_env_init(&parseEnv);
420
421         xmlrpc_parse_call(&parseEnv, xml_data, xml_len, 
422                           &methodName, &paramArray);
423
424         if (parseEnv.fault_occurred)
425             xmlrpc_env_set_fault_formatted(
426                 &fault, XMLRPC_PARSE_ERROR,
427                 "Call XML not a proper XML-RPC call.  %s",
428                 parseEnv.fault_string);
429         else {
430             xmlrpc_value * result;
431             
432             xmlrpc_dispatchCall(&fault, registryP, methodName, paramArray,
433                                 &result);
434
435             if (!fault.fault_occurred) {
436                 xmlrpc_serialize_response(envP, output, result);
437
438                 /* A comment here used to say that
439                    xmlrpc_serialize_response() could fail and "leave
440                    stuff in the buffer."  Don't know what that means,
441                    but it sounds like something that needs to be
442                    fixed.  The old code aborted the program here if
443                    xmlrpc_serialize_repsonse() failed.  04.11.17 
444                 */
445                 xmlrpc_DECREF(result);
446             } 
447             xmlrpc_strfree(methodName);
448             xmlrpc_DECREF(paramArray);
449         }
450         if (!envP->fault_occurred && fault.fault_occurred)
451             xmlrpc_serialize_fault(envP, output, &fault);
452
453         xmlrpc_env_clean(&parseEnv);
454         xmlrpc_env_clean(&fault);
455
456         if (envP->fault_occurred)
457             XMLRPC_MEMBLOCK_FREE(char, output);
458         else
459             xmlrpc_traceXml("XML-RPC RESPONSE", 
460                             XMLRPC_MEMBLOCK_CONTENTS(char, output),
461                             XMLRPC_MEMBLOCK_SIZE(char, output));
462     }
463     return output;
464 }
465
466
467 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
468 ** Copyright (C) 2001 by Eric Kidd. All rights reserved.
469 ** Copyright (C) 2001 by Luke Howard. All rights reserved.
470 **
471 ** Redistribution and use in source and binary forms, with or without
472 ** modification, are permitted provided that the following conditions
473 ** are met:
474 ** 1. Redistributions of source code must retain the above copyright
475 **    notice, this list of conditions and the following disclaimer.
476 ** 2. Redistributions in binary form must reproduce the above copyright
477 **    notice, this list of conditions and the following disclaimer in the
478 **    documentation and/or other materials provided with the distribution.
479 ** 3. The name of the author may not be used to endorse or promote products
480 **    derived from this software without specific prior written permission. 
481 **  
482 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
483 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
484 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
485 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
486 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
487 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
488 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
489 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
490 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
491 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
492 ** SUCH DAMAGE. */