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$";
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_NIL "nil"
62 #define ELEM_PARAM "param"
63 #define ELEM_PARAMS "params"
64 #define ELEM_STRING "string"
65 #define ELEM_STRUCT "struct"
66 #define ELEM_VALUE "value"
69 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) {
71 /* This should only be the case for the first element */
72 current_val = XMLRPC_CreateValueEmpty();
77 /* first, deal with the crazy/stupid fault format */
78 if (!strcmp(el->name, ELEM_FAULT)) {
79 xml_element* fault_value = (xml_element*)Q_Head(&el->children);
80 XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
83 xml_element* fault_struct = (xml_element*)Q_Head(&fault_value->children);
85 xml_element* iter = (xml_element*)Q_Head(&fault_struct->children);
88 XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
89 xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
90 XMLRPC_AddValueToVector(current_val, xNextVal);
91 iter = (xml_element*)Q_Next(&fault_struct->children);
96 else if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */
97 || (!strcmp(el->name, ELEM_PARAMS) &&
98 (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) ) { /* this "PARAMS" concept is silly. dave?! */
99 xml_element* iter = (xml_element*)Q_Head(&el->children);
100 XMLRPC_SetIsVector(current_val, xmlrpc_vector_array);
103 XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
104 xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
105 XMLRPC_AddValueToVector(current_val, xNextVal);
106 iter = (xml_element*)Q_Next(&el->children);
109 else if (!strcmp(el->name, ELEM_STRUCT)) {
110 xml_element* iter = (xml_element*)Q_Head(&el->children);
111 XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
114 XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
115 xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
116 XMLRPC_AddValueToVector(current_val, xNextVal);
117 iter = (xml_element*)Q_Next(&el->children);
120 else if (!strcmp(el->name, ELEM_STRING) ||
121 (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) {
122 XMLRPC_SetValueString(current_val, el->text.str, el->text.len);
124 else if (!strcmp(el->name, ELEM_NAME)) {
125 XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact);
127 else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) {
128 XMLRPC_SetValueInt(current_val, atoi(el->text.str));
130 else if (!strcmp(el->name, ELEM_BOOLEAN)) {
131 XMLRPC_SetValueBoolean(current_val, atoi(el->text.str));
133 else if (!strcmp(el->name, ELEM_DOUBLE)) {
134 XMLRPC_SetValueDouble(current_val, atof(el->text.str));
136 else if (!strcmp(el->name, ELEM_DATETIME)) {
137 XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str);
139 else if (!strcmp(el->name, ELEM_BASE64)) {
140 struct buffer_st buf;
141 base64_decode(&buf, el->text.str, el->text.len);
142 XMLRPC_SetValueBase64(current_val, buf.data, buf.offset);
148 if (!strcmp(el->name, ELEM_METHODCALL)) {
150 XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
153 else if (!strcmp(el->name, ELEM_METHODRESPONSE)) {
155 XMLRPC_RequestSetRequestType(request, xmlrpc_request_response);
158 else if (!strcmp(el->name, ELEM_METHODNAME)) {
160 XMLRPC_RequestSetMethodName(request, el->text.str);
164 iter = (xml_element*)Q_Head(&el->children);
166 xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector,
168 iter = (xml_element*)Q_Next(&el->children);
175 XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el)
177 return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el);
180 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el)
183 return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el));
188 xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node,
189 XMLRPC_REQUEST_TYPE request_type, int depth) {
191 xml_element* root = NULL;
194 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node);
195 XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node);
196 xml_element* elem_val = xml_elem_new();
198 /* special case for when root element is not an array */
200 !(type == xmlrpc_vector &&
201 vtype == xmlrpc_vector_array &&
202 request_type == xmlrpc_request_call) ) {
203 int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE));
205 xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1);
207 Q_PushTail(&elem_val->children, next_el);
209 elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS);
213 case xmlrpc_empty: /* treat null value as empty string in xmlrpc. */
215 elem_val->name = strdup(ELEM_NIL);
218 elem_val->name = strdup(ELEM_STRING);
219 simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
222 elem_val->name = strdup(ELEM_INT);
223 snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
224 simplestring_add(&elem_val->text, buf);
227 elem_val->name = strdup(ELEM_BOOLEAN);
228 snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
229 simplestring_add(&elem_val->text, buf);
232 elem_val->name = strdup(ELEM_DOUBLE);
233 snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node));
234 simplestring_add(&elem_val->text, buf);
236 case xmlrpc_datetime:
237 elem_val->name = strdup(ELEM_DATETIME);
238 simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
242 struct buffer_st buf;
243 elem_val->name = strdup(ELEM_BASE64);
244 base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
245 simplestring_addn(&elem_val->text, buf.data, buf.offset );
251 XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
252 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
253 xml_element* root_vector_elem = elem_val;
256 case xmlrpc_vector_array:
259 elem_val->name = strdup(ELEM_PARAMS);
262 /* Hi my name is Dave and I like to make things as confusing
263 * as possible, thus I will throw in this 'data' element
264 * where it absolutely does not belong just so that people
265 * cannot code arrays and structs in a similar and straight
266 * forward manner. Have a good day.
270 xml_element* data = xml_elem_new();
271 data->name = strdup(ELEM_DATA);
273 elem_val->name = strdup(ELEM_ARRAY);
274 Q_PushTail(&elem_val->children, data);
275 root_vector_elem = data;
279 case xmlrpc_vector_mixed: /* not officially supported */
280 case xmlrpc_vector_struct:
281 elem_val->name = strdup(ELEM_STRUCT);
287 /* recurse through sub-elements */
289 xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1);
291 Q_PushTail(&root_vector_elem->children, next_el);
293 xIter = XMLRPC_VectorNext(node);
303 XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector);
306 xml_element* value = xml_elem_new();
307 value->name = strdup(ELEM_VALUE);
309 /* yet another hack for the "fault" crap */
310 if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) {
314 xml_element* param = xml_elem_new();
315 param->name = strdup(ELEM_PARAM);
317 Q_PushTail(¶m->children, value);
321 Q_PushTail(&value->children, elem_val);
323 else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) {
324 xml_element* member = xml_elem_new();
325 xml_element* name = xml_elem_new();
326 xml_element* value = xml_elem_new();
328 member->name = strdup(ELEM_MEMBER);
329 name->name = strdup(ELEM_NAME);
330 value->name = strdup(ELEM_VALUE);
332 simplestring_add(&name->text, XMLRPC_GetValueID(node));
334 Q_PushTail(&member->children, name);
335 Q_PushTail(&member->children, value);
336 Q_PushTail(&value->children, elem_val);
340 else if (vtype == xmlrpc_vector_array) {
341 xml_element* value = xml_elem_new();
343 value->name = strdup(ELEM_VALUE);
345 Q_PushTail(&value->children, elem_val);
349 else if (vtype == xmlrpc_vector_none) {
350 /* no parent. non-op */
354 xml_element* value = xml_elem_new();
356 value->name = strdup(ELEM_VALUE);
358 Q_PushTail(&value->children, elem_val);
367 xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
368 return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0);
371 xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
372 xml_element* wrapper = NULL;
374 const char* pStr = NULL;
375 XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
376 XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request);
378 wrapper = xml_elem_new();
380 if (request_type == xmlrpc_request_call) {
381 pStr = ELEM_METHODCALL;
383 else if (request_type == xmlrpc_request_response) {
384 pStr = ELEM_METHODRESPONSE;
387 wrapper->name = strdup(pStr);
390 if(request_type == xmlrpc_request_call) {
391 pStr = XMLRPC_RequestGetMethodName(request);
394 xml_element* method = xml_elem_new();
395 method->name = strdup(ELEM_METHODNAME);
396 simplestring_add(&method->text, pStr);
397 Q_PushTail(&wrapper->children, method);
401 Q_PushTail(&wrapper->children,
402 XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0));
405 /* Despite the spec, the xml-rpc list folk want me to send an empty params element */
406 xml_element* params = xml_elem_new();
407 params->name = strdup(ELEM_PARAMS);
408 Q_PushTail(&wrapper->children, params);