2 This file is part of libXMLRPC - a C library for xml-encoded function calls.
4 Author: Dan Libby (dan@libby.com)
5 Epinions.com may be contacted at feedback@epinions-inc.com
9 Copyright 2000 Epinions, Inc.
11 Subject to the following 3 conditions, Epinions, Inc. permits you, free
12 of charge, to (a) use, copy, distribute, modify, perform and display this
13 software and associated documentation files (the "Software"), and (b)
14 permit others to whom the Software is furnished to do so as well.
16 1) The above copyright notice and this permission notice shall be included
17 without modification in all copies or substantial portions of the
20 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF
21 ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY
22 IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR
23 PURPOSE OR NONINFRINGEMENT.
25 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
26 SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
27 OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING
28 NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH
34 static const char rcsid[] = "#(@) $Id: xml_to_xmlrpc.c,v 1.5 2004/04/27 17:33:59 iliaa Exp $";
37 #include "xmlrpc_win32.h"
41 #include "xml_to_xmlrpc.h"
44 /* list of tokens used in vocab */
45 #define ELEM_ARRAY "array"
46 #define ELEM_BASE64 "base64"
47 #define ELEM_BOOLEAN "boolean"
48 #define ELEM_DATA "data"
49 #define ELEM_DATETIME "dateTime.iso8601"
50 #define ELEM_DOUBLE "double"
51 #define ELEM_FAULT "fault"
52 #define ELEM_FAULTCODE "faultCode"
53 #define ELEM_FAULTSTRING "faultString"
55 #define ELEM_INT "int"
56 #define ELEM_MEMBER "member"
57 #define ELEM_METHODCALL "methodCall"
58 #define ELEM_METHODNAME "methodName"
59 #define ELEM_METHODRESPONSE "methodResponse"
60 #define ELEM_NAME "name"
61 #define ELEM_PARAM "param"
62 #define ELEM_PARAMS "params"
63 #define ELEM_STRING "string"
64 #define ELEM_STRUCT "struct"
65 #define ELEM_VALUE "value"
68 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) {
70 /* This should only be the case for the first element */
71 current_val = XMLRPC_CreateValueEmpty();
76 /* first, deal with the crazy/stupid fault format */
77 if (!strcmp(el->name, ELEM_FAULT)) {
78 xml_element* fault_value = (xml_element*)Q_Head(&el->children);
79 XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
82 xml_element* fault_struct = (xml_element*)Q_Head(&fault_value->children);
84 xml_element* iter = (xml_element*)Q_Head(&fault_struct->children);
87 XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
88 xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
89 XMLRPC_AddValueToVector(current_val, xNextVal);
90 iter = (xml_element*)Q_Next(&fault_struct->children);
95 else if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */
96 || (!strcmp(el->name, ELEM_PARAMS) &&
97 (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) ) { /* this "PARAMS" concept is silly. dave?! */
98 xml_element* iter = (xml_element*)Q_Head(&el->children);
99 XMLRPC_SetIsVector(current_val, xmlrpc_vector_array);
102 XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
103 xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
104 XMLRPC_AddValueToVector(current_val, xNextVal);
105 iter = (xml_element*)Q_Next(&el->children);
108 else if (!strcmp(el->name, ELEM_STRUCT)) {
109 xml_element* iter = (xml_element*)Q_Head(&el->children);
110 XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
113 XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
114 xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
115 XMLRPC_AddValueToVector(current_val, xNextVal);
116 iter = (xml_element*)Q_Next(&el->children);
119 else if (!strcmp(el->name, ELEM_STRING) ||
120 (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) {
121 XMLRPC_SetValueString(current_val, el->text.str, el->text.len);
123 else if (!strcmp(el->name, ELEM_NAME)) {
124 XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact);
126 else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) {
127 XMLRPC_SetValueInt(current_val, atoi(el->text.str));
129 else if (!strcmp(el->name, ELEM_BOOLEAN)) {
130 XMLRPC_SetValueBoolean(current_val, atoi(el->text.str));
132 else if (!strcmp(el->name, ELEM_DOUBLE)) {
133 XMLRPC_SetValueDouble(current_val, atof(el->text.str));
135 else if (!strcmp(el->name, ELEM_DATETIME)) {
136 XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str);
138 else if (!strcmp(el->name, ELEM_BASE64)) {
139 struct buffer_st buf;
140 base64_decode(&buf, el->text.str, el->text.len);
141 XMLRPC_SetValueBase64(current_val, buf.data, buf.offset);
147 if (!strcmp(el->name, ELEM_METHODCALL)) {
149 XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
152 else if (!strcmp(el->name, ELEM_METHODRESPONSE)) {
154 XMLRPC_RequestSetRequestType(request, xmlrpc_request_response);
157 else if (!strcmp(el->name, ELEM_METHODNAME)) {
159 XMLRPC_RequestSetMethodName(request, el->text.str);
163 iter = (xml_element*)Q_Head(&el->children);
165 xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector,
167 iter = (xml_element*)Q_Next(&el->children);
174 XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el)
176 return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el);
179 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el)
182 return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el));
187 xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node,
188 XMLRPC_REQUEST_TYPE request_type, int depth) {
190 xml_element* root = NULL;
193 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node);
194 XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node);
195 xml_element* elem_val = xml_elem_new();
197 /* special case for when root element is not an array */
199 !(type == xmlrpc_vector &&
200 vtype == xmlrpc_vector_array &&
201 request_type == xmlrpc_request_call) ) {
202 int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE));
204 xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1);
206 Q_PushTail(&elem_val->children, next_el);
208 elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS);
212 case xmlrpc_empty: /* treat null value as empty string in xmlrpc. */
214 elem_val->name = strdup(ELEM_STRING);
215 simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
218 elem_val->name = strdup(ELEM_INT);
219 snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
220 simplestring_add(&elem_val->text, buf);
223 elem_val->name = strdup(ELEM_BOOLEAN);
224 snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
225 simplestring_add(&elem_val->text, buf);
228 elem_val->name = strdup(ELEM_DOUBLE);
229 snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
230 simplestring_add(&elem_val->text, buf);
232 case xmlrpc_datetime:
233 elem_val->name = strdup(ELEM_DATETIME);
234 simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
238 struct buffer_st buf;
239 elem_val->name = strdup(ELEM_BASE64);
240 base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
241 simplestring_addn(&elem_val->text, buf.data, buf.offset );
247 XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
248 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
249 xml_element* root_vector_elem = elem_val;
252 case xmlrpc_vector_array:
255 elem_val->name = strdup(ELEM_PARAMS);
258 /* Hi my name is Dave and I like to make things as confusing
259 * as possible, thus I will throw in this 'data' element
260 * where it absolutely does not belong just so that people
261 * cannot code arrays and structs in a similar and straight
262 * forward manner. Have a good day.
266 xml_element* data = xml_elem_new();
267 data->name = strdup(ELEM_DATA);
269 elem_val->name = strdup(ELEM_ARRAY);
270 Q_PushTail(&elem_val->children, data);
271 root_vector_elem = data;
275 case xmlrpc_vector_mixed: /* not officially supported */
276 case xmlrpc_vector_struct:
277 elem_val->name = strdup(ELEM_STRUCT);
283 /* recurse through sub-elements */
285 xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1);
287 Q_PushTail(&root_vector_elem->children, next_el);
289 xIter = XMLRPC_VectorNext(node);
299 XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector);
302 xml_element* value = xml_elem_new();
303 value->name = strdup(ELEM_VALUE);
305 /* yet another hack for the "fault" crap */
306 if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) {
310 xml_element* param = xml_elem_new();
311 param->name = strdup(ELEM_PARAM);
313 Q_PushTail(¶m->children, value);
317 Q_PushTail(&value->children, elem_val);
319 else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) {
320 xml_element* member = xml_elem_new();
321 xml_element* name = xml_elem_new();
322 xml_element* value = xml_elem_new();
324 member->name = strdup(ELEM_MEMBER);
325 name->name = strdup(ELEM_NAME);
326 value->name = strdup(ELEM_VALUE);
328 simplestring_add(&name->text, XMLRPC_GetValueID(node));
330 Q_PushTail(&member->children, name);
331 Q_PushTail(&member->children, value);
332 Q_PushTail(&value->children, elem_val);
336 else if (vtype == xmlrpc_vector_array) {
337 xml_element* value = xml_elem_new();
339 value->name = strdup(ELEM_VALUE);
341 Q_PushTail(&value->children, elem_val);
345 else if (vtype == xmlrpc_vector_none) {
346 /* no parent. non-op */
350 xml_element* value = xml_elem_new();
352 value->name = strdup(ELEM_VALUE);
354 Q_PushTail(&value->children, elem_val);
363 xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
364 return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0);
367 xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
368 xml_element* wrapper = NULL;
370 const char* pStr = NULL;
371 XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
372 XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request);
374 wrapper = xml_elem_new();
376 if (request_type == xmlrpc_request_call) {
377 pStr = ELEM_METHODCALL;
379 else if (request_type == xmlrpc_request_response) {
380 pStr = ELEM_METHODRESPONSE;
383 wrapper->name = strdup(pStr);
386 if(request_type == xmlrpc_request_call) {
387 pStr = XMLRPC_RequestGetMethodName(request);
390 xml_element* method = xml_elem_new();
391 method->name = strdup(ELEM_METHODNAME);
392 simplestring_add(&method->text, pStr);
393 Q_PushTail(&wrapper->children, method);
397 Q_PushTail(&wrapper->children,
398 XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0));
401 /* Despite the spec, the xml-rpc list folk want me to send an empty params element */
402 xml_element* params = xml_elem_new();
403 params->name = strdup(ELEM_PARAMS);
404 Q_PushTail(&wrapper->children, params);