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 TSRMLS_FETCH();
232 elem_val->name = strdup(ELEM_DOUBLE);
233 ap_php_snprintf(buf, BUF_SIZE, "%.*G", (int) EG(precision), XMLRPC_GetValueDouble(node));
234 simplestring_add(&elem_val->text, buf);
235 }
236 break;
237 case xmlrpc_datetime:
238 elem_val->name = strdup(ELEM_DATETIME);
239 simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node));
240 break;
241 case xmlrpc_base64:
242 {
243 struct buffer_st buf;
244 elem_val->name = strdup(ELEM_BASE64);
245 base64_encode_xmlrpc(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node));
246 simplestring_addn(&elem_val->text, buf.data, buf.offset );
247 buffer_delete(&buf);
248 }
249 break;
250 case xmlrpc_vector:
251 {
252 XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node);
253 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node);
254 xml_element* root_vector_elem = elem_val;
255
256 switch (my_type) {
257 case xmlrpc_vector_array:
258 {
259 if(depth == 0) {
260 elem_val->name = strdup(ELEM_PARAMS);
261 }
262 else {
263 /* Hi my name is Dave and I like to make things as confusing
264 * as possible, thus I will throw in this 'data' element
265 * where it absolutely does not belong just so that people
266 * cannot code arrays and structs in a similar and straight
267 * forward manner. Have a good day.
268 *
269 * GRRRRRRRRR!
270 */
271 xml_element* data = xml_elem_new();
272 data->name = strdup(ELEM_DATA);
273
274 elem_val->name = strdup(ELEM_ARRAY);
275 Q_PushTail(&elem_val->children, data);
276 root_vector_elem = data;
277 }
278 }
279 break;
280 case xmlrpc_vector_mixed: /* not officially supported */
281 case xmlrpc_vector_struct:
282 elem_val->name = strdup(ELEM_STRUCT);
283 break;
284 default:
285 break;
286 }
287
288 /* recurse through sub-elements */
289 while ( xIter ) {
290 xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1);
291 if (next_el) {
292 Q_PushTail(&root_vector_elem->children, next_el);
293 }
294 xIter = XMLRPC_VectorNext(node);
295 }
296 }
297 break;
298 default:
299 break;
300 }
301 }
302
303 {
304 XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector);
305
306 if (depth == 1) {
307 xml_element* value = xml_elem_new();
308 value->name = strdup(ELEM_VALUE);
309
310 /* yet another hack for the "fault" crap */
311 if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) {
312 root = value;
313 }
314 else {
315 xml_element* param = xml_elem_new();
316 param->name = strdup(ELEM_PARAM);
317
318 Q_PushTail(¶m->children, value);
319
320 root = param;
321 }
322 Q_PushTail(&value->children, elem_val);
323 }
324 else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) {
325 xml_element* member = xml_elem_new();
326 xml_element* name = xml_elem_new();
327 xml_element* value = xml_elem_new();
328
329 member->name = strdup(ELEM_MEMBER);
330 name->name = strdup(ELEM_NAME);
331 value->name = strdup(ELEM_VALUE);
332
333 simplestring_add(&name->text, XMLRPC_GetValueID(node));
334
335 Q_PushTail(&member->children, name);
336 Q_PushTail(&member->children, value);
337 Q_PushTail(&value->children, elem_val);
338
339 root = member;
340 }
341 else if (vtype == xmlrpc_vector_array) {
342 xml_element* value = xml_elem_new();
343
344 value->name = strdup(ELEM_VALUE);
345
346 Q_PushTail(&value->children, elem_val);
347
348 root = value;
349 }
350 else if (vtype == xmlrpc_vector_none) {
351 /* no parent. non-op */
352 root = elem_val;
353 }
354 else {
355 xml_element* value = xml_elem_new();
356
357 value->name = strdup(ELEM_VALUE);
358
359 Q_PushTail(&value->children, elem_val);
360
361 root = value;
362 }
363 }
364 }
365 return root;
366 }
367
XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node)368 xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) {
369 return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0);
370 }
371
XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request)372 xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) {
373 xml_element* wrapper = NULL;
374 if (request) {
375 const char* pStr = NULL;
376 XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request);
377 XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request);
378
379 wrapper = xml_elem_new();
380
381 if (request_type == xmlrpc_request_call) {
382 pStr = ELEM_METHODCALL;
383 }
384 else if (request_type == xmlrpc_request_response) {
385 pStr = ELEM_METHODRESPONSE;
386 }
387 if (pStr) {
388 wrapper->name = strdup(pStr);
389 }
390
391 if(request_type == xmlrpc_request_call) {
392 pStr = XMLRPC_RequestGetMethodName(request);
393
394 if (pStr) {
395 xml_element* method = xml_elem_new();
396 method->name = strdup(ELEM_METHODNAME);
397 simplestring_add(&method->text, pStr);
398 Q_PushTail(&wrapper->children, method);
399 }
400 }
401 if (xParams) {
402 Q_PushTail(&wrapper->children,
403 XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0));
404 }
405 else {
406 /* Despite the spec, the xml-rpc list folk want me to send an empty params element */
407 xml_element* params = xml_elem_new();
408 params->name = strdup(ELEM_PARAMS);
409 Q_PushTail(&wrapper->children, params);
410 }
411 }
412 return wrapper;
413 }
414
415