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