1 /*
2 This file is part of libXMLRPC - a C library for xml-encoded function calls.
3
4 Author: Dan Libby (dan@libby.com)
5 Epinions.com may be contacted at feedback@epinions-inc.com
6 */
7
8 /*
9 Copyright 2000 Epinions, Inc.
10
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.
15
16 1) The above copyright notice and this permission notice shall be included
17 without modification in all copies or substantial portions of the
18 Software.
19
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.
24
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
29 DAMAGES.
30
31 */
32
33
34 static const char rcsid[] = "#(@) $Id$";
35
36 #include "php.h"
37 #include "main/snprintf.h"
38 #ifdef _WIN32
39 #include "xmlrpc_win32.h"
40 #endif
41 #include <string.h>
42 #include <stdlib.h>
43 #include "xml_to_xmlrpc.h"
44 #include "base64.h"
45
46 /* list of tokens used in vocab */
47 #define ELEM_ARRAY "array"
48 #define ELEM_BASE64 "base64"
49 #define ELEM_BOOLEAN "boolean"
50 #define ELEM_DATA "data"
51 #define ELEM_DATETIME "dateTime.iso8601"
52 #define ELEM_DOUBLE "double"
53 #define ELEM_FAULT "fault"
54 #define ELEM_FAULTCODE "faultCode"
55 #define ELEM_FAULTSTRING "faultString"
56 #define ELEM_I4 "i4"
57 #define ELEM_INT "int"
58 #define ELEM_MEMBER "member"
59 #define ELEM_METHODCALL "methodCall"
60 #define ELEM_METHODNAME "methodName"
61 #define ELEM_METHODRESPONSE "methodResponse"
62 #define ELEM_NAME "name"
63 #define ELEM_PARAM "param"
64 #define ELEM_PARAMS "params"
65 #define ELEM_STRING "string"
66 #define ELEM_STRUCT "struct"
67 #define ELEM_VALUE "value"
68
69
xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request,XMLRPC_VALUE parent_vector,XMLRPC_VALUE current_val,xml_element * el)70 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) {
71 if (!current_val) {
72 /* This should only be the case for the first element */
73 current_val = XMLRPC_CreateValueEmpty();
74 }
75
76 if (el->name) {
77
78 /* first, deal with the crazy/stupid fault format */
79 if (!strcmp(el->name, ELEM_FAULT)) {
80 xml_element* fault_value = (xml_element*)Q_Head(&el->children);
81 XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
82
83 if(fault_value) {
84 xml_element* fault_struct = (xml_element*)Q_Head(&fault_value->children);
85 if(fault_struct) {
86 xml_element* iter = (xml_element*)Q_Head(&fault_struct->children);
87
88 while (iter) {
89 XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
90 xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
91 XMLRPC_AddValueToVector(current_val, xNextVal);
92 iter = (xml_element*)Q_Next(&fault_struct->children);
93 }
94 }
95 }
96 }
97 else if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */
98 || (!strcmp(el->name, ELEM_PARAMS) &&
99 (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) ) { /* this "PARAMS" concept is silly. dave?! */
100 xml_element* iter = (xml_element*)Q_Head(&el->children);
101 XMLRPC_SetIsVector(current_val, xmlrpc_vector_array);
102
103 while (iter) {
104 XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
105 xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
106 XMLRPC_AddValueToVector(current_val, xNextVal);
107 iter = (xml_element*)Q_Next(&el->children);
108 }
109 }
110 else if (!strcmp(el->name, ELEM_STRUCT)) {
111 xml_element* iter = (xml_element*)Q_Head(&el->children);
112 XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct);
113
114 while ( iter ) {
115 XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty();
116 xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter);
117 XMLRPC_AddValueToVector(current_val, xNextVal);
118 iter = (xml_element*)Q_Next(&el->children);
119 }
120 }
121 else if (!strcmp(el->name, ELEM_STRING) ||
122 (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) {
123 XMLRPC_SetValueString(current_val, el->text.str, el->text.len);
124 }
125 else if (!strcmp(el->name, ELEM_NAME)) {
126 XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact);
127 }
128 else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) {
129 XMLRPC_SetValueInt(current_val, atoi(el->text.str));
130 }
131 else if (!strcmp(el->name, ELEM_BOOLEAN)) {
132 XMLRPC_SetValueBoolean(current_val, atoi(el->text.str));
133 }
134 else if (!strcmp(el->name, ELEM_DOUBLE)) {
135 XMLRPC_SetValueDouble(current_val, atof(el->text.str));
136 }
137 else if (!strcmp(el->name, ELEM_DATETIME)) {
138 XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str);
139 }
140 else if (!strcmp(el->name, ELEM_BASE64)) {
141 struct buffer_st buf;
142 base64_decode_xmlrpc(&buf, el->text.str, el->text.len);
143 XMLRPC_SetValueBase64(current_val, buf.data, buf.offset);
144 buffer_delete(&buf);
145 }
146 else {
147 xml_element* iter;
148
149 if (!strcmp(el->name, ELEM_METHODCALL)) {
150 if (request) {
151 XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
152 }
153 }
154 else if (!strcmp(el->name, ELEM_METHODRESPONSE)) {
155 if (request) {
156 XMLRPC_RequestSetRequestType(request, xmlrpc_request_response);
157 }
158 }
159 else if (!strcmp(el->name, ELEM_METHODNAME)) {
160 if (request) {
161 XMLRPC_RequestSetMethodName(request, el->text.str);
162 }
163 }
164
165 iter = (xml_element*)Q_Head(&el->children);
166 while ( iter ) {
167 xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector,
168 current_val, iter);
169 iter = (xml_element*)Q_Next(&el->children);
170 }
171 }
172 }
173 return current_val;
174 }
175
xml_element_to_XMLRPC_VALUE(xml_element * el)176 XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el)
177 {
178 return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el);
179 }
180
xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request,xml_element * el)181 XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el)
182 {
183 if (request) {
184 return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el));
185 }
186 return NULL;
187 }
188
XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector,XMLRPC_VALUE node,XMLRPC_REQUEST_TYPE request_type,int depth)189 xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node,
190 XMLRPC_REQUEST_TYPE request_type, int depth) {
191 #define BUF_SIZE 512
192 xml_element* root = NULL;
193 if (node) {
194 char buf[BUF_SIZE];
195 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node);
196 XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node);
197 xml_element* elem_val = xml_elem_new();
198
199 /* special case for when root element is not an array */
200 if (depth == 0 &&
201 !(type == xmlrpc_vector &&
202 vtype == xmlrpc_vector_array &&
203 request_type == xmlrpc_request_call) ) {
204 int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE));
205
206 xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1);
207 if (next_el) {
208 Q_PushTail(&elem_val->children, next_el);
209 }
210 elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS);
211 }
212 else {
213 switch (type) {
214 case xmlrpc_empty: /* treat null value as empty string in xmlrpc. */
215 case xmlrpc_string:
216 elem_val->name = strdup(ELEM_STRING);
217 simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node));
218 break;
219 case xmlrpc_int:
220 elem_val->name = strdup(ELEM_INT);
221 snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node));
222 simplestring_add(&elem_val->text, buf);
223 break;
224 case xmlrpc_boolean:
225 elem_val->name = strdup(ELEM_BOOLEAN);
226 snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node));
227 simplestring_add(&elem_val->text, buf);
228 break;
229 case xmlrpc_double:
230 {
231 elem_val->name = strdup(ELEM_DOUBLE);
232 ap_php_snprintf(buf, BUF_SIZE, "%.*G", (int) EG(precision), XMLRPC_GetValueDouble(node));
233 simplestring_add(&elem_val->text, buf);
234 }
235 break;
236 case xmlrpc_datetime:
237 elem_val->name = strdup(ELEM_DATETIME);
238 simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
239 break;
240 case xmlrpc_base64:
241 {
242 struct buffer_st buf;
243 elem_val->name = strdup(ELEM_BASE64);
244 base64_encode_xmlrpc(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
245 simplestring_addn(&elem_val->text, buf.data, buf.offset );
246 buffer_delete(&buf);
247 }
248 break;
249 case xmlrpc_vector:
250 {
251 XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
252 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
253 xml_element* root_vector_elem = elem_val;
254
255 switch (my_type) {
256 case xmlrpc_vector_array:
257 {
258 if(depth == 0) {
259 elem_val->name = strdup(ELEM_PARAMS);
260 }
261 else {
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.
267 *
268 * GRRRRRRRRR!
269 */
270 xml_element* data = xml_elem_new();
271 data->name = strdup(ELEM_DATA);
272
273 elem_val->name = strdup(ELEM_ARRAY);
274 Q_PushTail(&elem_val->children, data);
275 root_vector_elem = data;
276 }
277 }
278 break;
279 case xmlrpc_vector_mixed: /* not officially supported */
280 case xmlrpc_vector_struct:
281 elem_val->name = strdup(ELEM_STRUCT);
282 break;
283 default:
284 break;
285 }
286
287 /* recurse through sub-elements */
288 while ( xIter ) {
289 xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1);
290 if (next_el) {
291 Q_PushTail(&root_vector_elem->children, next_el);
292 }
293 xIter = XMLRPC_VectorNext(node);
294 }
295 }
296 break;
297 default:
298 break;
299 }
300 }
301
302 {
303 XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector);
304
305 if (depth == 1) {
306 xml_element* value = xml_elem_new();
307 value->name = strdup(ELEM_VALUE);
308
309 /* yet another hack for the "fault" crap */
310 if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) {
311 root = value;
312 }
313 else {
314 xml_element* param = xml_elem_new();
315 param->name = strdup(ELEM_PARAM);
316
317 Q_PushTail(¶m->children, value);
318
319 root = param;
320 }
321 Q_PushTail(&value->children, elem_val);
322 }
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();
327
328 member->name = strdup(ELEM_MEMBER);
329 name->name = strdup(ELEM_NAME);
330 value->name = strdup(ELEM_VALUE);
331
332 simplestring_add(&name->text, XMLRPC_GetValueID(node));
333
334 Q_PushTail(&member->children, name);
335 Q_PushTail(&member->children, value);
336 Q_PushTail(&value->children, elem_val);
337
338 root = member;
339 }
340 else if (vtype == xmlrpc_vector_array) {
341 xml_element* value = xml_elem_new();
342
343 value->name = strdup(ELEM_VALUE);
344
345 Q_PushTail(&value->children, elem_val);
346
347 root = value;
348 }
349 else if (vtype == xmlrpc_vector_none) {
350 /* no parent. non-op */
351 root = elem_val;
352 }
353 else {
354 xml_element* value = xml_elem_new();
355
356 value->name = strdup(ELEM_VALUE);
357
358 Q_PushTail(&value->children, elem_val);
359
360 root = value;
361 }
362 }
363 }
364 return root;
365 }
366
XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node)367 xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
368 return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0);
369 }
370
XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request)371 xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
372 xml_element* wrapper = NULL;
373 if (request) {
374 const char* pStr = NULL;
375 XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
376 XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request);
377
378 wrapper = xml_elem_new();
379
380 if (request_type == xmlrpc_request_call) {
381 pStr = ELEM_METHODCALL;
382 }
383 else if (request_type == xmlrpc_request_response) {
384 pStr = ELEM_METHODRESPONSE;
385 }
386 if (pStr) {
387 wrapper->name = strdup(pStr);
388 }
389
390 if(request_type == xmlrpc_request_call) {
391 pStr = XMLRPC_RequestGetMethodName(request);
392
393 if (pStr) {
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);
398 }
399 }
400 if (xParams) {
401 Q_PushTail(&wrapper->children,
402 XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0));
403 }
404 else {
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);
409 }
410 }
411 return wrapper;
412 }
413
414