1 /* Copyright information is at end of file */
3 #include "xmlrpc_config.h"
9 #include "xmlrpc-c/base_int.h"
10 #include "xmlrpc-c/string_int.h"
11 #include "xmlrpc-c/base.h"
12 #include "xmlrpc-c/server.h"
15 #include "system_method.h"
18 struct systemMethodReg {
19 /*----------------------------------------------------------------------------
20 Information needed to register a system method
21 -----------------------------------------------------------------------------*/
22 const char * const methodName;
23 xmlrpc_method const methodFunction;
24 const char * const signatureString;
25 const char * const helpText;
31 xmlrpc_registry_disable_introspection(xmlrpc_registry * const registryP) {
33 XMLRPC_ASSERT_PTR_OK(registryP);
35 registryP->_introspection_enabled = false;
41 translateTypeSpecifierToName(xmlrpc_env * const envP,
42 char const typeSpecifier,
43 const char ** const typeNameP) {
45 switch (typeSpecifier) {
46 case 'i': *typeNameP = "int"; break;
47 case 'b': *typeNameP = "boolean"; break;
48 case 'd': *typeNameP = "double"; break;
49 case 's': *typeNameP = "string"; break;
50 case '8': *typeNameP = "dateTime.iso8601"; break;
51 case '6': *typeNameP = "base64"; break;
52 case 'S': *typeNameP = "struct"; break;
53 case 'A': *typeNameP = "array"; break;
54 case 'n': *typeNameP = "nil"; break;
57 "Method registry contains invalid signature "
58 "data. It contains the type specifier '%c'",
66 parseOneTypeSpecifier(xmlrpc_env * const envP,
67 const char * const startP,
68 xmlrpc_value * const signatureP,
69 const char ** const nextP) {
70 /*----------------------------------------------------------------------------
71 Parse one type specifier at 'startP' within a signature string.
73 Add the appropriate item for it to the array 'signatureP'.
75 Return as *nextP the location the signature string just past the
76 type specifier, and also past the colon that comes after the
77 type specifier for a return value.
78 -----------------------------------------------------------------------------*/
79 const char * typeName;
84 translateTypeSpecifierToName(envP, *cursorP, &typeName);
86 if (!envP->fault_occurred) {
90 /* Append the appropriate string to the signature. */
91 typeP = xmlrpc_string_new(envP, typeName);
92 xmlrpc_array_append_item(envP, signatureP, typeP);
95 ++cursorP; /* move past the type specifier */
97 sigArraySize = xmlrpc_array_size(envP, signatureP);
98 if (!envP->fault_occurred) {
99 if (sigArraySize == 1) {
100 /* We parsed off the result type, so we should now
101 see the colon that separates the result type from
105 xmlrpc_faultf(envP, "No colon (':') after "
106 "the result type specifier");
118 parseOneSignature(xmlrpc_env * const envP,
119 const char * const startP,
120 xmlrpc_value ** const signaturePP,
121 const char ** const nextP) {
122 /*----------------------------------------------------------------------------
123 Parse one signature from the signature string that starts at 'startP'.
125 Return that signature as an array xmlrpc_value pointer
126 *signaturePP. The array has one element for the return value,
127 followed by one element for each parameter described in the
128 signature. That element is a string naming the return value or
129 parameter type, e.g. "int".
131 Return as *nextP the location in the signature string of the next
132 signature (i.e. right after the next comma). If there is no next
133 signature (the string ends before any comma), make it point to the
135 -----------------------------------------------------------------------------*/
136 xmlrpc_value * signatureP;
138 signatureP = xmlrpc_array_new(envP); /* Start with empty array */
139 if (!envP->fault_occurred) {
140 const char * cursorP;
142 cursorP = startP; /* start at the beginning */
144 while (!envP->fault_occurred && *cursorP != ',' && *cursorP != '\0')
145 parseOneTypeSpecifier(envP, cursorP, signatureP, &cursorP);
147 if (!envP->fault_occurred) {
148 if (xmlrpc_array_size(envP, signatureP) < 1)
149 xmlrpc_faultf(envP, "empty signature (a signature "
150 "must have at least return value type)");
151 if (*cursorP != '\0') {
152 assert(*cursorP == ',');
157 if (envP->fault_occurred)
158 xmlrpc_DECREF(signatureP);
160 *signaturePP = signatureP;
167 xmlrpc_buildSignatureArray(xmlrpc_env * const envP,
168 const char * const sigListString,
169 xmlrpc_value ** const resultPP) {
170 /*----------------------------------------------------------------------------
171 Turn the signature string 'sig' (e.g. "ii,s") into an array
172 as *resultP. The array contains one element for each signature in
173 the string. (Signatures are separated by commas. The "ii,s" example
174 is two signatures: "ii" and "s"). Each element is itself an array
175 as described under parseOneSignature().
176 -----------------------------------------------------------------------------*/
177 xmlrpc_value * signatureListP;
179 signatureListP = xmlrpc_array_new(envP);
180 if (!envP->fault_occurred) {
181 if (sigListString == NULL || xmlrpc_streq(sigListString, "?")) {
182 /* No signatures -- leave the array empty */
184 const char * cursorP;
186 cursorP = &sigListString[0];
188 while (!envP->fault_occurred && *cursorP != '\0') {
189 xmlrpc_value * signatureP;
191 parseOneSignature(envP, cursorP, &signatureP, &cursorP);
193 /* cursorP now points at next signature in the list or the
197 if (!envP->fault_occurred) {
198 xmlrpc_array_append_item(envP, signatureListP, signatureP);
199 xmlrpc_DECREF(signatureP);
202 if (!envP->fault_occurred) {
203 unsigned int const arraySize =
204 xmlrpc_array_size(envP, signatureListP);
205 XMLRPC_ASSERT_ENV_OK(envP);
207 xmlrpc_faultf(envP, "Signature string is empty.");
210 if (envP->fault_occurred)
211 xmlrpc_DECREF(signatureListP);
213 *resultPP = signatureListP;
218 /*=========================================================================
220 =========================================================================*/
222 static xmlrpc_value *
223 call_one_method(xmlrpc_env *env, xmlrpc_registry *registry,
224 xmlrpc_value *method_info) {
226 xmlrpc_value *result_val, *result;
228 xmlrpc_value *param_array;
230 /* Error-handling preconditions. */
231 result = result_val = NULL;
233 /* Extract our method name and parameters. */
234 xmlrpc_parse_value(env, method_info, "{s:s,s:A,*}",
235 "methodName", &method_name,
236 "params", ¶m_array);
237 XMLRPC_FAIL_IF_FAULT(env);
239 /* Watch out for a deep recursion attack. */
240 if (strcmp(method_name, "system.multicall") == 0)
241 XMLRPC_FAIL(env, XMLRPC_REQUEST_REFUSED_ERROR,
242 "Recursive system.multicall strictly forbidden");
244 /* Perform the call. */
245 xmlrpc_dispatchCall(env, registry, method_name, param_array, &result_val);
246 XMLRPC_FAIL_IF_FAULT(env);
248 /* Build our one-item result array. */
249 result = xmlrpc_build_value(env, "(V)", result_val);
250 XMLRPC_FAIL_IF_FAULT(env);
254 xmlrpc_DECREF(result_val);
255 if (env->fault_occurred) {
257 xmlrpc_DECREF(result);
265 static xmlrpc_value *
266 system_multicall(xmlrpc_env *env,
267 xmlrpc_value *param_array,
270 xmlrpc_registry *registry;
271 xmlrpc_value *methlist, *methinfo, *results, *result;
275 XMLRPC_ASSERT_ENV_OK(env);
276 XMLRPC_ASSERT_VALUE_OK(param_array);
277 XMLRPC_ASSERT_PTR_OK(user_data);
279 /* Error-handling preconditions. */
280 results = result = NULL;
281 xmlrpc_env_init(&env2);
283 /* Turn our arguments into something more useful. */
284 registry = (xmlrpc_registry*) user_data;
285 xmlrpc_parse_value(env, param_array, "(A)", &methlist);
286 XMLRPC_FAIL_IF_FAULT(env);
288 /* Create an empty result list. */
289 results = xmlrpc_build_value(env, "()");
290 XMLRPC_FAIL_IF_FAULT(env);
292 /* Loop over our input list, calling each method in turn. */
293 size = xmlrpc_array_size(env, methlist);
294 XMLRPC_ASSERT_ENV_OK(env);
295 for (i = 0; i < size; i++) {
296 methinfo = xmlrpc_array_get_item(env, methlist, i);
297 XMLRPC_ASSERT_ENV_OK(env);
299 /* Call our method. */
300 xmlrpc_env_clean(&env2);
301 xmlrpc_env_init(&env2);
302 result = call_one_method(&env2, registry, methinfo);
304 /* Turn any fault into a structure. */
305 if (env2.fault_occurred) {
306 XMLRPC_ASSERT(result == NULL);
308 xmlrpc_build_value(env, "{s:i,s:s}",
309 "faultCode", (xmlrpc_int32) env2.fault_code,
310 "faultString", env2.fault_string);
311 XMLRPC_FAIL_IF_FAULT(env);
314 /* Append this method result to our master array. */
315 xmlrpc_array_append_item(env, results, result);
316 xmlrpc_DECREF(result);
318 XMLRPC_FAIL_IF_FAULT(env);
322 xmlrpc_env_clean(&env2);
324 xmlrpc_DECREF(result);
325 if (env->fault_occurred) {
327 xmlrpc_DECREF(results);
335 static struct systemMethodReg const multicall = {
339 "Process an array of calls, and return an array of results. Calls should "
340 "be structs of the form {'methodName': string, 'params': array}. Each "
341 "result will either be a single-item array containg the result value, or "
342 "a struct of the form {'faultCode': int, 'faultString': string}. This "
343 "is useful when you need to make lots of small calls without lots of "
348 /*=========================================================================
350 =========================================================================*/
354 static xmlrpc_value *
355 system_listMethods(xmlrpc_env *env,
356 xmlrpc_value *param_array,
359 xmlrpc_registry *registry;
360 xmlrpc_value *method_names, *method_name, *method_info;
363 XMLRPC_ASSERT_ENV_OK(env);
364 XMLRPC_ASSERT_VALUE_OK(param_array);
365 XMLRPC_ASSERT_PTR_OK(user_data);
367 /* Error-handling preconditions. */
370 /* Turn our arguments into something more useful. */
371 registry = (xmlrpc_registry*) user_data;
372 xmlrpc_parse_value(env, param_array, "()");
373 XMLRPC_FAIL_IF_FAULT(env);
375 /* Make sure we're allowed to introspect. */
376 if (!registry->_introspection_enabled)
377 XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
378 "Introspection disabled for security reasons");
380 /* Iterate over all the methods in the registry, adding their names
382 method_names = xmlrpc_build_value(env, "()");
383 XMLRPC_FAIL_IF_FAULT(env);
384 size = xmlrpc_struct_size(env, registry->_methods);
385 XMLRPC_FAIL_IF_FAULT(env);
386 for (i = 0; i < size; i++) {
387 xmlrpc_struct_get_key_and_value(env, registry->_methods, i,
388 &method_name, &method_info);
389 XMLRPC_FAIL_IF_FAULT(env);
390 xmlrpc_array_append_item(env, method_names, method_name);
391 XMLRPC_FAIL_IF_FAULT(env);
395 if (env->fault_occurred) {
397 xmlrpc_DECREF(method_names);
403 static struct systemMethodReg const listMethods = {
404 "system.listMethods",
407 "Return an array of all available XML-RPC methods on this server.",
412 /*=========================================================================
414 =========================================================================*/
416 static xmlrpc_value *
417 system_methodHelp(xmlrpc_env *env,
418 xmlrpc_value *param_array,
421 xmlrpc_registry *registry;
423 xmlrpc_value *ignored1, *ignored2, *ignored3, *help;
425 XMLRPC_ASSERT_ENV_OK(env);
426 XMLRPC_ASSERT_VALUE_OK(param_array);
427 XMLRPC_ASSERT_PTR_OK(user_data);
429 /* Turn our arguments into something more useful. */
430 registry = (xmlrpc_registry*) user_data;
431 xmlrpc_parse_value(env, param_array, "(s)", &method_name);
432 XMLRPC_FAIL_IF_FAULT(env);
434 /* Make sure we're allowed to introspect. */
435 if (!registry->_introspection_enabled)
436 XMLRPC_FAIL(env, XMLRPC_INTROSPECTION_DISABLED_ERROR,
437 "Introspection disabled for security reasons");
439 /* Get our documentation string. */
440 xmlrpc_parse_value(env, registry->_methods, "{s:(VVVV*),*}",
441 method_name, &ignored1, &ignored2, &ignored3, &help);
442 XMLRPC_FAIL_IF_FAULT(env);
445 if (env->fault_occurred)
452 static struct systemMethodReg const methodHelp = {
456 "Given the name of a method, return a help string.",
462 getMethodInfo(xmlrpc_env * const envP,
463 xmlrpc_registry * const registryP,
464 const char * const methodName,
465 xmlrpc_value ** const methodInfoPP) {
466 /*----------------------------------------------------------------------------
467 Look up the method info for the named method. Method info
469 -----------------------------------------------------------------------------*/
471 xmlrpc_value * methodInfoP;
473 xmlrpc_env_init(&env);
475 /* We can't use xmlrpc_struct_find_value() here because it isn't
476 thread-safe (it manipulates the reference count) and servers
477 sometimes call system methods from multiple threads at once.
479 methodInfoP = xmlrpc_struct_get_value(
480 &env, registryP->_methods, methodName);
482 if (env.fault_occurred) {
483 if (env.fault_code == XMLRPC_INDEX_ERROR)
484 xmlrpc_env_set_fault_formatted(
485 envP, XMLRPC_NO_SUCH_METHOD_ERROR,
486 "Method '%s' does not exist", methodName);
488 xmlrpc_faultf(envP, "Unable to look up method named '%s' in the "
489 "registry. %s", methodName, env.fault_string);
491 *methodInfoPP = methodInfoP;
493 xmlrpc_env_clean(&env);
498 /*=========================================================================
499 system.methodSignature
500 ==========================================================================*/
503 buildNoSigSuppliedResult(xmlrpc_env * const envP,
504 xmlrpc_value ** const resultPP) {
508 xmlrpc_env_init(&env);
510 *resultPP = xmlrpc_string_new(&env, "undef");
511 if (env.fault_occurred)
512 xmlrpc_faultf(envP, "Unable to construct 'undef'. %s",
515 xmlrpc_env_clean(&env);
521 makeSigListCopy(xmlrpc_env * const envP,
522 xmlrpc_value * const oldP,
523 xmlrpc_value ** const newPP) {
527 newP = xmlrpc_array_new(envP);
529 if (!envP->fault_occurred) {
530 unsigned int const size = xmlrpc_array_size(envP, oldP);
531 if (!envP->fault_occurred) {
533 for (i = 0; i < size; ++i) {
534 /* We can't use xmlrpc_array_read_item() here because
535 it isn't thread-safe (it manipulates the reference count)
536 an servers sometimes call system methods from multiple
539 xmlrpc_value * const itemP =
540 xmlrpc_array_get_item(envP, oldP, i);
541 xmlrpc_array_append_item(envP, newP, itemP);
551 getSignatureList(xmlrpc_env * const envP,
552 xmlrpc_registry * const registryP,
553 const char * const methodName,
554 xmlrpc_value ** const signatureListPP) {
555 /*----------------------------------------------------------------------------
556 Get the signature list array for method named 'methodName' from registry
559 If there is no signature information for the method in the registry,
560 return *signatureListPP == NULL.
562 Nonexistent method is considered a failure.
563 -----------------------------------------------------------------------------*/
564 xmlrpc_value * methodInfoP;
566 getMethodInfo(envP, registryP, methodName, &methodInfoP);
567 if (!envP->fault_occurred) {
569 xmlrpc_value * signatureListP;
571 xmlrpc_env_init(&env);
573 /* We can't use xmlrpc_array_read_item() because it isn't thread
574 safe (it manipulates the reference count) and servers sometimes
575 run system methods from multiple threads at once.
577 signatureListP = xmlrpc_array_get_item(&env, methodInfoP, 2);
579 if (env.fault_occurred)
580 xmlrpc_faultf(envP, "Failed to read signature list "
581 "from method info array. %s",
586 arraySize = xmlrpc_array_size(&env, signatureListP);
587 if (env.fault_occurred)
588 xmlrpc_faultf(envP, "xmlrpc_array_size() on signature "
589 "list array failed! %s", env.fault_string);
592 *signatureListPP = NULL;
594 makeSigListCopy(envP, signatureListP, signatureListPP);
598 xmlrpc_env_clean(&env);
604 static xmlrpc_value *
605 system_methodSignature(xmlrpc_env * const envP,
606 xmlrpc_value * const paramArrayP,
607 void * const userData) {
609 xmlrpc_registry * const registryP = (xmlrpc_registry *) userData;
611 xmlrpc_value * retvalP;
612 const char * methodName;
615 XMLRPC_ASSERT_ENV_OK(envP);
616 XMLRPC_ASSERT_VALUE_OK(paramArrayP);
617 XMLRPC_ASSERT_PTR_OK(userData);
619 xmlrpc_env_init(&env);
621 /* Turn our arguments into something more useful. */
622 xmlrpc_decompose_value(&env, paramArrayP, "(s)", &methodName);
623 if (env.fault_occurred)
624 xmlrpc_env_set_fault_formatted(
625 envP, env.fault_code,
626 "Invalid parameter list. %s", env.fault_string);
628 if (!registryP->_introspection_enabled)
629 xmlrpc_env_set_fault(envP, XMLRPC_INTROSPECTION_DISABLED_ERROR,
630 "Introspection disabled on this server");
632 xmlrpc_value * signatureListP;
634 getSignatureList(envP, registryP, methodName, &signatureListP);
636 if (!envP->fault_occurred) {
638 retvalP = signatureListP;
640 buildNoSigSuppliedResult(envP, &retvalP);
643 xmlrpc_strfree(methodName);
645 xmlrpc_env_clean(&env);
652 static struct systemMethodReg const methodSignature = {
653 "system.methodSignature",
654 &system_methodSignature,
656 "Given the name of a method, return an array of legal signatures. "
657 "Each signature is an array of strings. The first item of each signature "
658 "is the return type, and any others items are parameter types.",
664 /*=========================================================================
666 ==========================================================================*/
668 static xmlrpc_value *
669 system_shutdown(xmlrpc_env * const envP,
670 xmlrpc_value * const paramArrayP,
671 void * const userData) {
673 xmlrpc_registry * const registryP = (xmlrpc_registry *) userData;
675 xmlrpc_value * retvalP;
676 const char * comment;
679 XMLRPC_ASSERT_ENV_OK(envP);
680 XMLRPC_ASSERT_VALUE_OK(paramArrayP);
681 XMLRPC_ASSERT_PTR_OK(userData);
683 xmlrpc_env_init(&env);
685 retvalP = NULL; /* quiet compiler warning */
687 /* Turn our arguments into something more useful. */
688 xmlrpc_decompose_value(&env, paramArrayP, "(s)", &comment);
689 if (env.fault_occurred)
690 xmlrpc_env_set_fault_formatted(
691 envP, env.fault_code,
692 "Invalid parameter list. %s", env.fault_string);
694 if (!registryP->_shutdown_server_fn)
695 xmlrpc_env_set_fault(
696 envP, 0, "This server program is not capable of "
699 registryP->_shutdown_server_fn(
700 &env, registryP->_shutdown_context, comment);
702 if (env.fault_occurred)
703 xmlrpc_env_set_fault(envP, env.fault_code, env.fault_string);
705 retvalP = xmlrpc_int_new(&env, 0);
707 if (env.fault_occurred)
709 "Failed to construct return value. %s",
713 xmlrpc_strfree(comment);
715 xmlrpc_env_clean(&env);
722 static struct systemMethodReg const shutdown = {
726 "Shut down the server. Return code is always zero.",
731 /*============================================================================
732 Installer of system methods
733 ============================================================================*/
736 registerSystemMethod(xmlrpc_env * const envP,
737 xmlrpc_registry * const registryP,
738 struct systemMethodReg const methodReg) {
741 xmlrpc_env_init(&env);
743 xmlrpc_registry_add_method_w_doc(
744 &env, registryP, NULL, methodReg.methodName,
745 methodReg.methodFunction, registryP,
746 methodReg.signatureString, methodReg.helpText);
748 if (env.fault_occurred)
749 xmlrpc_faultf(envP, "Failed to register '%s' system method. %s",
750 methodReg.methodName, env.fault_string);
752 xmlrpc_env_clean(&env);
758 xmlrpc_installSystemMethods(xmlrpc_env * const envP,
759 xmlrpc_registry * const registryP) {
760 /*----------------------------------------------------------------------------
761 Install the built-in methods (system.*) into registry 'registryP'.
762 -----------------------------------------------------------------------------*/
763 if (!envP->fault_occurred)
764 registerSystemMethod(envP, registryP, listMethods);
766 if (!envP->fault_occurred)
767 registerSystemMethod(envP, registryP, methodSignature);
769 if (!envP->fault_occurred)
770 registerSystemMethod(envP, registryP, methodHelp);
772 if (!envP->fault_occurred)
773 registerSystemMethod(envP, registryP, multicall);
775 if (!envP->fault_occurred)
776 registerSystemMethod(envP, registryP, shutdown);
781 /* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
782 ** Copyright (C) 2001 by Eric Kidd. All rights reserved.
783 ** Copyright (C) 2001 by Luke Howard. All rights reserved.
785 ** Redistribution and use in source and binary forms, with or without
786 ** modification, are permitted provided that the following conditions
788 ** 1. Redistributions of source code must retain the above copyright
789 ** notice, this list of conditions and the following disclaimer.
790 ** 2. Redistributions in binary form must reproduce the above copyright
791 ** notice, this list of conditions and the following disclaimer in the
792 ** documentation and/or other materials provided with the distribution.
793 ** 3. The name of the author may not be used to endorse or promote products
794 ** derived from this software without specific prior written permission.
796 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
797 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
798 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
799 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
800 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
801 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
802 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
803 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
804 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
805 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF