Created
April 22, 2011 17:43
-
-
Save emk/937200 to your computer and use it in GitHub Desktop.
XML-RPC for C code snippet (revision 1.10 of xmlrpc_data.c and 1.23 of rpctest.c, from ~10 years ago)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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. */ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#ifndef HAVE_WIN32_CONFIG_H | |
#include "xmlrpc_config.h" | |
#else | |
#include "xmlrpc_win32_config.h" | |
#endif | |
#include "xmlrpc.h" | |
#include "xmlrpc_xmlparser.h" | |
#define CRLF "\015\012" | |
#define INT_MAX (2147483647) | |
#define INT_MIN (-INT_MAX - 1) | |
/*========================================================================= | |
** Test Harness | |
**========================================================================= | |
** This is a super light-weight test harness. It's vaguely inspired by | |
** Kent Beck's book on eXtreme Programming (XP)--the output is succinct, | |
** new tests can be coded quickly, and the whole thing runs in a few | |
** second's time. | |
** | |
** To run the tests, type './rpctest'. | |
** To check for memory leaks, install RedHat's 'memprof' utility, and | |
** type 'memprof rpctest'. | |
** | |
** If you add new tests to this file, please deallocate any data | |
** structures you use in the appropriate fashion. This allows us to test | |
** various destructor code for memory leaks. | |
*/ | |
int total_tests = 0; | |
int total_failures = 0; | |
/* This is a good place to set a breakpoint. */ | |
static void test_failure (char *file, int line, char *label, char *statement) | |
{ | |
total_failures++; | |
printf("\n%s:%d: test failure: %s (%s)\n", file, line, label, statement); | |
exit(1); | |
} | |
#define TEST(statement) \ | |
do { \ | |
total_tests++; \ | |
if ((statement)) { \ | |
printf("."); \ | |
} else { \ | |
test_failure(__FILE__, __LINE__, "expected", #statement); \ | |
} \ | |
} while (0) | |
#define TEST_NO_FAULT(env) \ | |
do { \ | |
total_tests++; \ | |
if (!(env)->fault_occurred) { \ | |
printf("."); \ | |
} else { \ | |
test_failure(__FILE__, __LINE__, "fault occurred", \ | |
(env)->fault_string); \ | |
} \ | |
} while (0) | |
/*========================================================================= | |
** Test Data | |
**========================================================================= | |
** Some common test data which need to be allocated at a fixed address, | |
** or which are inconvenient to allocate inline. | |
*/ | |
static char* test_string_1 = "foo"; | |
static char* test_string_2 = "bar"; | |
static int test_int_array_1[5] = {1, 2, 3, 4, 5}; | |
static int test_int_array_2[3] = {6, 7, 8}; | |
static int test_int_array_3[8] = {1, 2, 3, 4, 5, 6, 7, 8}; | |
/* We use these strings for simple serialization and deserialization tests. */ | |
#define RAW_STRING_DATA \ | |
"<value><array><data>"CRLF \ | |
"<value><i4>2147483647</i4></value>"CRLF \ | |
"<value><i4>-2147483648</i4></value>"CRLF \ | |
"<value><boolean>0</boolean></value>"CRLF \ | |
"<value><boolean>1</boolean></value>"CRLF \ | |
"<value><string>Hello, world! <&></string></value>"CRLF \ | |
"<value><base64>"CRLF \ | |
"YmFzZTY0IGRhdGE="CRLF \ | |
"</base64></value>"CRLF \ | |
"<value><dateTime.iso8601>19980717T14:08:55</dateTime.iso8601></value>"CRLF \ | |
"<value><array><data>"CRLF \ | |
"</data></array></value>"CRLF \ | |
"</data></array></value>" | |
static char serialized_data[] = RAW_STRING_DATA; | |
static char serialized_struct[] = \ | |
"<value><struct>"CRLF \ | |
"<member><name><&></name>"CRLF \ | |
"<value><i4>10</i4></value></member>"CRLF \ | |
"</struct></value>"; | |
#define XML_PROLOGUE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"CRLF | |
static char serialized_call[] = | |
XML_PROLOGUE | |
"<methodCall>"CRLF | |
"<methodName>gloom&doom</methodName>"CRLF | |
"<params>"CRLF | |
"<param><value><i4>10</i4></value></param>"CRLF | |
"<param><value><i4>20</i4></value></param>"CRLF | |
"</params>"CRLF | |
"</methodCall>"CRLF; | |
static char serialized_response[] = | |
XML_PROLOGUE | |
"<methodResponse>"CRLF | |
"<params>"CRLF | |
"<param><value><i4>30</i4></value></param>"CRLF | |
"</params>"CRLF | |
"</methodResponse>"CRLF; | |
static char serialized_fault[] = | |
XML_PROLOGUE | |
"<methodResponse>"CRLF | |
"<fault>"CRLF | |
"<value><struct>"CRLF | |
"<member><name>faultCode</name>"CRLF | |
"<value><i4>6</i4></value></member>"CRLF | |
"<member><name>faultString</name>"CRLF | |
"<value><string>A fault occurred</string></value></member>"CRLF | |
"</struct></value>"CRLF | |
"</fault>"CRLF | |
"</methodResponse>"CRLF; | |
static char expat_data[] = XML_PROLOGUE RAW_STRING_DATA CRLF; | |
static char expat_error_data[] = \ | |
XML_PROLOGUE \ | |
"<foo><bar>abc</bar><baz></baz>"CRLF; | |
static char correct_value[] = \ | |
XML_PROLOGUE \ | |
"<methodResponse><params><param>"CRLF \ | |
"<value><array><data>"CRLF \ | |
RAW_STRING_DATA CRLF \ | |
"<value><int>1</int></value>"CRLF \ | |
"<value><double>-1.0</double></value>"CRLF \ | |
"<value><double>0.0</double></value>"CRLF \ | |
"<value><double>1.0</double></value>"CRLF \ | |
"<value><struct>"CRLF \ | |
"<member><name>ten <&></name>"CRLF \ | |
"<value><i4>10</i4></value></member>"CRLF \ | |
"<member><name>twenty</name>"CRLF \ | |
"<value><i4>20</i4></value></member>"CRLF \ | |
"</struct></value>"CRLF \ | |
"<value>Untagged string</value>"CRLF \ | |
"</data></array></value>"CRLF \ | |
"</param></params></methodResponse>"CRLF; | |
#define VALUE_HEADER \ | |
XML_PROLOGUE"<methodResponse><params><param><value>"CRLF | |
#define VALUE_FOOTER \ | |
"</value></param></params></methodResponse>"CRLF | |
#define MEMBER_HEADER \ | |
VALUE_HEADER"<struct><member>" | |
#define MEMBER_FOOTER \ | |
"</member></struct>"VALUE_FOOTER | |
#define ARBITRARY_VALUE \ | |
"<value><i4>0</i4></value>" | |
static char unparseable_value[] = VALUE_HEADER"<i4>"VALUE_FOOTER; | |
static char *(bad_values[]) = \ | |
{VALUE_HEADER"<i4>0</i4><i4>0</i4>"VALUE_FOOTER, | |
VALUE_HEADER"<foo></foo>"VALUE_FOOTER, | |
VALUE_HEADER"<i4><i4>4</i4></i4>"VALUE_FOOTER, | |
VALUE_HEADER"<i4>2147483648</i4>"VALUE_FOOTER, | |
VALUE_HEADER"<i4>-2147483649</i4>"VALUE_FOOTER, | |
VALUE_HEADER"<i4> 0</i4>"VALUE_FOOTER, | |
VALUE_HEADER"<i4>0 </i4>"VALUE_FOOTER, | |
VALUE_HEADER"<boolean>2</boolean>"VALUE_FOOTER, | |
VALUE_HEADER"<boolean>-1</boolean>"VALUE_FOOTER, | |
VALUE_HEADER"<double> 0.0</double>"VALUE_FOOTER, | |
VALUE_HEADER"<double>0.0 </double>"VALUE_FOOTER, | |
VALUE_HEADER"<array></array>"VALUE_FOOTER, | |
VALUE_HEADER"<array><data></data><data></data></array>"VALUE_FOOTER, | |
VALUE_HEADER"<array><data></data><data></data></array>"VALUE_FOOTER, | |
VALUE_HEADER"<array><data><foo></foo></data></array>"VALUE_FOOTER, | |
VALUE_HEADER"<struct><foo></foo></struct>"VALUE_FOOTER, | |
MEMBER_HEADER MEMBER_FOOTER, | |
MEMBER_HEADER"<name>a</name>"MEMBER_FOOTER, | |
MEMBER_HEADER"<name>a</name>"ARBITRARY_VALUE"<f></f>"MEMBER_FOOTER, | |
MEMBER_HEADER"<foo></foo>"ARBITRARY_VALUE MEMBER_FOOTER, | |
MEMBER_HEADER"<name>a</name><foo></foo>"MEMBER_FOOTER, | |
MEMBER_HEADER"<name><foo></foo></name>"ARBITRARY_VALUE MEMBER_FOOTER, | |
NULL}; | |
#define RESPONSE_HEADER \ | |
XML_PROLOGUE"<methodResponse>"CRLF | |
#define RESPONSE_FOOTER \ | |
"</methodResponse>"CRLF | |
#define PARAMS_RESP_HEADER \ | |
RESPONSE_HEADER"<params>" | |
#define PARAMS_RESP_FOOTER \ | |
"</params>"RESPONSE_FOOTER | |
#define FAULT_HEADER \ | |
RESPONSE_HEADER"<fault>" | |
#define FAULT_FOOTER \ | |
"</fault>"RESPONSE_FOOTER | |
#define FAULT_STRUCT_HEADER \ | |
FAULT_HEADER"<value><struct>" | |
#define FAULT_STRUCT_FOOTER \ | |
"</struct></value>"FAULT_FOOTER | |
static char *(bad_responses[]) = | |
{XML_PROLOGUE"<foo></foo>"CRLF, | |
RESPONSE_HEADER RESPONSE_FOOTER, | |
RESPONSE_HEADER"<params></params><params></params>"RESPONSE_FOOTER, | |
RESPONSE_HEADER"<foo></foo>"RESPONSE_FOOTER, | |
/* Make sure we insist on only one parameter in a response. */ | |
PARAMS_RESP_HEADER PARAMS_RESP_FOOTER, | |
PARAMS_RESP_HEADER | |
"<param><i4>0</i4></param>" | |
"<param><i4>0</i4></param>" | |
PARAMS_RESP_FOOTER, | |
/* Test other sorts of bad parameters. */ | |
PARAMS_RESP_HEADER"<foo></foo>"PARAMS_RESP_FOOTER, | |
PARAMS_RESP_HEADER"<param></param>"PARAMS_RESP_FOOTER, | |
PARAMS_RESP_HEADER"<param><foo></foo></param>"PARAMS_RESP_FOOTER, | |
PARAMS_RESP_HEADER | |
"<param>"ARBITRARY_VALUE ARBITRARY_VALUE"</param>" | |
PARAMS_RESP_FOOTER, | |
/* Basic fault tests. */ | |
FAULT_HEADER FAULT_FOOTER, | |
FAULT_HEADER"<foo></foo>"FAULT_FOOTER, | |
FAULT_HEADER"<value></value><value></value>"FAULT_FOOTER, | |
FAULT_HEADER"<value><i4>1</i4></value>"FAULT_FOOTER, | |
/* Make sure we insist on the proper members within the fault struct. */ | |
FAULT_STRUCT_HEADER | |
"<member><name>faultString</name>" | |
"<value><string>foo</string></value></member>" | |
FAULT_STRUCT_FOOTER, | |
FAULT_STRUCT_HEADER | |
"<member><name>faultCode</name>" | |
"<value><i4>0</i4></value></member>" | |
FAULT_STRUCT_FOOTER, | |
FAULT_STRUCT_HEADER | |
"<member><name>faultCode</name>" | |
"<value><i4>0</i4></value></member>" | |
"<member><name>faultString</name>" | |
"<value><i4>0</i4></value></member>" | |
FAULT_STRUCT_FOOTER, | |
FAULT_STRUCT_HEADER | |
"<member><name>faultCode</name>" | |
"<value><string>0</string></value></member>" | |
"<member><name>faultString</name>" | |
"<value><string>foo</string></value></member>" | |
FAULT_STRUCT_FOOTER, | |
NULL}; | |
#define CALL_HEADER \ | |
XML_PROLOGUE"<methodCall>"CRLF | |
#define CALL_FOOTER \ | |
"</methodCall>"CRLF | |
static char *(bad_calls[]) = | |
{XML_PROLOGUE"<foo></foo>"CRLF, | |
CALL_HEADER CALL_FOOTER, | |
CALL_HEADER"<methodName>m</methodName><foo></foo>"CALL_FOOTER, | |
CALL_HEADER"<foo></foo><params></params>"CALL_FOOTER, | |
CALL_HEADER"<methodName><f></f></methodName><params></params>"CALL_FOOTER, | |
NULL}; | |
/*========================================================================= | |
** Test Suites | |
**========================================================================= | |
*/ | |
static void test_env(void) | |
{ | |
xmlrpc_env env, env2; | |
char *s; | |
/* Test xmlrpc_env_init. */ | |
xmlrpc_env_init(&env); | |
TEST(!env.fault_occurred); | |
TEST(env.fault_code == 0); | |
TEST(env.fault_string == NULL); | |
/* Test xmlrpc_set_fault. */ | |
xmlrpc_env_set_fault(&env, 1, test_string_1); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == 1); | |
TEST(env.fault_string != test_string_1); | |
TEST(strcmp(env.fault_string, test_string_1) == 0); | |
/* Change an existing fault. */ | |
xmlrpc_env_set_fault(&env, 2, test_string_2); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == 2); | |
TEST(strcmp(env.fault_string, test_string_2) == 0); | |
/* Set a fault with a format string. */ | |
xmlrpc_env_set_fault_formatted(&env, 3, "a%s%d", "bar", 9); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == 3); | |
TEST(strcmp(env.fault_string, "abar9") == 0); | |
/* Set a fault with an oversized string. */ | |
s = "12345678901234567890123456789012345678901234567890"; | |
xmlrpc_env_set_fault_formatted(&env, 4, "%s%s%s%s%s%s", s, s, s, s, s, s); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == 4); | |
TEST(strlen(env.fault_string) == 255); | |
/* Test cleanup code (with help from memprof). */ | |
xmlrpc_env_clean(&env); | |
/* Test cleanup code on in absence of xmlrpc_env_set_fault. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_env_clean(&env2); | |
} | |
static void test_mem_block (void) | |
{ | |
xmlrpc_env env; | |
xmlrpc_mem_block* block; | |
xmlrpc_mem_block* typed_heap_block; | |
xmlrpc_mem_block typed_auto_block; | |
void** typed_contents; | |
xmlrpc_env_init(&env); | |
/* Allocate a zero-size block. */ | |
block = xmlrpc_mem_block_new(&env, 0); | |
TEST_NO_FAULT(&env); | |
TEST(block != NULL); | |
TEST(xmlrpc_mem_block_size(block) == 0); | |
/* Grow the block a little bit. */ | |
xmlrpc_mem_block_resize(&env, block, strlen(test_string_1) + 1); | |
TEST_NO_FAULT(&env); | |
TEST(xmlrpc_mem_block_size(block) == strlen(test_string_1) + 1); | |
/* Insert a string into the block, and resize it by large amount. | |
** We want to cause a reallocation and copy of the block contents. */ | |
strcpy(xmlrpc_mem_block_contents(block), test_string_1); | |
xmlrpc_mem_block_resize(&env, block, 10000); | |
TEST_NO_FAULT(&env); | |
TEST(xmlrpc_mem_block_size(block) == 10000); | |
TEST(strcmp(xmlrpc_mem_block_contents(block), test_string_1) == 0); | |
/* Test cleanup code (with help from memprof). */ | |
xmlrpc_mem_block_free(block); | |
/* Allocate a bigger block. */ | |
block = xmlrpc_mem_block_new(&env, 128); | |
TEST_NO_FAULT(&env); | |
TEST(block != NULL); | |
TEST(xmlrpc_mem_block_size(block) == 128); | |
/* Test cleanup code (with help from memprof). */ | |
xmlrpc_mem_block_free(block); | |
/* Allocate a "typed" memory block. */ | |
typed_heap_block = XMLRPC_TYPED_MEM_BLOCK_NEW(void*, &env, 20); | |
TEST_NO_FAULT(&env); | |
TEST(typed_heap_block != NULL); | |
TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(void*, typed_heap_block) == 20); | |
typed_contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(void*, typed_heap_block); | |
TEST(typed_contents != NULL); | |
/* Resize a typed memory block. */ | |
XMLRPC_TYPED_MEM_BLOCK_RESIZE(void*, &env, typed_heap_block, 100); | |
TEST_NO_FAULT(&env); | |
TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(void*, typed_heap_block) == 100); | |
/* Test cleanup code (with help from memprof). */ | |
XMLRPC_TYPED_MEM_BLOCK_FREE(void*, typed_heap_block); | |
/* Test _INIT and _CLEAN for stack-based memory blocks. */ | |
XMLRPC_TYPED_MEM_BLOCK_INIT(void*, &env, &typed_auto_block, 30); | |
TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(void*, &typed_auto_block) == 30); | |
XMLRPC_TYPED_MEM_BLOCK_CLEAN(void*, &typed_auto_block); | |
/* Test xmlrpc_mem_block_append. */ | |
block = XMLRPC_TYPED_MEM_BLOCK_NEW(int, &env, 5); | |
TEST_NO_FAULT(&env); | |
memcpy(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(int, block), | |
test_int_array_1, sizeof(test_int_array_1)); | |
XMLRPC_TYPED_MEM_BLOCK_APPEND(int, &env, block, test_int_array_2, 3); | |
TEST(XMLRPC_TYPED_MEM_BLOCK_SIZE(int, block) == 8); | |
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(int, block), | |
test_int_array_3, sizeof(test_int_array_3)) == 0); | |
XMLRPC_TYPED_MEM_BLOCK_FREE(int, block); | |
xmlrpc_env_clean(&env); | |
} | |
static char *(base64_triplets[]) = { | |
"", "", CRLF, | |
"a", "YQ==", "YQ=="CRLF, | |
"aa", "YWE=", "YWE="CRLF, | |
"aaa", "YWFh", "YWFh"CRLF, | |
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", | |
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY" | |
"2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", | |
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY" | |
"2Rl"CRLF | |
"ZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo="CRLF, | |
NULL}; | |
static void test_base64_conversion (void) | |
{ | |
xmlrpc_env env, env2; | |
char **triplet, *bin_data, *nocrlf_ascii_data, *ascii_data; | |
xmlrpc_mem_block *output; | |
xmlrpc_env_init(&env); | |
for (triplet = base64_triplets; *triplet != NULL; triplet += 3) { | |
bin_data = *triplet; | |
nocrlf_ascii_data = *(triplet + 1); | |
ascii_data = *(triplet + 2); | |
/* Test our encoding routine. */ | |
output = xmlrpc_base64_encode(&env, | |
(unsigned char*) bin_data, | |
strlen(bin_data)); | |
TEST_NO_FAULT(&env); | |
TEST(output != NULL); | |
TEST(xmlrpc_mem_block_size(output) == strlen(ascii_data)); | |
TEST(memcmp(xmlrpc_mem_block_contents(output), ascii_data, | |
strlen(ascii_data)) == 0); | |
xmlrpc_mem_block_free(output); | |
/* Test our newline-free encoding routine. */ | |
output = | |
xmlrpc_base64_encode_without_newlines(&env, | |
(unsigned char*) bin_data, | |
strlen(bin_data)); | |
TEST_NO_FAULT(&env); | |
TEST(output != NULL); | |
TEST(xmlrpc_mem_block_size(output) == strlen(nocrlf_ascii_data)); | |
TEST(memcmp(xmlrpc_mem_block_contents(output), nocrlf_ascii_data, | |
strlen(nocrlf_ascii_data)) == 0); | |
xmlrpc_mem_block_free(output); | |
/* Test our decoding routine. */ | |
output = xmlrpc_base64_decode(&env, ascii_data, strlen(ascii_data)); | |
TEST_NO_FAULT(&env); | |
TEST(output != NULL); | |
TEST(xmlrpc_mem_block_size(output) == strlen(bin_data)); | |
TEST(memcmp(xmlrpc_mem_block_contents(output), bin_data, | |
strlen(bin_data)) == 0); | |
xmlrpc_mem_block_free(output); | |
} | |
/* Now for something broken... */ | |
xmlrpc_env_init(&env2); | |
output = xmlrpc_base64_decode(&env2, "====", 4); | |
TEST(output == NULL); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_PARSE_ERROR); | |
xmlrpc_env_clean(&env2); | |
/* Now for something broken in a really sneaky way... */ | |
xmlrpc_env_init(&env2); | |
output = xmlrpc_base64_decode(&env2, "a==", 4); | |
TEST(output == NULL); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_PARSE_ERROR); | |
xmlrpc_env_clean(&env2); | |
xmlrpc_env_clean(&env); | |
} | |
static void test_value (void) | |
{ | |
xmlrpc_env env, env2; | |
xmlrpc_value *v, *v2, *v3, *item; | |
xmlrpc_int32 i, i1, i2, i3, i4; | |
xmlrpc_bool b; | |
double d; | |
char *str; | |
size_t len; | |
void *ptr; | |
unsigned char* data; | |
xmlrpc_env_init(&env); | |
/* Test allocation and deallocation (w/memprof). */ | |
v = xmlrpc_build_value(&env, "i", (xmlrpc_int32) 5); | |
TEST_NO_FAULT(&env); | |
TEST(v != NULL); | |
xmlrpc_INCREF(v); | |
xmlrpc_DECREF(v); | |
xmlrpc_DECREF(v); | |
/* Test integers. */ | |
v = xmlrpc_build_value(&env, "i", (xmlrpc_int32) 10); | |
TEST_NO_FAULT(&env); | |
TEST(v != NULL); | |
TEST(XMLRPC_TYPE_INT == xmlrpc_value_type(v)); | |
xmlrpc_parse_value(&env, v, "i", &i); | |
TEST_NO_FAULT(&env); | |
TEST(i == 10); | |
xmlrpc_DECREF(v); | |
/* Test booleans. */ | |
v = xmlrpc_build_value(&env, "b", (xmlrpc_bool) 1); | |
TEST_NO_FAULT(&env); | |
TEST(v != NULL); | |
TEST(XMLRPC_TYPE_BOOL == xmlrpc_value_type(v)); | |
xmlrpc_parse_value(&env, v, "b", &b); | |
TEST_NO_FAULT(&env); | |
TEST(b); | |
xmlrpc_DECREF(v); | |
/* Test doubles. */ | |
v = xmlrpc_build_value(&env, "d", 1.0); | |
TEST_NO_FAULT(&env); | |
TEST(v != NULL); | |
TEST(XMLRPC_TYPE_DOUBLE == xmlrpc_value_type(v)); | |
xmlrpc_parse_value(&env, v, "d", &d); | |
TEST_NO_FAULT(&env); | |
TEST(d == 1.0); | |
xmlrpc_DECREF(v); | |
/* Test strings (without '\0' bytes). */ | |
v = xmlrpc_build_value(&env, "s", test_string_1); | |
TEST_NO_FAULT(&env); | |
TEST(v != NULL); | |
TEST(XMLRPC_TYPE_STRING == xmlrpc_value_type(v)); | |
xmlrpc_parse_value(&env, v, "s", &str); | |
TEST_NO_FAULT(&env); | |
TEST(strcmp(str, test_string_1) == 0); | |
xmlrpc_parse_value(&env, v, "s#", &str, &len); | |
TEST_NO_FAULT(&env); | |
TEST(memcmp(str, test_string_1, strlen(test_string_1)) == 0); | |
TEST(strlen(str) == strlen(test_string_1)); | |
xmlrpc_DECREF(v); | |
/* Test a string with a '\0' byte. */ | |
v = xmlrpc_build_value(&env, "s#", "foo\0bar", (size_t) 7); | |
TEST_NO_FAULT(&env); | |
TEST(v != NULL); | |
TEST(XMLRPC_TYPE_STRING == xmlrpc_value_type(v)); | |
xmlrpc_parse_value(&env, v, "s#", &str, &len); | |
TEST_NO_FAULT(&env); | |
TEST(memcmp(str, "foo\0bar", 7) == 0); | |
TEST(len == 7); | |
/* Test for type error when decoding a string with a zero byte to a | |
** regular C string. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_parse_value(&env2, v, "s", &str); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_env_clean(&env2); | |
xmlrpc_DECREF(v); | |
/* Test for one, simple kind of type mismatch error. We assume that | |
** if one of these typechecks works, the rest work fine. */ | |
xmlrpc_env_init(&env2); | |
v = xmlrpc_build_value(&env, "i", (xmlrpc_int32) 5); | |
TEST_NO_FAULT(&env); | |
xmlrpc_parse_value(&env2, v, "s", &str); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_DECREF(v); | |
xmlrpc_env_clean(&env2); | |
/* Test 'V' with building and parsing. */ | |
v2 = xmlrpc_build_value(&env, "i", (xmlrpc_int32) 5); | |
TEST_NO_FAULT(&env); | |
v = xmlrpc_build_value(&env, "V", v2); | |
TEST_NO_FAULT(&env); | |
TEST(v == v2); | |
xmlrpc_parse_value(&env, v2, "V", &v3); | |
TEST_NO_FAULT(&env); | |
TEST(v2 == v3); | |
xmlrpc_DECREF(v); | |
xmlrpc_DECREF(v2); | |
/* Basic array-building test. */ | |
v = xmlrpc_build_value(&env, "()"); | |
TEST_NO_FAULT(&env); | |
TEST(XMLRPC_TYPE_ARRAY == xmlrpc_value_type(v)); | |
len = xmlrpc_array_size(&env, v); | |
TEST_NO_FAULT(&env); | |
TEST(len == 0); | |
xmlrpc_DECREF(v); | |
/* A more complex array. */ | |
v = xmlrpc_build_value(&env, "(i(ii)i)", | |
(xmlrpc_int32) 10, (xmlrpc_int32) 20, | |
(xmlrpc_int32) 30, (xmlrpc_int32) 40); | |
TEST_NO_FAULT(&env); | |
TEST(XMLRPC_TYPE_ARRAY == xmlrpc_value_type(v)); | |
len = xmlrpc_array_size(&env, v); | |
TEST_NO_FAULT(&env); | |
TEST(len == 3); | |
item = xmlrpc_array_get_item(&env, v, 1); | |
TEST_NO_FAULT(&env); | |
len = xmlrpc_array_size(&env, item); | |
TEST_NO_FAULT(&env); | |
TEST(len == 2); | |
item = xmlrpc_array_get_item(&env, item, 0); | |
TEST_NO_FAULT(&env); | |
xmlrpc_parse_value(&env, item, "i", &i); | |
TEST_NO_FAULT(&env); | |
TEST(i == 20); | |
xmlrpc_parse_value(&env, v, "(i(ii)i)", &i1, &i2, &i3, &i4); | |
TEST_NO_FAULT(&env); | |
TEST(i1 == 10 && i2 == 20 && i3 == 30 && i4 == 40); | |
xmlrpc_parse_value(&env, v, "(i(i*)i)", &i1, &i2, &i3); | |
TEST_NO_FAULT(&env); | |
TEST(i1 == 10 && i2 == 20 && i3 == 40); | |
xmlrpc_parse_value(&env, v, "(i(ii*)i)", &i1, &i2, &i3, &i4); | |
TEST_NO_FAULT(&env); | |
xmlrpc_DECREF(v); | |
/* Test parsing of 'A' and 'S'. */ | |
v = xmlrpc_build_value(&env, "((){})"); | |
TEST_NO_FAULT(&env); | |
xmlrpc_parse_value(&env, v, "(AS)", &v2, &v3); | |
TEST_NO_FAULT(&env); | |
TEST(XMLRPC_TYPE_ARRAY == xmlrpc_value_type(v2)); | |
TEST(XMLRPC_TYPE_STRUCT == xmlrpc_value_type(v3)); | |
len = xmlrpc_array_size(&env, v2); | |
TEST_NO_FAULT(&env); | |
TEST(len == 0); | |
len = xmlrpc_struct_size(&env, v3); | |
TEST_NO_FAULT(&env); | |
TEST(len == 0); | |
xmlrpc_DECREF(v); | |
/* Test typechecks for 'A' and 'S'. */ | |
v = xmlrpc_build_value(&env, "s", "foo"); | |
TEST_NO_FAULT(&env); | |
xmlrpc_env_init(&env2); | |
xmlrpc_parse_value(&env2, v, "A", &v2); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_env_clean(&env2); | |
xmlrpc_env_init(&env2); | |
xmlrpc_parse_value(&env2, v, "S", &v2); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_env_clean(&env2); | |
xmlrpc_DECREF(v); | |
/* Test C pointer storage using 'p'. | |
** We don't support cleanup functions (yet). */ | |
v = xmlrpc_build_value(&env, "p", (void*) 0x00000017); | |
TEST_NO_FAULT(&env); | |
TEST(XMLRPC_TYPE_C_PTR == xmlrpc_value_type(v)); | |
xmlrpc_parse_value(&env, v, "p", &ptr); | |
TEST_NO_FAULT(&env); | |
TEST(ptr == (void*) 0x00000017); | |
xmlrpc_DECREF(v); | |
/* Test <base64> data. */ | |
v = xmlrpc_build_value(&env, "6", "a\0b", (size_t) 3); | |
TEST_NO_FAULT(&env); | |
TEST(XMLRPC_TYPE_BASE64 == xmlrpc_value_type(v)); | |
xmlrpc_parse_value(&env, v, "6", &data, &len); | |
TEST_NO_FAULT(&env); | |
TEST(len == 3); | |
TEST(memcmp(data, "a\0b", len) == 0); | |
xmlrpc_DECREF(v); | |
xmlrpc_env_clean(&env); | |
} | |
static void test_bounds_checks (void) | |
{ | |
xmlrpc_env env; | |
xmlrpc_value *array; | |
int i1, i2, i3, i4; | |
/* Get an array to work with. */ | |
xmlrpc_env_init(&env); | |
array = xmlrpc_build_value(&env, "(iii)", 100, 200, 300); | |
TEST_NO_FAULT(&env); | |
xmlrpc_env_clean(&env); | |
/* Test bounds check on xmlrpc_array_get_item. */ | |
xmlrpc_env_init(&env); | |
xmlrpc_array_get_item(&env, array, 3); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == XMLRPC_INDEX_ERROR); | |
xmlrpc_env_clean(&env); | |
/* Test xmlrpc_parse_value with too few values. */ | |
xmlrpc_env_init(&env); | |
xmlrpc_parse_value(&env, array, "(iiii)", &i1, &i2, &i3, &i4); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == XMLRPC_INDEX_ERROR); | |
xmlrpc_env_clean(&env); | |
/* Test xmlrpc_parse_value with too many values. */ | |
xmlrpc_env_init(&env); | |
xmlrpc_parse_value(&env, array, "(ii)", &i1, &i2, &i3, &i4); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == XMLRPC_INDEX_ERROR); | |
xmlrpc_env_clean(&env); | |
/* Dispose of our array. */ | |
xmlrpc_DECREF(array); | |
} | |
static void test_struct (void) | |
{ | |
xmlrpc_env env, env2; | |
xmlrpc_value *s, *i, *i1, *i2, *i3, *key, *value; | |
size_t size; | |
int present; | |
xmlrpc_int32 ival; | |
xmlrpc_bool bval; | |
char *sval; | |
int index; | |
xmlrpc_env_init(&env); | |
/* Create a struct. */ | |
s = xmlrpc_struct_new(&env); | |
TEST_NO_FAULT(&env); | |
TEST(s != NULL); | |
TEST(XMLRPC_TYPE_STRUCT == xmlrpc_value_type(s)); | |
size = xmlrpc_struct_size(&env, s); | |
TEST_NO_FAULT(&env); | |
TEST(size == 0); | |
/* Create some elements to insert into our struct. */ | |
i1 = xmlrpc_build_value(&env, "s", "Item #1"); | |
TEST_NO_FAULT(&env); | |
i2 = xmlrpc_build_value(&env, "s", "Item #2"); | |
TEST_NO_FAULT(&env); | |
i3 = xmlrpc_build_value(&env, "s", "Item #3"); | |
TEST_NO_FAULT(&env); | |
/* Insert a single item. */ | |
xmlrpc_struct_set_value(&env, s, "foo", i1); | |
TEST_NO_FAULT(&env); | |
size = xmlrpc_struct_size(&env, s); | |
TEST_NO_FAULT(&env); | |
TEST(size == 1); | |
/* Insert two more items with conflicting hash codes. (We assume that | |
** nobody has changed the hash function.) */ | |
xmlrpc_struct_set_value(&env, s, "bar", i2); | |
TEST_NO_FAULT(&env); | |
xmlrpc_struct_set_value(&env, s, "aas", i3); | |
TEST_NO_FAULT(&env); | |
size = xmlrpc_struct_size(&env, s); | |
TEST_NO_FAULT(&env); | |
TEST(size == 3); | |
/* Replace an existing element with a different element. */ | |
xmlrpc_struct_set_value(&env, s, "aas", i1); | |
TEST_NO_FAULT(&env); | |
size = xmlrpc_struct_size(&env, s); | |
TEST_NO_FAULT(&env); | |
TEST(size == 3); | |
/* Get an element. */ | |
i = xmlrpc_struct_get_value(&env, s, "aas"); | |
TEST_NO_FAULT(&env); | |
TEST(i == i1); | |
/* Replace an existing element with the same element (tricky). */ | |
xmlrpc_struct_set_value(&env, s, "aas", i1); | |
TEST_NO_FAULT(&env); | |
size = xmlrpc_struct_size(&env, s); | |
TEST_NO_FAULT(&env); | |
TEST(size == 3); | |
i = xmlrpc_struct_get_value(&env, s, "aas"); | |
TEST_NO_FAULT(&env); | |
TEST(i == i1); | |
/* Test for the presence and absence of elements. */ | |
present = xmlrpc_struct_has_key(&env, s, "aas"); | |
TEST_NO_FAULT(&env); | |
TEST(present); | |
present = xmlrpc_struct_has_key(&env, s, "bogus"); | |
TEST_NO_FAULT(&env); | |
TEST(!present); | |
/* Make sure our typechecks work correctly. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_struct_size(&env2, i1); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_env_clean(&env2); | |
xmlrpc_env_init(&env2); | |
xmlrpc_struct_has_key(&env2, i1, "foo"); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_env_clean(&env2); | |
xmlrpc_env_init(&env2); | |
i = xmlrpc_struct_get_value(&env2, i1, "foo"); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_env_clean(&env2); | |
xmlrpc_env_init(&env2); | |
xmlrpc_struct_set_value(&env2, i1, "foo", i2); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_env_clean(&env2); | |
xmlrpc_env_init(&env2); | |
xmlrpc_struct_set_value_v(&env2, s, s, i2); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_env_clean(&env2); | |
/* Attempt to access a non-existant element. */ | |
xmlrpc_env_init(&env2); | |
i = xmlrpc_struct_get_value(&env2, s, "bogus"); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_INDEX_ERROR); | |
xmlrpc_env_clean(&env2); | |
/* Test cleanup code (w/memprof). */ | |
xmlrpc_DECREF(s); | |
/* Build a struct using our automagic struct builder. */ | |
s = xmlrpc_build_value(&env, "{s:s,s:i,s:b}", | |
"foo", "Hello!", | |
"bar", (xmlrpc_int32) 1, | |
"baz", (xmlrpc_bool) 0); | |
TEST_NO_FAULT(&env); | |
TEST(s != NULL); | |
TEST(XMLRPC_TYPE_STRUCT == xmlrpc_value_type(s)); | |
size = xmlrpc_struct_size(&env, s); | |
TEST_NO_FAULT(&env); | |
TEST(size == 3); | |
present = xmlrpc_struct_has_key(&env, s, "foo"); | |
TEST_NO_FAULT(&env); | |
TEST(present); | |
present = xmlrpc_struct_has_key(&env, s, "bar"); | |
TEST_NO_FAULT(&env); | |
TEST(present); | |
present = xmlrpc_struct_has_key(&env, s, "baz"); | |
TEST_NO_FAULT(&env); | |
TEST(present); | |
i = xmlrpc_struct_get_value(&env, s, "baz"); | |
TEST_NO_FAULT(&env); | |
xmlrpc_parse_value(&env, i, "b", &bval); | |
TEST_NO_FAULT(&env); | |
TEST(!bval); | |
/* Extract keys and values. */ | |
for (index = 0; index < 3; index++) { | |
xmlrpc_struct_get_key_and_value(&env, s, index, &key, &value); | |
TEST_NO_FAULT(&env); | |
TEST(key != NULL); | |
TEST(value != NULL); | |
} | |
/* Test our automagic struct parser. */ | |
xmlrpc_parse_value(&env, s, "{s:b,s:s,s:i,*}", | |
"baz", &bval, | |
"foo", &sval, | |
"bar", &ival); | |
TEST_NO_FAULT(&env); | |
TEST(ival == 1); | |
TEST(!bval); | |
TEST(strcmp(sval, "Hello!") == 0); | |
/* Test automagic struct parser with value of wrong type. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_parse_value(&env2, s, "{s:b,s:i,*}", | |
"baz", &bval, | |
"foo", &sval); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
xmlrpc_env_clean(&env2); | |
/* Test automagic struct parser with bad key. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_parse_value(&env2, s, "{s:b,s:i,*}", | |
"baz", &bval, | |
"nosuch", &sval); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_INDEX_ERROR); | |
xmlrpc_env_clean(&env2); | |
/* Test type check. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_struct_get_key_and_value(&env2, i1, 0, &key, &value); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_TYPE_ERROR); | |
TEST(key == NULL && value == NULL); | |
xmlrpc_env_clean(&env2); | |
/* Test bounds checks. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_struct_get_key_and_value(&env2, s, -1, &key, &value); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_INDEX_ERROR); | |
TEST(key == NULL && value == NULL); | |
xmlrpc_env_clean(&env2); | |
xmlrpc_env_init(&env2); | |
xmlrpc_struct_get_key_and_value(&env2, s, 3, &key, &value); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_INDEX_ERROR); | |
TEST(key == NULL && value == NULL); | |
xmlrpc_env_clean(&env2); | |
/* Test cleanup code (w/memprof). */ | |
xmlrpc_DECREF(s); | |
xmlrpc_DECREF(i1); | |
xmlrpc_DECREF(i2); | |
xmlrpc_DECREF(i3); | |
xmlrpc_env_clean(&env); | |
} | |
static void test_serialize (void) | |
{ | |
xmlrpc_env env, fault; | |
xmlrpc_value *v; | |
xmlrpc_mem_block *output; | |
size_t size; | |
xmlrpc_env_init(&env); | |
/* Build a nice, messy value to serialize. We should attempt to use | |
** use every data type except double (which doesn't serialize in a | |
** portable manner. */ | |
v = xmlrpc_build_value(&env, "(iibbs68())", | |
(xmlrpc_int32) INT_MAX, (xmlrpc_int32) INT_MIN, | |
(xmlrpc_bool) 0, (xmlrpc_bool) 1, | |
"Hello, world! <&>", | |
"base64 data", (size_t) 11, | |
"19980717T14:08:55"); | |
TEST_NO_FAULT(&env); | |
/* Serialize the value. */ | |
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0); | |
TEST_NO_FAULT(&env); | |
xmlrpc_serialize_value(&env, output, v); | |
TEST_NO_FAULT(&env); | |
/* Make sure we serialized the correct value. */ | |
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output); | |
TEST(size == strlen(serialized_data)); | |
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output), | |
serialized_data, size) == 0); | |
/* (Debugging code to display the value.) */ | |
/* XMLRPC_TYPED_MEM_BLOCK_APPEND(char, &env, output, "\0", 1); | |
** TEST_NO_FAULT(&env); | |
** printf("%s\n", XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output)); */ | |
/* Clean up our value. */ | |
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output); | |
xmlrpc_DECREF(v); | |
/* Serialize a simple struct. */ | |
v = xmlrpc_build_value(&env, "{s:i}", "<&>", (xmlrpc_int32) 10); | |
TEST_NO_FAULT(&env); | |
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0); | |
TEST_NO_FAULT(&env); | |
xmlrpc_serialize_value(&env, output, v); | |
TEST_NO_FAULT(&env); | |
/* Make sure we serialized the correct value. */ | |
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output); | |
TEST(size == strlen(serialized_struct)); | |
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output), | |
serialized_struct, size) == 0); | |
/* Clean up our struct. */ | |
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output); | |
xmlrpc_DECREF(v); | |
/* Serialize a methodResponse. */ | |
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0); | |
TEST_NO_FAULT(&env); | |
v = xmlrpc_build_value(&env, "i", (xmlrpc_int32) 30); | |
TEST_NO_FAULT(&env); | |
xmlrpc_serialize_response(&env, output, v); | |
TEST_NO_FAULT(&env); | |
/* Make sure we serialized the correct value. */ | |
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output); | |
TEST(size == strlen(serialized_response)); | |
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output), | |
serialized_response, size) == 0); | |
/* Clean up our methodResponse. */ | |
xmlrpc_DECREF(v); | |
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output); | |
/* Serialize a methodCall. */ | |
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0); | |
TEST_NO_FAULT(&env); | |
v = xmlrpc_build_value(&env, "(ii)", (xmlrpc_int32) 10, (xmlrpc_int32) 20); | |
TEST_NO_FAULT(&env); | |
xmlrpc_serialize_call(&env, output, "gloom&doom", v); | |
TEST_NO_FAULT(&env); | |
/* Make sure we serialized the correct value. */ | |
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output); | |
TEST(size == strlen(serialized_call)); | |
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output), | |
serialized_call, size) == 0); | |
/* Clean up our methodCall. */ | |
xmlrpc_DECREF(v); | |
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output); | |
/* Serialize a fault. */ | |
output = XMLRPC_TYPED_MEM_BLOCK_NEW(char, &env, 0); | |
TEST_NO_FAULT(&env); | |
xmlrpc_env_init(&fault); | |
xmlrpc_env_set_fault(&fault, 6, "A fault occurred"); | |
xmlrpc_serialize_fault(&env, output, &fault); | |
TEST_NO_FAULT(&env); | |
/* Make sure we serialized the correct value. */ | |
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output); | |
TEST(size == strlen(serialized_fault)); | |
TEST(memcmp(XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output), | |
serialized_fault, size) == 0); | |
/* Clean up our fault. */ | |
xmlrpc_env_clean(&fault); | |
XMLRPC_TYPED_MEM_BLOCK_FREE(char, output); | |
xmlrpc_env_clean(&env); | |
} | |
static void test_expat (void) | |
{ | |
xmlrpc_env env; | |
xml_element *elem, *array, *data, *value1, *i4; | |
char *cdata; | |
size_t size; | |
xmlrpc_env_init(&env); | |
/* Parse a moderately complex XML document. */ | |
elem = xml_parse(&env, expat_data, strlen(expat_data)); | |
TEST_NO_FAULT(&env); | |
TEST(elem != NULL); | |
/* Verify our results. */ | |
TEST(strcmp(xml_element_name(elem), "value") == 0); | |
TEST(xml_element_children_size(elem) == 1); | |
array = xml_element_children(elem)[0]; | |
TEST(strcmp(xml_element_name(array), "array") == 0); | |
TEST(xml_element_children_size(array) == 1); | |
data = xml_element_children(array)[0]; | |
TEST(strcmp(xml_element_name(data), "data") == 0); | |
TEST(xml_element_children_size(data) > 1); | |
value1 = xml_element_children(data)[0]; | |
TEST(strcmp(xml_element_name(value1), "value") == 0); | |
TEST(xml_element_children_size(value1) == 1); | |
i4 = xml_element_children(value1)[0]; | |
TEST(strcmp(xml_element_name(i4), "i4") == 0); | |
TEST(xml_element_children_size(i4) == 0); | |
cdata = xml_element_cdata(i4); | |
size = xml_element_cdata_size(i4); | |
TEST(size == strlen("2147483647")); | |
TEST(memcmp(cdata, "2147483647", strlen("2147483647")) == 0); | |
/* Test cleanup code (w/memprof). */ | |
xml_element_free(elem); | |
/* Try to parse broken XML. We want to know that a proper error occurs, | |
** AND that we don't leak any memory (w/memprof). */ | |
elem = xml_parse(&env, expat_error_data, strlen(expat_error_data)); | |
TEST(env.fault_occurred); | |
TEST(elem == NULL); | |
xmlrpc_env_clean(&env); | |
} | |
static void test_parse_xml_value (void) | |
{ | |
xmlrpc_env env, env2; | |
xmlrpc_value *val, *s, *sval; | |
xmlrpc_int32 int_max, int_min, int_one; | |
xmlrpc_bool bool_false, bool_true; | |
char *str_hello, *str_untagged, *datetime; | |
unsigned char *b64_data; | |
size_t b64_len; | |
double negone, zero, one; | |
int size, sval_int; | |
char **bad_value; | |
xml_element *elem; | |
xmlrpc_env_init(&env); | |
/* Parse a correctly-formed response. */ | |
val = xmlrpc_parse_response(&env, correct_value, | |
strlen(correct_value)); | |
TEST_NO_FAULT(&env); | |
TEST(val != NULL); | |
/* Analyze it and make sure it contains the correct values. */ | |
xmlrpc_parse_value(&env, val, "((iibbs68())idddSs)", &int_max, &int_min, | |
&bool_false, &bool_true, &str_hello, | |
&b64_data, &b64_len, &datetime, | |
&int_one, &negone, &zero, &one, &s, &str_untagged); | |
TEST_NO_FAULT(&env); | |
TEST(int_max == INT_MAX); | |
TEST(int_min == INT_MIN); | |
TEST(!bool_false); | |
TEST(bool_true); | |
TEST(strlen(str_hello) == strlen("Hello, world! <&>")); | |
TEST(strcmp(str_hello, "Hello, world! <&>") == 0); | |
TEST(b64_len == 11); | |
TEST(memcmp(b64_data, "base64 data", b64_len) == 0); | |
TEST(strcmp(datetime, "19980717T14:08:55") == 0); | |
TEST(int_one == 1); | |
TEST(negone == -1.0); | |
TEST(zero == 0.0); | |
TEST(one == 1.0); | |
TEST(strcmp(str_untagged, "Untagged string") == 0); | |
/* Analyze the contents of our struct. */ | |
TEST(s != NULL); | |
size = xmlrpc_struct_size(&env, s); | |
TEST_NO_FAULT(&env); | |
TEST(size == 2); | |
sval = xmlrpc_struct_get_value(&env, s, "ten <&>"); | |
TEST_NO_FAULT(&env); | |
xmlrpc_parse_value(&env, sval, "i", &sval_int); | |
TEST_NO_FAULT(&env); | |
TEST(sval_int == 10); | |
sval = xmlrpc_struct_get_value(&env, s, "twenty"); | |
TEST_NO_FAULT(&env); | |
xmlrpc_parse_value(&env, sval, "i", &sval_int); | |
TEST_NO_FAULT(&env); | |
TEST(sval_int == 20); | |
/* Test cleanup code (w/memprof). */ | |
xmlrpc_DECREF(val); | |
/* Test our error-checking code. This is exposed to potentially-malicious | |
** network data, so we need to handle evil data gracefully, without | |
** barfing or leaking memory. (w/memprof) */ | |
/* First, test some poorly-formed XML data. */ | |
xmlrpc_env_init(&env2); | |
val = xmlrpc_parse_response(&env2, unparseable_value, | |
strlen(unparseable_value)); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_PARSE_ERROR); | |
TEST(val == NULL); | |
xmlrpc_env_clean(&env2); | |
/* Next, check for bogus values. These are all well-formed XML, but | |
** they aren't legal XML-RPC. */ | |
for (bad_value = bad_values; *bad_value != NULL; bad_value++) { | |
/* First, check to make sure that our test case is well-formed XML. | |
** (It's easy to make mistakes when writing the test cases!) */ | |
elem = xml_parse(&env, *bad_value, strlen(*bad_value)); | |
TEST_NO_FAULT(&env); | |
xml_element_free(elem); | |
/* Now, make sure the higher-level routine barfs appropriately. */ | |
xmlrpc_env_init(&env2); | |
val = xmlrpc_parse_response(&env2, *bad_value, strlen(*bad_value)); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_PARSE_ERROR); | |
TEST(val == NULL); | |
xmlrpc_env_clean(&env2); | |
} | |
xmlrpc_env_clean(&env); | |
} | |
static void test_parse_xml_response (void) | |
{ | |
xmlrpc_env env, env2, fault; | |
xmlrpc_value *v; | |
int i1; | |
char **bad_resp; | |
xml_element *elem; | |
xmlrpc_env_init(&env); | |
/* Parse a valid response. */ | |
v = xmlrpc_parse_response(&env, serialized_response, | |
strlen(serialized_response)); | |
TEST_NO_FAULT(&env); | |
TEST(v != NULL); | |
xmlrpc_parse_value(&env, v, "i", &i1); | |
TEST_NO_FAULT(&env); | |
TEST(i1 == 30); | |
xmlrpc_DECREF(v); | |
/* Parse a valid fault. */ | |
xmlrpc_env_init(&fault); | |
v = xmlrpc_parse_response(&fault, serialized_fault, | |
strlen(serialized_fault)); | |
TEST(fault.fault_occurred); | |
TEST(fault.fault_code == 6); | |
TEST(strcmp(fault.fault_string, "A fault occurred") == 0); | |
xmlrpc_env_clean(&fault); | |
/* We don't need to test our handling of poorly formatted XML here, | |
** because we already did that in test_parse_xml_value. */ | |
/* Next, check for bogus responses. These are all well-formed XML, but | |
** they aren't legal XML-RPC. */ | |
for (bad_resp = bad_responses; *bad_resp != NULL; bad_resp++) { | |
/* First, check to make sure that our test case is well-formed XML. | |
** (It's easy to make mistakes when writing the test cases!) */ | |
elem = xml_parse(&env, *bad_resp, strlen(*bad_resp)); | |
TEST_NO_FAULT(&env); | |
xml_element_free(elem); | |
/* Now, make sure the higher-level routine barfs appropriately. */ | |
xmlrpc_env_init(&env2); | |
v = xmlrpc_parse_response(&env2, *bad_resp, strlen(*bad_resp)); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code != 0); /* We use 0 as a code in our bad faults. */ | |
TEST(v == NULL); | |
xmlrpc_env_clean(&env2); | |
} | |
xmlrpc_env_clean(&env); | |
} | |
static void test_parse_xml_call (void) | |
{ | |
xmlrpc_env env, env2; | |
char *method_name; | |
xmlrpc_value *params; | |
int i1, i2; | |
char **bad_call; | |
xml_element *elem; | |
xmlrpc_env_init(&env); | |
/* Parse a valid call. */ | |
xmlrpc_parse_call(&env, serialized_call, strlen(serialized_call), | |
&method_name, ¶ms); | |
TEST_NO_FAULT(&env); | |
TEST(params != NULL); | |
xmlrpc_parse_value(&env, params, "(ii)", &i1, &i2); | |
TEST_NO_FAULT(&env); | |
TEST(strcmp(method_name, "gloom&doom") == 0); | |
TEST(i1 == 10 && i2 == 20); | |
free(method_name); | |
xmlrpc_DECREF(params); | |
/* Test some poorly-formed XML data. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_parse_call(&env2, unparseable_value, strlen(unparseable_value), | |
&method_name, ¶ms); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_PARSE_ERROR); | |
TEST(method_name == NULL && params == NULL); | |
xmlrpc_env_clean(&env2); | |
/* Next, check for bogus values. These are all well-formed XML, but | |
** they aren't legal XML-RPC. */ | |
for (bad_call = bad_calls; *bad_call != NULL; bad_call++) { | |
/* First, check to make sure that our test case is well-formed XML. | |
** (It's easy to make mistakes when writing the test cases!) */ | |
elem = xml_parse(&env, *bad_call, strlen(*bad_call)); | |
TEST_NO_FAULT(&env); | |
xml_element_free(elem); | |
/* Now, make sure the higher-level routine barfs appropriately. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_parse_call(&env2, *bad_call, strlen(*bad_call), | |
&method_name, ¶ms); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_PARSE_ERROR); | |
TEST(method_name == NULL && params == NULL); | |
xmlrpc_env_clean(&env2); | |
} | |
xmlrpc_env_clean(&env); | |
} | |
/*========================================================================= | |
** test_method_registry | |
**========================================================================= | |
** We need to define some static callbacks to test this code. | |
*/ | |
#define FOO_USER_DATA ((void*) 0xF00) | |
#define BAR_USER_DATA ((void*) 0xBAF) | |
static xmlrpc_value *test_foo (xmlrpc_env *env, | |
xmlrpc_value *param_array, | |
void *user_data) | |
{ | |
xmlrpc_int32 x, y; | |
TEST_NO_FAULT(env); | |
TEST(param_array != NULL); | |
TEST(user_data == FOO_USER_DATA); | |
xmlrpc_parse_value(env, param_array, "(ii)", &x, &y); | |
TEST_NO_FAULT(env); | |
TEST(x == 25); | |
TEST(y == 17); | |
return xmlrpc_build_value(env, "i", (xmlrpc_int32) x + y); | |
} | |
static xmlrpc_value *test_bar (xmlrpc_env *env, | |
xmlrpc_value *param_array, | |
void *user_data) | |
{ | |
xmlrpc_int32 x, y; | |
TEST_NO_FAULT(env); | |
TEST(param_array != NULL); | |
TEST(user_data == BAR_USER_DATA); | |
xmlrpc_parse_value(env, param_array, "(ii)", &x, &y); | |
TEST_NO_FAULT(env); | |
TEST(x == 25); | |
TEST(y == 17); | |
xmlrpc_env_set_fault(env, 123, "Test fault"); | |
return NULL; | |
} | |
static xmlrpc_value *test_default (xmlrpc_env *env, | |
char *host, | |
char *method_name, | |
xmlrpc_value *param_array, | |
void *user_data) | |
{ | |
xmlrpc_int32 x, y; | |
TEST_NO_FAULT(env); | |
TEST(param_array != NULL); | |
TEST(user_data == FOO_USER_DATA); | |
xmlrpc_parse_value(env, param_array, "(ii)", &x, &y); | |
TEST_NO_FAULT(env); | |
TEST(x == 25); | |
TEST(y == 17); | |
return xmlrpc_build_value(env, "i", 2 * (x + y)); | |
} | |
static xmlrpc_value * | |
process_call_helper (xmlrpc_env *env, | |
xmlrpc_registry *registry, | |
char *method_name, | |
xmlrpc_value *arg_array) | |
{ | |
xmlrpc_mem_block *call, *response; | |
xmlrpc_value *value; | |
/* Build a call, and tell the registry to handle it. */ | |
call = xmlrpc_mem_block_new(env, 0); | |
TEST_NO_FAULT(env); | |
xmlrpc_serialize_call(env, call, method_name, arg_array); | |
TEST_NO_FAULT(env); | |
response = xmlrpc_registry_process_call(env, registry, NULL, | |
xmlrpc_mem_block_contents(call), | |
xmlrpc_mem_block_size(call)); | |
TEST_NO_FAULT(env); | |
TEST(response != NULL); | |
/* Parse the response. */ | |
value = xmlrpc_parse_response(env, xmlrpc_mem_block_contents(response), | |
xmlrpc_mem_block_size(response)); | |
xmlrpc_mem_block_free(call); | |
xmlrpc_mem_block_free(response); | |
return value; | |
} | |
static void test_method_registry (void) | |
{ | |
xmlrpc_env env, env2; | |
xmlrpc_value *arg_array, *value; | |
xmlrpc_registry *registry; | |
xmlrpc_mem_block *response; | |
xmlrpc_int32 i; | |
xmlrpc_value *multi; | |
xmlrpc_int32 foo1_result, foo2_result; | |
xmlrpc_int32 bar_code, nosuch_code, multi_code, bogus1_code, bogus2_code; | |
char *bar_string, *nosuch_string, *multi_string; | |
char *bogus1_string, *bogus2_string; | |
xmlrpc_env_init(&env); | |
/* Create a new registry. */ | |
registry = xmlrpc_registry_new(&env); | |
TEST(registry != NULL); | |
TEST_NO_FAULT(&env); | |
/* Add some test methods. */ | |
xmlrpc_registry_add_method(&env, registry, NULL, "test.foo", | |
test_foo, FOO_USER_DATA); | |
TEST_NO_FAULT(&env); | |
xmlrpc_registry_add_method(&env, registry, NULL, "test.bar", | |
test_bar, BAR_USER_DATA); | |
TEST_NO_FAULT(&env); | |
/* Build an argument array for our calls. */ | |
arg_array = xmlrpc_build_value(&env, "(ii)", | |
(xmlrpc_int32) 25, (xmlrpc_int32) 17); | |
TEST_NO_FAULT(&env); | |
/* Call test.foo and check the result. */ | |
value = process_call_helper(&env, registry, "test.foo", arg_array); | |
TEST_NO_FAULT(&env); | |
TEST(value != NULL); | |
xmlrpc_parse_value(&env, value, "i", &i); | |
TEST_NO_FAULT(&env); | |
TEST(i == 42); | |
xmlrpc_DECREF(value); | |
/* Call test.bar and check the result. */ | |
xmlrpc_env_init(&env2); | |
value = process_call_helper(&env2, registry, "test.bar", arg_array); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == 123); | |
TEST(env2.fault_string && strcmp(env2.fault_string, "Test fault") == 0); | |
xmlrpc_env_clean(&env2); | |
/* Call a non-existant method and check the result. */ | |
xmlrpc_env_init(&env2); | |
value = process_call_helper(&env2, registry, "test.nosuch", arg_array); | |
TEST(value == NULL); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_NO_SUCH_METHOD_ERROR); | |
xmlrpc_env_clean(&env2); | |
/* Test system.multicall. */ | |
multi = xmlrpc_build_value(&env, | |
"(({s:s,s:V}{s:s,s:V}{s:s,s:V}" | |
"{s:s,s:()}s{}{s:s,s:V}))", | |
"methodName", "test.foo", | |
"params", arg_array, | |
"methodName", "test.bar", | |
"params", arg_array, | |
"methodName", "test.nosuch", | |
"params", arg_array, | |
"methodName", "system.multicall", | |
"params", | |
"bogus_entry", | |
"methodName", "test.foo", | |
"params", arg_array); | |
TEST_NO_FAULT(&env); | |
value = process_call_helper(&env, registry, "system.multicall", multi); | |
TEST_NO_FAULT(&env); | |
xmlrpc_parse_value(&env, value, | |
"((i){s:i,s:s,*}{s:i,s:s,*}" | |
"{s:i,s:s,*}{s:i,s:s,*}{s:i,s:s,*}(i))", | |
&foo1_result, | |
"faultCode", &bar_code, | |
"faultString", &bar_string, | |
"faultCode", &nosuch_code, | |
"faultString", &nosuch_string, | |
"faultCode", &multi_code, | |
"faultString", &multi_string, | |
"faultCode", &bogus1_code, | |
"faultString", &bogus1_string, | |
"faultCode", &bogus2_code, | |
"faultString", &bogus2_string, | |
&foo2_result); | |
TEST_NO_FAULT(&env); | |
TEST(foo1_result == 42); | |
TEST(bar_code == 123); | |
TEST(strcmp(bar_string, "Test fault") == 0); | |
TEST(nosuch_code == XMLRPC_NO_SUCH_METHOD_ERROR); | |
TEST(multi_code == XMLRPC_REQUEST_REFUSED_ERROR); | |
TEST(foo2_result == 42); | |
xmlrpc_DECREF(multi); | |
xmlrpc_DECREF(value); | |
/* PASS bogus XML data and make sure our parser pukes gracefully. | |
** (Because of the way the code is laid out, and the presence of other | |
** test suites, this lets us skip tests for invalid XML-RPC data.) */ | |
xmlrpc_env_init(&env2); | |
response = xmlrpc_registry_process_call(&env, registry, NULL, | |
expat_error_data, | |
strlen(expat_error_data)); | |
TEST_NO_FAULT(&env); | |
TEST(response != NULL); | |
value = xmlrpc_parse_response(&env2, xmlrpc_mem_block_contents(response), | |
xmlrpc_mem_block_size(response)); | |
TEST(value == NULL); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_PARSE_ERROR); | |
xmlrpc_mem_block_free(response); | |
xmlrpc_env_clean(&env2); | |
/* Test default method support. */ | |
xmlrpc_registry_set_default_method(&env, registry, &test_default, | |
FOO_USER_DATA); | |
TEST_NO_FAULT(&env); | |
value = process_call_helper(&env, registry, "test.nosuch", arg_array); | |
TEST_NO_FAULT(&env); | |
TEST(value != NULL); | |
xmlrpc_parse_value(&env, value, "i", &i); | |
TEST_NO_FAULT(&env); | |
TEST(i == 84); | |
xmlrpc_DECREF(value); | |
/* Change the default method. */ | |
xmlrpc_registry_set_default_method(&env, registry, &test_default, | |
BAR_USER_DATA); | |
TEST_NO_FAULT(&env); | |
/* Test cleanup code (w/memprof). */ | |
xmlrpc_registry_free(registry); | |
xmlrpc_DECREF(arg_array); | |
xmlrpc_env_clean(&env); | |
} | |
static void test_nesting_limit (void) | |
{ | |
xmlrpc_env env; | |
xmlrpc_value *val; | |
xmlrpc_env_init(&env); | |
/* Test with an adequate limit for (...(...()...)...). */ | |
xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, 2); | |
val = xmlrpc_parse_response(&env, correct_value, strlen(correct_value)); | |
TEST_NO_FAULT(&env); | |
TEST(val != NULL); | |
xmlrpc_DECREF(val); | |
/* Test with an inadequate limit. */ | |
xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, 1); | |
val = xmlrpc_parse_response(&env, correct_value, strlen(correct_value)); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == XMLRPC_PARSE_ERROR); /* BREAKME - Will change. */ | |
TEST(val == NULL); | |
/* Reset the default limit. */ | |
xmlrpc_limit_set(XMLRPC_NESTING_LIMIT_ID, XMLRPC_NESTING_LIMIT_DEFAULT); | |
TEST(xmlrpc_limit_get(XMLRPC_NESTING_LIMIT_ID) | |
== XMLRPC_NESTING_LIMIT_DEFAULT); | |
xmlrpc_env_clean(&env); | |
} | |
static void test_xml_size_limit (void) | |
{ | |
xmlrpc_env env; | |
char *method_name; | |
xmlrpc_value *params, *val; | |
/* NOTE - This test suite only verifies the last-ditch size-checking | |
** code. There should also be matching code in all server (and | |
** preferably all client) modules as well. */ | |
/* Set our XML size limit to something ridiculous. */ | |
xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 6); | |
/* Attempt to parse a call. */ | |
xmlrpc_env_init(&env); | |
xmlrpc_parse_call(&env, serialized_call, strlen(serialized_call), | |
&method_name, ¶ms); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == XMLRPC_LIMIT_EXCEEDED_ERROR); | |
TEST(method_name == NULL); | |
TEST(params == NULL); | |
xmlrpc_env_clean(&env); | |
/* Attempt to parse a response. */ | |
xmlrpc_env_init(&env); | |
val = xmlrpc_parse_response(&env, correct_value, strlen(correct_value)); | |
TEST(env.fault_occurred); | |
TEST(env.fault_code == XMLRPC_LIMIT_EXCEEDED_ERROR); | |
TEST(val == NULL); | |
xmlrpc_env_clean(&env); | |
/* Reset the default limit. */ | |
xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, XMLRPC_XML_SIZE_LIMIT_DEFAULT); | |
} | |
/*========================================================================= | |
** test_sample_files | |
**========================================================================= | |
** Read in a bunch of sample test files and make sure we get plausible | |
** results. | |
** | |
** We use these files to test strange-but-legal encodings, illegal-but- | |
** supported encodings, etc. | |
*/ | |
#define FILE_PREFIX \ | |
".." PATH_SEPARATOR TOP_SRCDIR PATH_SEPARATOR \ | |
"src" PATH_SEPARATOR "testdata" PATH_SEPARATOR | |
static char *good_requests[] = { | |
FILE_PREFIX "req_out_of_order.xml", | |
FILE_PREFIX "req_no_params.xml", | |
FILE_PREFIX "req_value_name.xml", | |
NULL | |
}; | |
#define MAX_SAMPLE_FILE_LEN (16 * 1024) | |
static char file_buff [MAX_SAMPLE_FILE_LEN]; | |
static void | |
read_file (char *path, char **out_data, size_t *out_size) | |
{ | |
FILE *f; | |
size_t bytes_read; | |
/* Open the file. */ | |
f = fopen(path, "r"); | |
if (f == NULL) { | |
/* Since this error is fairly likely to happen, give an | |
** informative error message... */ | |
fflush(stdout); | |
perror("\n" __FILE__); | |
fprintf(stderr, "Could not open file \"%s\".\n", path); | |
exit(1); | |
} | |
/* Read in one buffer full of data, and make sure that everything | |
** fit. (We perform a lazy error/no-eof/zero-length-file test using | |
** bytes_read.) */ | |
bytes_read = fread(file_buff, sizeof(char), MAX_SAMPLE_FILE_LEN, f); | |
TEST(0 < bytes_read && bytes_read < MAX_SAMPLE_FILE_LEN); | |
/* Close the file and return our data. */ | |
fclose(f); | |
*out_data = file_buff; | |
*out_size = bytes_read; | |
} | |
static void test_sample_files (void) | |
{ | |
xmlrpc_env env; | |
char **paths, *path; | |
char *data; | |
size_t data_len; | |
char *method_name; | |
xmlrpc_value *params; | |
xmlrpc_env_init(&env); | |
/* Test our good requests. */ | |
for (paths = good_requests; *paths != NULL; paths++) { | |
path = *paths; | |
read_file(path, &data, &data_len); | |
xmlrpc_parse_call(&env, data, data_len, &method_name, ¶ms); | |
TEST_NO_FAULT(&env); | |
free(method_name); | |
xmlrpc_DECREF(params); | |
} | |
xmlrpc_env_clean(&env); | |
} | |
/*========================================================================= | |
** test_utf8_coding | |
**========================================================================= | |
** We need to test our UTF-8 decoder thoroughly. Most of these test | |
** cases are taken from the UTF-8-test.txt file by Markus Kuhn | |
** <[email protected]>: | |
** http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt | |
*/ | |
#ifdef HAVE_UNICODE_WCHAR | |
typedef struct { | |
char *utf8; | |
wchar_t wcs[16]; | |
} utf8_and_wcs; | |
static utf8_and_wcs good_utf8[] = { | |
/* Greek 'kosme'. */ | |
{"\316\272\341\275\271\317\203\316\274\316\265", | |
{0x03BA, 0x1F79, 0x03C3, 0x03BC, 0x03B5, 0}}, | |
/* First sequences of a given length. */ | |
/* '\000' is not a legal C string. */ | |
{"\302\200", {0x0080, 0}}, | |
{"\340\240\200", {0x0800, 0}}, | |
/* Last sequences of a given length. */ | |
{"\177", {0x007F, 0}}, | |
{"\337\277", {0x07FF, 0}}, | |
/* 0xFFFF is not a legal Unicode character. */ | |
/* Other boundry conditions. */ | |
{"\001", {0x0001, 0}}, | |
{"\355\237\277", {0xD7FF, 0}}, | |
{"\356\200\200", {0xE000, 0}}, | |
{"\357\277\275", {0xFFFD, 0}}, | |
/* Other random test cases. */ | |
{"", {0}}, | |
{"abc", {0x0061, 0x0062, 0x0063, 0}}, | |
{"[\302\251]", {0x005B, 0x00A9, 0x005D, 0}}, | |
{NULL, {0}} | |
}; | |
static char *(bad_utf8[]) = { | |
/* Continuation bytes. */ | |
"\200", "\277", | |
/* Lonely start characters. */ | |
"\300", "\300x", "\300xx", | |
"\340", "\340x", "\340xx", "\340xxx", | |
/* Last byte missing. */ | |
"\340\200", "\340\200x", "\340\200xx", | |
"\357\277", "\357\277x", "\357\277xx", | |
/* Illegal bytes. */ | |
"\376", "\377", | |
/* Overlong '/'. */ | |
"\300\257", "\340\200\257", | |
/* Overlong ASCII NUL. */ | |
"\300\200", "\340\200\200", | |
/* Maximum overlong sequences. */ | |
"\301\277", "\340\237\277", | |
/* Illegal code positions. */ | |
"\357\277\276", /* U+FFFE */ | |
"\357\277\277", /* U+FFFF */ | |
/* UTF-16 surrogates (unpaired and paired). */ | |
"\355\240\200", | |
"\355\277\277", | |
"\355\240\200\355\260\200", | |
"\355\257\277\355\277\277", | |
/* Valid UCS-4 characters (not supported yet). | |
** On systems with UCS-4 or UTF-16 wchar_t values, these | |
** may eventually be supported in some fashion. */ | |
"\360\220\200\200", | |
"\370\210\200\200\200", | |
"\374\204\200\200\200\200", | |
NULL | |
}; | |
/* This routine is missing on certain platforms. This implementation | |
** *appears* to be correct. */ | |
#if 0 | |
#ifndef HAVE_WCSNCMP | |
int wcsncmp(wchar_t *wcs1, wchar_t* wcs2, size_t len) | |
{ | |
size_t i; | |
/* XXX - 'unsigned long' should be 'uwchar_t'. */ | |
unsigned long c1, c2; | |
for (i=0; i < len; i++) { | |
c1 = wcs1[i]; | |
c2 = wcs2[i]; | |
/* This clever comparison borrowed from the GNU C Library. */ | |
if (c1 == 0 || c1 != c2) | |
return c1 - c2; | |
} | |
return 0; | |
} | |
#endif /* HAVE_WCSNCMP */ | |
#endif | |
static void test_utf8_coding (void) | |
{ | |
xmlrpc_env env, env2; | |
utf8_and_wcs *good_data; | |
char **bad_data; | |
char *utf8; | |
wchar_t *wcs; | |
xmlrpc_mem_block *output; | |
xmlrpc_env_init(&env); | |
/* Test each of our valid UTF-8 sequences. */ | |
for (good_data = good_utf8; good_data->utf8 != NULL; good_data++) { | |
utf8 = good_data->utf8; | |
wcs = good_data->wcs; | |
/* Attempt to validate the UTF-8 string. */ | |
xmlrpc_validate_utf8(&env, utf8, strlen(utf8)); | |
TEST_NO_FAULT(&env); | |
/* Attempt to decode the UTF-8 string. */ | |
output = xmlrpc_utf8_to_wcs(&env, utf8, strlen(utf8)); | |
TEST_NO_FAULT(&env); | |
TEST(output != NULL); | |
TEST(wcslen(wcs) == XMLRPC_TYPED_MEM_BLOCK_SIZE(wchar_t, output)); | |
TEST(0 == | |
wcsncmp(wcs, XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, output), | |
wcslen(wcs))); | |
xmlrpc_mem_block_free(output); | |
/* Test the UTF-8 encoder, too. */ | |
output = xmlrpc_wcs_to_utf8(&env, wcs, wcslen(wcs)); | |
TEST_NO_FAULT(&env); | |
TEST(output != NULL); | |
TEST(strlen(utf8) == XMLRPC_TYPED_MEM_BLOCK_SIZE(char, output)); | |
TEST(0 == | |
strncmp(utf8, XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, output), | |
strlen(utf8))); | |
xmlrpc_mem_block_free(output); | |
} | |
/* Test each of our illegal UTF-8 sequences. */ | |
for (bad_data = bad_utf8; *bad_data != NULL; bad_data++) { | |
utf8 = *bad_data; | |
/* Attempt to validate the UTF-8 string. */ | |
xmlrpc_env_init(&env2); | |
xmlrpc_validate_utf8(&env2, utf8, strlen(utf8)); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_INVALID_UTF8_ERROR); | |
/* printf("Fault: %s\n", env2.fault_string); --Hand-checked */ | |
xmlrpc_env_clean(&env2); | |
/* Attempt to decode the UTF-8 string. */ | |
xmlrpc_env_init(&env2); | |
output = xmlrpc_utf8_to_wcs(&env2, utf8, strlen(utf8)); | |
TEST(env2.fault_occurred); | |
TEST(env2.fault_code == XMLRPC_INVALID_UTF8_ERROR); | |
TEST(output == NULL); | |
xmlrpc_env_clean(&env2); | |
} | |
xmlrpc_env_clean(&env); | |
} | |
static char utf8_data[] = "[\302\251\0]"; | |
static wchar_t wcs_data[] = {0x005B, 0x00A9, 0, 0x005D, 0}; | |
static void test_wchar_support (void) | |
{ | |
xmlrpc_env env; | |
xmlrpc_value *val; | |
wchar_t *wcs; | |
char *str; | |
size_t len; | |
xmlrpc_env_init(&env); | |
/* Build a string from UTF-8 data. */ | |
val = xmlrpc_build_value(&env, "s#", utf8_data, (size_t) 5); | |
TEST_NO_FAULT(&env); | |
TEST(val != NULL); | |
/* Extract it as a wchar_t string. */ | |
xmlrpc_parse_value(&env, val, "w#", &wcs, &len); | |
TEST_NO_FAULT(&env); | |
TEST(wcs != NULL); | |
TEST(len == 4); | |
TEST(wcs[len] == '\0'); | |
TEST(0 == wcsncmp(wcs, wcs_data, len)); | |
xmlrpc_DECREF(val); | |
/* Build a string from wchar_t data. */ | |
val = xmlrpc_build_value(&env, "w#", wcs_data, 4); | |
TEST_NO_FAULT(&env); | |
TEST(val != NULL); | |
/* Extract it as a wchar_t string. */ | |
xmlrpc_parse_value(&env, val, "w#", &wcs, &len); | |
TEST_NO_FAULT(&env); | |
TEST(wcs != NULL); | |
TEST(len == 4); | |
TEST(wcs[len] == '\0'); | |
TEST(0 == wcsncmp(wcs, wcs_data, len)); | |
/* Extract it as a UTF-8 string. */ | |
xmlrpc_parse_value(&env, val, "s#", &str, &len); | |
TEST_NO_FAULT(&env); | |
TEST(str != NULL); | |
TEST(len == 5); | |
TEST(str[len] == '\0'); | |
TEST(0 == strncmp(str, utf8_data, len)); | |
xmlrpc_DECREF(val); | |
xmlrpc_env_clean(&env); | |
} | |
#endif /* HAVE_UNICODE_WCHAR */ | |
/*========================================================================= | |
** Test Driver | |
**========================================================================= | |
*/ | |
int main (int argc, char** argv) | |
{ | |
/* Add your test suites here. */ | |
test_env(); | |
test_mem_block(); | |
test_base64_conversion(); | |
test_value(); | |
test_bounds_checks(); | |
test_struct(); | |
test_serialize(); | |
test_expat(); | |
test_parse_xml_value(); | |
test_parse_xml_response(); | |
test_parse_xml_call(); | |
test_method_registry(); | |
test_nesting_limit(); | |
test_xml_size_limit(); | |
test_sample_files(); | |
#ifdef HAVE_UNICODE_WCHAR | |
test_utf8_coding(); | |
test_wchar_support(); | |
#endif /* HAVE_UNICODE_WCHAR */ | |
/* Summarize our test run. */ | |
printf("\nRan %d tests, %d failed, %.1f%% passed\n", | |
total_tests, total_failures, | |
100.0 - (100.0 * total_failures) / total_tests); | |
/* Print the final result. */ | |
if (total_failures == 0) { | |
printf("OK\n"); | |
return 0; | |
} | |
printf("FAILED\n"); | |
return 1; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright (C) 2001 by First Peer, Inc. All rights reserved. | |
** Copyright (C) 2001 by Eric Kidd. 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. */ | |
#ifndef HAVE_WIN32_CONFIG_H | |
#include "xmlrpc_config.h" | |
#else | |
#include "xmlrpc_win32_config.h" | |
#endif | |
#include <stddef.h> | |
#include <stdlib.h> | |
#include <stdarg.h> | |
#include <string.h> | |
#define XMLRPC_WANT_INTERNAL_DECLARATIONS | |
#include "xmlrpc.h" | |
/* Borrowed from Python 1.5.2. | |
** MPW pushes 'extended' for float and double types with varargs */ | |
#ifdef MPW | |
typedef extended va_double; | |
#else | |
typedef double va_double; | |
#endif | |
/* Borrowed from Python 1.5.2. | |
** Python copies its va_list objects before using them in certain | |
** tricky fashions. We don't why Python does this, but since we're | |
** abusing our va_list objects in a similar fashion, we'll copy them | |
** too. */ | |
#ifdef VA_LIST_IS_ARRAY | |
#define VA_LIST_COPY(dest,src) memcpy((dest), (src), sizeof(va_list)) | |
#else | |
#define VA_LIST_COPY(dest,src) ((dest) = (src)) | |
#endif | |
/*========================================================================= | |
** Reference Counting | |
**========================================================================= | |
** Some simple reference-counting code. The xmlrpc_DECREF routine is in | |
** charge of destroying values when their reference count equals zero. | |
*/ | |
void xmlrpc_INCREF (xmlrpc_value* value) | |
{ | |
XMLRPC_ASSERT_VALUE_OK(value); | |
XMLRPC_ASSERT(value->_refcount > 0); | |
value->_refcount++; | |
} | |
void xmlrpc_DECREF (xmlrpc_value* value) | |
{ | |
xmlrpc_env env; | |
int size, i; | |
xmlrpc_value *item; | |
_struct_member *members; | |
XMLRPC_ASSERT_VALUE_OK(value); | |
XMLRPC_ASSERT(value->_refcount > 0); | |
XMLRPC_ASSERT(value->_type != XMLRPC_TYPE_DEAD); | |
value->_refcount--; | |
/* If we have no more refs, we need to deallocate this value. */ | |
if (value->_refcount == 0) { | |
/* First, we need to destroy this value's contents, if any. */ | |
switch (value->_type) { | |
case XMLRPC_TYPE_INT: | |
case XMLRPC_TYPE_BOOL: | |
case XMLRPC_TYPE_DOUBLE: | |
break; | |
case XMLRPC_TYPE_ARRAY: | |
/* Dispose of the contents of the array. | |
** No errors should *ever* occur when this code is running, | |
** so we use assertions instead of regular error checks. */ | |
xmlrpc_env_init(&env); | |
size = xmlrpc_array_size(&env, value); | |
XMLRPC_ASSERT(!env.fault_occurred); | |
for (i = 0; i < size; i++) { | |
item = xmlrpc_array_get_item(&env, value, i); | |
XMLRPC_ASSERT(!env.fault_occurred); | |
xmlrpc_DECREF(item); | |
} | |
xmlrpc_env_clean(&env); | |
xmlrpc_mem_block_clean(&value->_block); | |
break; | |
case XMLRPC_TYPE_STRING: | |
#ifdef HAVE_UNICODE_WCHAR | |
if (value->_wcs_block) | |
xmlrpc_mem_block_free(value->_wcs_block); | |
#endif /* HAVE_UNICODE_WCHAR */ | |
/* Fall through. */ | |
case XMLRPC_TYPE_DATETIME: | |
case XMLRPC_TYPE_BASE64: | |
xmlrpc_mem_block_clean(&value->_block); | |
break; | |
case XMLRPC_TYPE_STRUCT: | |
/* Dispose of the contents of the struct. | |
** No errors should *ever* occur when this code is running, | |
** so we use assertions instead of regular error checks. */ | |
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(_struct_member, | |
&value->_block); | |
members = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(_struct_member, | |
&value->_block); | |
for (i = 0; i < size; i++) { | |
xmlrpc_DECREF(members[i].key); | |
xmlrpc_DECREF(members[i].value); | |
} | |
xmlrpc_mem_block_clean(&value->_block); | |
break; | |
case XMLRPC_TYPE_C_PTR: | |
break; | |
case XMLRPC_TYPE_DEAD: | |
XMLRPC_FATAL_ERROR("Tried to destroy deallocated value"); | |
default: | |
XMLRPC_FATAL_ERROR("Unknown XML-RPC type"); | |
} | |
/* Next, we mark this value as invalid, to help catch refcount | |
** errors. */ | |
value->_type = XMLRPC_TYPE_DEAD; | |
/* Finally, we destroy the value itself. */ | |
free(value); | |
} | |
} | |
/*========================================================================= | |
** Building XML-RPC values. | |
**========================================================================= | |
*/ | |
xmlrpc_type xmlrpc_value_type (xmlrpc_value* value) | |
{ | |
XMLRPC_ASSERT_VALUE_OK(value); | |
return value->_type; | |
} | |
/*========================================================================= | |
** Building XML-RPC values. | |
**========================================================================= | |
** Build new XML-RPC values from a format string. This code is heavily | |
** inspired by Py_BuildValue from Python 1.5.2. In particular, our | |
** particular abuse of the va_list data type is copied from the equivalent | |
** Python code in modsupport.c. Since Python is portable, our code should | |
** (in theory) also be portable. | |
*/ | |
static xmlrpc_value* mkvalue(xmlrpc_env* env, char** format, va_list* args); | |
static xmlrpc_value* mkarray(xmlrpc_env* env, | |
char** format, | |
char delimiter, | |
va_list* args) | |
{ | |
xmlrpc_value *array, *item; | |
int array_valid; | |
char code; | |
/* Set up error handling preconditions. */ | |
array = NULL; | |
array_valid = 0; | |
/* Allocate our array. */ | |
array = (xmlrpc_value*) malloc(sizeof(xmlrpc_value)); | |
XMLRPC_FAIL_IF_NULL(array, env, XMLRPC_INTERNAL_ERROR, | |
"Could not allocate memory for array"); | |
array->_refcount = 1; | |
array->_type = XMLRPC_TYPE_ARRAY; | |
XMLRPC_TYPED_MEM_BLOCK_INIT(xmlrpc_value*, env, &array->_block, 0); | |
XMLRPC_FAIL_IF_FAULT(env); | |
array_valid = 1; | |
/* Add items to the array until we hit our delimiter. */ | |
code = **format; | |
while (code != delimiter && code != '\0') { | |
item = mkvalue(env, format, args); | |
XMLRPC_FAIL_IF_FAULT(env); | |
xmlrpc_array_append_item(env, array, item); | |
xmlrpc_DECREF(item); | |
XMLRPC_FAIL_IF_FAULT(env); | |
code = **format; | |
} | |
XMLRPC_ASSERT(code == delimiter); | |
cleanup: | |
if (env->fault_occurred) { | |
if (array) { | |
if (array_valid) | |
xmlrpc_DECREF(array); | |
else | |
free(array); | |
} | |
return NULL; | |
} | |
return array; | |
} | |
static xmlrpc_value* mkstruct(xmlrpc_env* env, | |
char** format, | |
char delimiter, | |
va_list* args) | |
{ | |
xmlrpc_value *strct, *key, *value; | |
/* Set up error handling preconditions. */ | |
strct = key = value = NULL; | |
/* Allocate a new struct for us to use. */ | |
strct = xmlrpc_struct_new(env); | |
XMLRPC_FAIL_IF_FAULT(env); | |
/* Build the members of our struct. */ | |
while (**format != delimiter && **format != '\0') { | |
/* Get our key, and skip over the ':' character. */ | |
key = mkvalue(env, format, args); | |
XMLRPC_FAIL_IF_FAULT(env); | |
XMLRPC_ASSERT(**format == ':'); | |
(*format)++; | |
/* Get our value, and skip over the ',' character (if present). */ | |
value = mkvalue(env, format, args); | |
XMLRPC_FAIL_IF_FAULT(env); | |
XMLRPC_ASSERT(**format == ',' || **format == delimiter); | |
if (**format == ',') | |
(*format)++; | |
/* Add the new key/value pair to the struct. */ | |
xmlrpc_struct_set_value_v(env, strct, key, value); | |
XMLRPC_FAIL_IF_FAULT(env); | |
/* Release our references, and restore our invariants. */ | |
xmlrpc_DECREF(key); | |
key = NULL; | |
xmlrpc_DECREF(value); | |
value = NULL; | |
} | |
XMLRPC_ASSERT(**format == delimiter); | |
cleanup: | |
if (env->fault_occurred) { | |
if (strct) | |
xmlrpc_DECREF(strct); | |
if (key) | |
xmlrpc_DECREF(key); | |
if (value) | |
xmlrpc_DECREF(value); | |
return NULL; | |
} | |
return strct; | |
} | |
#ifdef HAVE_UNICODE_WCHAR | |
static xmlrpc_value *mkwidestring(xmlrpc_env *env, | |
wchar_t *wcs, | |
size_t wcs_len) | |
{ | |
xmlrpc_value* val; | |
char *contents; | |
wchar_t *wcs_contents; | |
int block_is_inited; | |
xmlrpc_mem_block *utf8_block; | |
char *utf8_contents; | |
size_t utf8_len; | |
/* Error-handling preconditions. */ | |
val = NULL; | |
utf8_block = NULL; | |
block_is_inited = 0; | |
/* Initialize our XML-RPC value. */ | |
val = (xmlrpc_value*) malloc(sizeof(xmlrpc_value)); | |
XMLRPC_FAIL_IF_NULL(val, env, XMLRPC_INTERNAL_ERROR, | |
"Could not allocate memory for wide string"); | |
val->_refcount = 1; | |
val->_type = XMLRPC_TYPE_STRING; | |
/* More error-handling preconditions. */ | |
val->_wcs_block = NULL; | |
/* Build our wchar_t block first. */ | |
val->_wcs_block = | |
XMLRPC_TYPED_MEM_BLOCK_NEW(wchar_t, env, wcs_len + 1); | |
XMLRPC_FAIL_IF_FAULT(env); | |
wcs_contents = | |
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, val->_wcs_block); | |
memcpy(wcs_contents, wcs, wcs_len * sizeof(wchar_t)); | |
wcs_contents[wcs_len] = '\0'; | |
/* Convert the wcs block to UTF-8. */ | |
utf8_block = xmlrpc_wcs_to_utf8(env, wcs_contents, wcs_len + 1); | |
XMLRPC_FAIL_IF_FAULT(env); | |
utf8_contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, utf8_block); | |
utf8_len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, utf8_block); | |
/* XXX - We need an extra memcopy to initialize _block. */ | |
XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &val->_block, utf8_len); | |
XMLRPC_FAIL_IF_FAULT(env); | |
block_is_inited = 1; | |
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &val->_block); | |
memcpy(contents, utf8_contents, utf8_len); | |
cleanup: | |
if (utf8_block) | |
xmlrpc_mem_block_free(utf8_block); | |
if (env->fault_occurred) { | |
if (val) { | |
if (val->_wcs_block) | |
xmlrpc_mem_block_free(val->_wcs_block); | |
if (block_is_inited) | |
xmlrpc_mem_block_clean(&val->_block); | |
free(val); | |
} | |
return NULL; | |
} | |
return val; | |
} | |
#endif /* HAVE_UNICODE_WCHAR */ | |
static xmlrpc_value* mkvalue(xmlrpc_env* env, char** format, va_list* args) | |
{ | |
xmlrpc_value* val; | |
char *str, *contents; | |
unsigned char *bin_data; | |
size_t len; | |
#ifdef HAVE_UNICODE_WCHAR | |
wchar_t *wcs; | |
#endif | |
/* XXX - This routine has dubious error handling. To make a long story | |
** short, you're not currently allowed to allocate memory inside of 'val' | |
** and then fail some later error check. This should examined and | |
** fixed. */ | |
/* Allocate some memory which we'll almost certainly use. If we don't | |
** use it, we'll deallocate it before returning. */ | |
val = (xmlrpc_value*) malloc(sizeof(xmlrpc_value)); | |
if (!val) { | |
xmlrpc_env_set_fault(env, XMLRPC_INTERNAL_ERROR, | |
"Could not allocate memory for xmlrpc_value"); | |
return NULL; | |
} | |
val->_refcount = 1; | |
/* Process the next format character. */ | |
switch (*(*format)++) { | |
case 'i': | |
val->_type = XMLRPC_TYPE_INT; | |
val->_value.i = (xmlrpc_int32) va_arg(*args, xmlrpc_int32); | |
break; | |
case 'b': | |
val->_type = XMLRPC_TYPE_BOOL; | |
val->_value.b = (xmlrpc_bool) va_arg(*args, xmlrpc_bool); | |
break; | |
case 'd': | |
val->_type = XMLRPC_TYPE_DOUBLE; | |
val->_value.d = (double) va_arg(*args, va_double); | |
break; | |
case 's': | |
val->_type = XMLRPC_TYPE_STRING; | |
#ifdef HAVE_UNICODE_WCHAR | |
val->_wcs_block = NULL; | |
#endif | |
str = (char*) va_arg(*args, char*); | |
if (**format == '#') { | |
(*format)++; | |
len = (size_t) va_arg(*args, size_t); | |
} else { | |
len = strlen(str); | |
} | |
XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &val->_block, len + 1); | |
XMLRPC_FAIL_IF_FAULT(env); | |
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &val->_block); | |
memcpy(contents, str, len); | |
contents[len] = '\0'; | |
break; | |
#ifdef HAVE_UNICODE_WCHAR | |
case 'w': | |
wcs = (wchar_t*) va_arg(*args, wchar_t*); | |
if (**format == '#') { | |
(*format)++; | |
len = (size_t) va_arg(*args, size_t); | |
} else { | |
len = wcslen(wcs); | |
} | |
free(val); /* We won't need that after all, I guess. */ | |
val = mkwidestring(env, wcs, len); | |
XMLRPC_FAIL_IF_FAULT(env); | |
break; | |
#endif /* HAVE_UNICODE_WCHAR */ | |
case '8': | |
/* The code 't' is reserved for a better, time_t based | |
** implementation of dateTime conversion. */ | |
val->_type = XMLRPC_TYPE_DATETIME; | |
str = (char*) va_arg(*args, char*); | |
len = strlen(str); | |
XMLRPC_TYPED_MEM_BLOCK_INIT(char, env, &val->_block, len + 1); | |
XMLRPC_FAIL_IF_FAULT(env); | |
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &val->_block); | |
memcpy(contents, str, len); | |
contents[len] = '\0'; | |
break; | |
case '6': | |
val->_type = XMLRPC_TYPE_BASE64; | |
bin_data = (unsigned char*) va_arg(*args, unsigned char*); | |
len = (size_t) va_arg(*args, size_t); | |
xmlrpc_mem_block_init(env, &val->_block, len); | |
XMLRPC_FAIL_IF_FAULT(env); | |
contents = xmlrpc_mem_block_contents(&val->_block); | |
memcpy(contents, bin_data, len); | |
break; | |
case 'p': | |
/* We might someday want to use the code 'p!' to read in a | |
** cleanup function for this pointer. */ | |
val->_type = XMLRPC_TYPE_C_PTR; | |
val->_value.c_ptr = (void*) va_arg(*args, void*); | |
break; | |
case 'V': | |
free(val); /* We won't need that after all, I guess. */ | |
val = (xmlrpc_value*) va_arg(*args, xmlrpc_value*); | |
xmlrpc_INCREF(val); | |
break; | |
case '(': | |
free(val); /* We won't need that after all, I guess. */ | |
val = mkarray(env, format, ')', args); | |
XMLRPC_FAIL_IF_FAULT(env); | |
(*format)++; | |
break; | |
case '{': | |
free(val); /* We won't need that after all, I guess. */ | |
val = mkstruct(env, format, '}', args); | |
XMLRPC_FAIL_IF_FAULT(env); | |
(*format)++; | |
break; | |
default: | |
XMLRPC_FATAL_ERROR("Unknown type code when building value"); | |
} | |
cleanup: | |
if (env->fault_occurred && val) { | |
free(val); | |
return NULL; | |
} | |
return val; | |
} | |
xmlrpc_value* xmlrpc_build_value_va (xmlrpc_env* env, | |
char* format, | |
va_list args) | |
{ | |
char *format_copy; | |
va_list args_copy; | |
xmlrpc_value* retval; | |
XMLRPC_ASSERT_ENV_OK(env); | |
XMLRPC_ASSERT(format != NULL); | |
format_copy = format; | |
VA_LIST_COPY(args_copy, args); | |
retval = mkvalue(env, &format_copy, &args_copy); | |
if (!env->fault_occurred) { | |
XMLRPC_ASSERT_VALUE_OK(retval); | |
XMLRPC_ASSERT(*format_copy == '\0'); | |
} | |
return retval; | |
} | |
xmlrpc_value* xmlrpc_build_value (xmlrpc_env* env, | |
char* format, ...) | |
{ | |
va_list args; | |
xmlrpc_value* retval; | |
va_start(args, format); | |
retval = xmlrpc_build_value_va(env, format, args); | |
va_end(args); | |
return retval; | |
} | |
/*========================================================================= | |
** Parsing XML-RPC values. | |
**========================================================================= | |
** Parse an XML-RPC value based on a format string. This code is heavily | |
** inspired by Py_BuildValue from Python 1.5.2. | |
*/ | |
static void parsevalue (xmlrpc_env* env, | |
xmlrpc_value* val, | |
char** format, | |
va_list* args); | |
static void parsearray (xmlrpc_env* env, | |
xmlrpc_value* array, | |
char** format, | |
char delimiter, | |
va_list* args) | |
{ | |
int size, i; | |
xmlrpc_value *item; | |
/* Fetch the array size. */ | |
size = xmlrpc_array_size(env, array); | |
XMLRPC_FAIL_IF_FAULT(env); | |
/* Loop over the items in the array. */ | |
for (i = 0; i < size; i++) { | |
/* Bail out if the caller didn't care about the rest of the items. */ | |
if (**format == '*') | |
break; | |
item = xmlrpc_array_get_item(env, array, i); | |
XMLRPC_FAIL_IF_FAULT(env); | |
XMLRPC_ASSERT(**format != '\0'); | |
if (**format == delimiter) | |
XMLRPC_FAIL(env, XMLRPC_INDEX_ERROR, "Too many items in array"); | |
parsevalue(env, item, format, args); | |
XMLRPC_FAIL_IF_FAULT(env); | |
} | |
if (**format == '*') | |
(*format)++; | |
if (**format != delimiter) | |
XMLRPC_FAIL(env, XMLRPC_INDEX_ERROR, "Not enough items in array"); | |
cleanup: | |
return; | |
} | |
static void parsestruct(xmlrpc_env* env, | |
xmlrpc_value* strct, | |
char** format, | |
char delimiter, | |
va_list* args) | |
{ | |
xmlrpc_value *key, *value; | |
char *keystr; | |
size_t keylen; | |
/* Set up error handling preconditions. */ | |
key = NULL; | |
/* Build the members of our struct. */ | |
while (**format != '*' && **format != delimiter && **format != '\0') { | |
/* Get our key, and skip over the ':' character. Notice the | |
** sudden call to mkvalue--we're going in the opposite direction. */ | |
key = mkvalue(env, format, args); | |
XMLRPC_FAIL_IF_FAULT(env); | |
XMLRPC_ASSERT(**format == ':'); | |
(*format)++; | |
/* Look up the value for our key. */ | |
xmlrpc_parse_value(env, key, "s#", &keystr, &keylen); | |
XMLRPC_FAIL_IF_FAULT(env); | |
value = xmlrpc_struct_get_value_n(env, strct, keystr, keylen); | |
XMLRPC_FAIL_IF_FAULT(env); | |
/* Get our value, and skip over the ',' character (if present). */ | |
parsevalue(env, value, format, args); | |
XMLRPC_FAIL_IF_FAULT(env); | |
XMLRPC_ASSERT(**format == ',' || **format == delimiter); | |
if (**format == ',') | |
(*format)++; | |
/* Release our reference, and restore our invariant. */ | |
xmlrpc_DECREF(key); | |
key = NULL; | |
} | |
XMLRPC_ASSERT(**format == '*'); | |
(*format)++; | |
XMLRPC_ASSERT(**format == delimiter); | |
cleanup: | |
if (key) | |
xmlrpc_DECREF(key); | |
} | |
static void parsevalue (xmlrpc_env* env, | |
xmlrpc_value* val, | |
char** format, | |
va_list* args) | |
{ | |
xmlrpc_int32 *int32ptr; | |
xmlrpc_bool *boolptr; | |
double *doubleptr; | |
char *contents; | |
unsigned char *bin_data; | |
char **strptr; | |
void **voidptrptr; | |
unsigned char **binptr; | |
size_t len, i, *sizeptr; | |
xmlrpc_value **valptr; | |
#ifdef HAVE_UNICODE_WCHAR | |
wchar_t *wcontents; | |
wchar_t **wcsptr; | |
#endif | |
switch (*(*format)++) { | |
case 'i': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_INT); | |
int32ptr = (xmlrpc_int32*) va_arg(*args, xmlrpc_int32*); | |
*int32ptr = val->_value.i; | |
break; | |
case 'b': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_BOOL); | |
boolptr = (xmlrpc_bool*) va_arg(*args, xmlrpc_bool*); | |
*boolptr = val->_value.b; | |
break; | |
case 'd': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_DOUBLE); | |
doubleptr = (double*) va_arg(*args, double*); | |
*doubleptr = val->_value.d; | |
break; | |
case 's': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_STRING); | |
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &val->_block); | |
len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &val->_block) - 1; | |
strptr = (char**) va_arg(*args, char**); | |
if (**format == '#') { | |
(*format)++; | |
sizeptr = (size_t*) va_arg(*args, size_t**); | |
*sizeptr = len; | |
} else { | |
for (i = 0; i < len; i++) | |
if (contents[i] == '\0') | |
XMLRPC_FAIL(env, XMLRPC_TYPE_ERROR, | |
"String must not contain NULL characters"); | |
} | |
*strptr = contents; | |
break; | |
#ifdef HAVE_UNICODE_WCHAR | |
case 'w': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_STRING); | |
if (!val->_wcs_block) { | |
/* Allocate a wchar_t string if we don't have one. */ | |
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &val->_block); | |
len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &val->_block) - 1; | |
val->_wcs_block = xmlrpc_utf8_to_wcs(env, contents, len + 1); | |
XMLRPC_FAIL_IF_FAULT(env); | |
} | |
wcontents = | |
XMLRPC_TYPED_MEM_BLOCK_CONTENTS(wchar_t, val->_wcs_block); | |
len = XMLRPC_TYPED_MEM_BLOCK_SIZE(wchar_t, val->_wcs_block) - 1; | |
wcsptr = (wchar_t**) va_arg(*args, wchar_t**); | |
if (**format == '#') { | |
(*format)++; | |
sizeptr = (size_t*) va_arg(*args, size_t**); | |
*sizeptr = len; | |
} else { | |
for (i = 0; i < len; i++) | |
if (wcontents[i] == '\0') | |
XMLRPC_FAIL(env, XMLRPC_TYPE_ERROR, | |
"String must not contain NULL characters"); | |
} | |
*wcsptr = wcontents; | |
break; | |
#endif /* HAVE_UNICODE_WCHAR */ | |
case '8': | |
/* The code 't' is reserved for a better, time_t based | |
** implementation of dateTime conversion. */ | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_DATETIME); | |
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(char, &val->_block); | |
strptr = (char**) va_arg(*args, char**); | |
*strptr = contents; | |
break; | |
case '6': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_BASE64); | |
bin_data = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(unsigned char, | |
&val->_block); | |
len = XMLRPC_TYPED_MEM_BLOCK_SIZE(char, &val->_block); | |
binptr = (unsigned char**) va_arg(*args, unsigned char**); | |
*binptr = bin_data; | |
sizeptr = (size_t*) va_arg(*args, size_t**); | |
*sizeptr = len; | |
break; | |
case 'p': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_C_PTR); | |
voidptrptr = (void**) va_arg(*args, void**); | |
*voidptrptr = val->_value.c_ptr; | |
break; | |
case 'V': | |
valptr = (xmlrpc_value**) va_arg(*args, xmlrpc_value**); | |
*valptr = val; | |
break; | |
case 'A': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_ARRAY); | |
valptr = (xmlrpc_value**) va_arg(*args, xmlrpc_value**); | |
*valptr = val; | |
break; | |
case 'S': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_STRUCT); | |
valptr = (xmlrpc_value**) va_arg(*args, xmlrpc_value**); | |
*valptr = val; | |
break; | |
case '(': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_ARRAY); | |
parsearray(env, val, format, ')', args); | |
(*format)++; | |
break; | |
case '{': | |
XMLRPC_TYPE_CHECK(env, val, XMLRPC_TYPE_STRUCT); | |
parsestruct(env, val, format, '}', args); | |
(*format)++; | |
break; | |
default: | |
XMLRPC_FATAL_ERROR("Unknown type code when parsing value"); | |
} | |
cleanup: | |
return; | |
} | |
static void xmlrpc_parse_value_va (xmlrpc_env* env, | |
xmlrpc_value* value, | |
char* format, | |
va_list args) | |
{ | |
char *format_copy; | |
va_list args_copy; | |
XMLRPC_ASSERT_ENV_OK(env); | |
XMLRPC_ASSERT_VALUE_OK(value); | |
XMLRPC_ASSERT(format != NULL); | |
format_copy = format; | |
VA_LIST_COPY(args_copy, args); | |
parsevalue(env, value, &format_copy, &args_copy); | |
XMLRPC_FAIL_IF_FAULT(env); | |
XMLRPC_ASSERT(*format_copy == '\0'); | |
cleanup: | |
return; | |
} | |
void xmlrpc_parse_value (xmlrpc_env* env, | |
xmlrpc_value* value, | |
char* format, ...) | |
{ | |
va_list args; | |
va_start(args, format); | |
xmlrpc_parse_value_va(env, value, format, args); | |
va_end(args); | |
} | |
/*========================================================================= | |
** XML-RPC Array Support | |
**========================================================================= | |
*/ | |
int xmlrpc_array_size (xmlrpc_env* env, xmlrpc_value* array) | |
{ | |
int retval; | |
/* Suppress a compiler warning about uninitialized variables. */ | |
retval = 0; | |
XMLRPC_ASSERT_ENV_OK(env); | |
XMLRPC_ASSERT_VALUE_OK(array); | |
XMLRPC_TYPE_CHECK(env, array, XMLRPC_TYPE_ARRAY); | |
retval = XMLRPC_TYPED_MEM_BLOCK_SIZE(xmlrpc_value*, &array->_block); | |
cleanup: | |
if (env->fault_occurred) | |
return -1; | |
else | |
return retval; | |
} | |
void xmlrpc_array_append_item (xmlrpc_env* env, | |
xmlrpc_value* array, | |
xmlrpc_value* value) | |
{ | |
size_t size; | |
xmlrpc_value **contents; | |
XMLRPC_ASSERT_ENV_OK(env); | |
XMLRPC_ASSERT_VALUE_OK(array); | |
XMLRPC_TYPE_CHECK(env, array, XMLRPC_TYPE_ARRAY); | |
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(xmlrpc_value*, &array->_block); | |
XMLRPC_TYPED_MEM_BLOCK_RESIZE(xmlrpc_value*, env, &array->_block, size+1); | |
XMLRPC_FAIL_IF_FAULT(env); | |
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xmlrpc_value*, &array->_block); | |
xmlrpc_INCREF(value); | |
contents[size] = value; | |
cleanup: | |
return; | |
} | |
xmlrpc_value* xmlrpc_array_get_item (xmlrpc_env* env, | |
xmlrpc_value* array, | |
int index) | |
{ | |
size_t size; | |
xmlrpc_value **contents, *retval; | |
/* Suppress a compiler warning about uninitialized variables. */ | |
retval = NULL; | |
XMLRPC_ASSERT_ENV_OK(env); | |
XMLRPC_ASSERT_VALUE_OK(array); | |
XMLRPC_TYPE_CHECK(env, array, XMLRPC_TYPE_ARRAY); | |
size = XMLRPC_TYPED_MEM_BLOCK_SIZE(xmlrpc_value*, &array->_block); | |
contents = XMLRPC_TYPED_MEM_BLOCK_CONTENTS(xmlrpc_value*, &array->_block); | |
/* BREAKME: 'index' should be a parameter of type size_t. */ | |
if (index < 0 || (size_t) index >= size) | |
XMLRPC_FAIL1(env, XMLRPC_INDEX_ERROR, "Index %d out of bounds", index); | |
retval = contents[index]; | |
cleanup: | |
if (env->fault_occurred) | |
return NULL; | |
return retval; | |
} | |
/* | |
int xmlrpc_array_set_item (xmlrpc_env* env, | |
xmlrpc_value* array, | |
int index, | |
xmlrpc_value* value) | |
{ | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment