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